#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Stashr - Flask API Routing Definitions
"""

"""
MIT License

Copyright (c) 2020 Andrew Vanderbye

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

"""-------------------------------------------------------------------------------------------
-- IMPORTS
-------------------------------------------------------------------------------------------"""

import os, json, pathlib, io, urllib, datetime, signal
from zipfile import ZipFile

try:
    from rarfile import RarFile
    use_rar = True
except ImportError:
    # print('RarFile not imported')
    use_rar = False

try:
    from tarfile import TarFile
    use_tar = True
except ImportError:
    # print('TarFile not imported')
    use_tar = False

from natsort import natsorted
from operator import itemgetter, attrgetter

from validate import Validator

""" --- STASHR CORE IMPORTS --- """
from stashr import log, database, utils, paths, folders, naming, forms, tasks

from stashr.config import stashrconfig
from stashr.comicvine import cv

""" --- FLASK IMPORTS --- """
from flask import Blueprint, jsonify, request, redirect, url_for, send_file

""" --- FLASK EXTENSION IMPORTS --- """
from flask_login import current_user

""" --- SQL ALCHEMY OPERATORS --- """
from sqlalchemy import or_, and_
from sqlalchemy.orm import contains_eager

""" --- WERKZEUG --- """
from werkzeug.utils import secure_filename
from werkzeug.datastructures import ImmutableMultiDict

""" --- CREATE LOGGER --- """
logger = log.stashr_logger(__name__)

"""-------------------------------------------------------------------------------------------
-- API ROUTES
-------------------------------------------------------------------------------------------"""

api = Blueprint('api', __name__, template_folder='templates')

""" --- API CALLS PER METHOD --- """
# GET - api_key = request.args.get('api_key')
# POST - api_key = request.json['api_key']
# PUT - api_key = request.json['api_key']
# DELETE - api_key = request.json['api_key']

""" --- DEFINE CODE MESSAGES --- """

return_codes = {
    '100': "Invalid API Key",
    '200': "OK",
    '201': "Created",
    '400': "Bad Request",
    '401': "Unauthorized",
    '403': "Forbidden",
    '404': "Not Found",
    '405': "Method Not Allowed",
    '406': "Not Acceptable",
    '409': "Item Exists",
    '500': "Server Error",
}

""" --- ALL VOLUMES --- """

# GET
@api.route('/volumes', methods=['GET'])
def api_get_all_volumes():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            logger.debug('API Key not provided')
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        logger.debug('API Key not in database')
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.Volumes.volume_id) \
        .count()

    volumes = database.session \
        .query(database.Volumes) \
        .filter(database.Volumes.volume_age_rating <= user.rating_allowed) \
        .order_by(database.Volumes.volume_sort_title) \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.VolumesSchema(many=True).dump(volumes)
    return create_json_return('200', total_results=total_results, results=data)

# POST

""" --- SINGLE VOLUME --- """

# GET
@api.route('/volumes/<volume_id>', methods=['GET'])
def api_get_single_volume(volume_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        if "api_key" not in request.args:
            return jsonify(create_json_return('100'))
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    volume = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_id == volume_id,
                    database.Volumes.volume_slug == volume_id)) \
        .filter(database.Volumes.volume_age_rating <= user.rating_allowed) \
        .first()
    
    if volume is None:
        return jsonify(create_json_return('404'))

    total_results = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_id == volume_id,
                    database.Volumes.volume_slug == volume_id)) \
        .count()

    data = database.VolumesSchema().dump(volume)
    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/volumes/<volume_id>', methods=["POST"])
def api_post_single_volume(volume_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()
        
    if user is None:
        return jsonify(create_json_return('100'))

    if not user.role == 'admin':
        return jsonify(create_json_return('401'))

    resource = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_id == volume_id,
                    database.Volumes.volume_slug == volume_id)) \
        .first()

    if resource is None:
        utils.add_volume_to_library(volume_id)
        return jsonify(create_json_return('200'))
    
    utils.scrape_volume_issues(volume_id)
    utils.refresh_single_volume(resource.volume_id)

    return jsonify(create_json_return('200'))

# PUT
@api.route('/volumes/<volume_id>', methods=["PUT"])
def api_put_single_volume(volume_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    volume = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_id == volume_id,
                    database.Volumes.volume_slug == volume_id),
                database.Users.id == user.id) \
        .first()

    if volume is None:
        return jsonify(create_json_return('404'))

    """
    if len(volume.notes) == 0:
        # utils.populate_user_notes(user.id, volume.volume_id)
        pass
    """
    
    allowed_keys = [
        'volume_status',
        'volume_age_rating',
        'volume_note'
    ]

    error = False

    for key, value in request.json['data'].items():
        if key not in allowed_keys:
            error = True
            break
        """
        if key == 'volume_note':
            print('updating note')
            volume.notes[0].volume_note = value
        """
        if user.role == 'admin':
            setattr(volume, key, value)

    if error:
        return jsonify(create_json_return('400'))

    database.session.merge(volume)
    database.session.commit()

    return jsonify(create_json_return('200'))

# DELETE
@api.route('/volumes/<volume_id>', methods=["DELETE"])
def api_delete_single_volume(volume_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if not user.role == 'admin':
        return jsonify(create_json_return('100'))

    resource = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_slug == volume_id,
                    database.Volumes.volume_id == volume_id)) \
        .first()

    if resource is None:
        return jsonify(create_json_return('404'))

    utils.delete_volume(resource.volume_id)

    return jsonify(create_json_return('200'))

""" --- SINGLE VOLUME ALL ISSUES --- """

# GET
@api.route('/volumes/<volume_id>/issues', methods=['GET'])
def api_get_single_volume_all_issues(volume_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - volumes
    """

    user = current_user

    if not user.is_authenticated:
        if "api_key" not in request.args:
            return jsonify(create_json_return('100'))
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))
        
    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    volume = database.session \
        .query(database.Volumes) \
        .filter(or_(database.Volumes.volume_slug == volume_id,
                    database.Volumes.volume_id == volume_id)) \
        .filter(database.Volumes.volume_age_rating <= user.rating_allowed) \
        .first()

    if volume is None:
        return jsonify(create_json_return('409'))

    total_issues = database.session \
        .query(database.Issues.issue_id) \
        .filter(database.Issues.issue_volume_id == volume.volume_id) \
        .count()

    if total_issues == 0:
        utils.scrape_volume_issues(volume.volume_id)
        # Return redirect
        return redirect(url_for('api.api_get_single_volume_all_issues', volume_id=volume_id))

    ret_issues = (
        database.session.query(database.Issues)
        .join(database.Issues.read_status)
        .options(contains_eager(database.Issues.read_status))
        .join(database.Issues.owned_status)
        .options(contains_eager(database.Issues.owned_status))
        .filter(database.Issues.issue_volume_id == volume_id)
        .filter(database.ReadIssues.read_user_id == user.id)
        .filter(database.OwnedIssues.owned_user_id == user.id)
    ).all()

    if len(ret_issues) != total_issues:
        logger.debug('NEED TO ADD A DAMN READ STATUS')
        utils.populate_statuses(user.id, volume_id, ret_issues)
        # utils.populate_read_status(user.id, volume_id, ret_issues)
        # utils.populate_owned_status(user.id, volume, ret_issues)
        return redirect(url_for('api.api_get_single_volume_all_issues', volume_id=volume_id))
    
    issues = (
        database.session.query(database.Issues)
        .join(database.Issues.read_status)
        .options(contains_eager(database.Issues.read_status))
        .join(database.Issues.owned_status)
        .options(contains_eager(database.Issues.owned_status))
        .filter(database.Issues.issue_volume_id == volume_id)
        .filter(database.ReadIssues.read_user_id == user.id)
        .filter(database.OwnedIssues.owned_user_id == user.id)
    ).order_by(database.Issues.issue_id.asc()).distinct().offset(offset).limit(limit).all()

    data = database.IssuesSchema(many=True).dump(issues)
    
    return create_json_return('200', results=data, total_results=total_issues)

""" --- SINGLE VOLUME SINGLE ISSUE --- """

# GET

# POST

# PUT

# DELETE

""" --- SINGLE VOLUME SINGLE ISSUE IMAGE --- """

# GET

""" --- ALL ISSUES --- """

# GET

""" --- SINGLE ISSUE --- """

# GET
@api.route('/issues/<issue_id>', methods=["GET"])
def api_get_single_issue(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - issues
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    issue = database.session \
        .query(database.Issues) \
        .filter(database.Issues.issue_id == issue_id) \
        .first()

    volumes_folder = stashrconfig['DIRECTORY']['comics']
    volume_folder = issue.volume.directory.directory_path
    issue_file = issue.issue_file_path

    issue_file = os.path.join(
        paths.base_path,
        volumes_folder,
        volume_folder,
        issue_file
    )

    if not os.path.isfile(issue_file):
        return jsonify(create_json_return('404'))

    filetype = pathlib.Path(issue_file).suffix

    if filetype == '.cbz':
        issue_file = ZipFile(issue_file)
    elif filetype == '.cbr' and use_rar:
        issue_file = RarFile(issue_file)
    else:
        return(jsonify(create_json_return('500')))

    images = []

    for name in issue_file.namelist():
        if pathlib.Path(name).suffix == '.jpg':
            image = {}
            image['id'] = issue_file.namelist().index(name)
            image['path'] = urllib.parse.quote(name)
            images.append(image)

    return jsonify(create_json_return('200', results=natsorted(images, key=lambda image: image['path'])))

# POST
@api.route('/issues/<issue_id>', methods=["POST"])
def api_post_single_issue(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - issues
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))

    if 'file' not in request.files:
        return jsonify(create_json_return('400'))

    issue = database.session \
        .query(database.Issues) \
        .filter(database.Issues.issue_id == issue_id) \
        .first()

    if issue is None:
        return jsonify(create_json_return('404'))

    if issue.issue_file_path is not None:
        existing_file = os.path.join(
            folders.StashrFolders().comic_folder(),
            issue.volume.directory.directory_path,
            issue.issue_file_path
        )
        if os.path.isfile(existing_file):
            return jsonify(create_json_return('409'))

    file = request.files['file']

    file_path = os.path.join(
        folders.StashrFolders().comic_folder(),
        issue.volume.directory.directory_path,
        f'{naming.file_name_by_db(issue)}{pathlib.Path(file.filename).suffix}'
    )
    
    volume_path = os.path.join(
        folders.StashrFolders().comic_folder(),
        issue.volume.volume_path,
    )

    if not os.path.exists(volume_path):
        os.makedirs(volume_path)

    file.save(file_path)

    issue.issue_file_path = pathlib.Path(file_path).name
    issue.issue_file_status = True
    issue.issue_file_date = datetime.date.today()

    database.session.commit()

    utils.refresh_single_volume(issue.volume.volume_id)

    return jsonify(create_json_return('200'))

# PUT
@api.route('/issues/<issue_id>', methods=["PUT"])
def api_put_single_issue(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - issues
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    issue = (
        database.session.query(database.Issues)
        .join(database.Issues.read_status)
        .options(contains_eager(database.Issues.read_status))
        .join(database.Issues.owned_status)
        .options(contains_eager(database.Issues.owned_status))
        .filter(database.Issues.issue_id == issue_id)
        .filter(database.ReadIssues.read_user_id == user.id)
        .filter(database.OwnedIssues.owned_user_id == user.id)
    ).first()

    if issue is None:
        return jsonify(create_json_return('404'))

    allowed_keys = [
        'read_status',
        'owned_status'
    ]

    error = False

    for key, value in request.json['data'].items():
        if key not in allowed_keys:
            error = True
            break
        if key == 'read_status':
            issue.read_status[0].read_status = value
            continue
        if key == 'owned_status':
            issue.owned_status[0].owned_status = value
            continue
        setattr(issue, key, value)

    if error:
        return jsonify(create_json_return('400'))
    
    database.session.merge(issue)
    database.session.commit()
    return jsonify(create_json_return('200'))

# DELETE
@api.route('/issues/<issue_id>', methods=["DELETE"])
def api_delete_single_issue(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - issues
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))
    
    issue = database.session \
        .query(database.Issues) \
        .filter(database.Issues.issue_id == issue_id) \
        .first()
    
    if issue is None:
        return jsonify(create_json_return('404'))

    utils.delete_comic_file(issue_id)

    utils.refresh_single_volume(issue.volume.volume_id)

    return create_json_return('200')

""" --- SINGLE ISSUE IMAGE --- """

# GET
@api.route('/issues/<issue_id>/<path:image_file>', methods=["GET"])
def api_single_issue_image(issue_id, image_file):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - issues
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    issue = database.session \
        .query(database.Issues) \
        .filter(database.Issues.issue_id == issue_id) \
        .first()

    volumes_folder = volume_folder = stashrconfig['DIRECTORY']['comics']
    volume_folder = issue.volume.directory.directory_path
    issue_file = issue.issue_file_path

    issue_file = os.path.join(
        paths.base_path,
        volumes_folder,
        volume_folder,
        issue_file
    )
    
    if not os.path.isfile(issue_file):
        return jsonify(create_json_return('404'))

    print(issue_file)

    filetype = pathlib.Path(issue_file).suffix

    if filetype == '.cbz':
        issue_file = ZipFile(issue_file)
    elif filetype == '.cbr' and use_rar:
        issue_file = RarFile(issue_file)
    else:
        return(jsonify(create_json_return('500')))

    image_data = issue_file.read(image_file)
    issue_file.close()

    return send_file(
        io.BytesIO(image_data),
        attachment_filename=image_file,
        mimetype='image/jpg'
    )

""" --- ALL PUBLISHERS --- """

# GET
@api.route('/publishers', methods=["GET"])
def api_get_all_publishers():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - publishers
    """

    user = current_user
    
    if not user.is_authenticated:
        if "api_key" not in request.args:
            return jsonify(create_json_return('100'))
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.Publishers) \
        .count()

    publishers = database.session \
        .query(database.Publishers) \
        .order_by() \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.PublishersSchema(many=True).dump(publishers)
    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/publishers', methods=["POST"])
def api_post_all_publishers():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - publishers
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()
    
    if user is None:
        return jsonify(create_json_return('100'))

    allowed_keys = [
        'publisher_id',
    ]

    for key, value in request.json['data'].items():
        if key not in allowed_keys:
            return jsonify(create_json_return('400'))
        if key == 'publisher_id':
            utils.add_publisher(value)
            return jsonify(create_json_return('201'))

""" --- SINGLE PUBLISHER --- """

# GET
@api.route('/publishers/<publisher_id>', methods=["GET"])
def api_get_single_publisher(publisher_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - publishers
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    total_results = database.session \
        .query(database.Publishers) \
        .filter(database.Publishers.publisher_id == publisher_id) \
        .count()

    publisher = database.session \
        .query(database.Publishers) \
        .filter(database.Publishers.publisher_id == publisher_id) \
        .first()

    data = database.PublishersSchema().dump(publisher)
    return create_json_return('200', total_results=total_results, results=data)

# POST

""" --- SINGLE PUBLISHER ALL VOLUMES --- """

# GET
@api.route('/publishers/<publisher_id>/volumes', methods=["GET"])
def api_get_single_publisher_volumes(publisher_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - publishers
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.Volumes) \
        .filter(database.Volumes.volume_publisher_id == publisher_id) \
        .filter(database.Volumes.volume_age_rating <= user.rating_allowed) \
        .count()

    publisher_volumes = database.session \
        .query(database.Volumes) \
        .filter(database.Volumes.volume_publisher_id == publisher_id) \
        .filter(database.Volumes.volume_age_rating <= user.rating_allowed) \
        .order_by(database.Volumes.volume_sort_title) \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.VolumesSchema(many=True).dump(publisher_volumes)
    return create_json_return('200', total_results=total_results, results=data)

""" --- NEW RELEASES --- """

# GET
@api.route('/newreleases', methods=["GET"])
def api_get_new_releases():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - newreleases
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.NewReleases) \
        .count()

    releases = database.session \
        .query(database.NewReleases) \
        .order_by(database.NewReleases.new_release_id) \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.NewReleasesSchema(many=True).dump(releases)
    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/newreleases', methods=["POST"])
def api_post_new_releases():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - newreleases
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()
    
    if user is None:
        return jsonify(create_json_return('100'))

    utils.update_release_list()

    return jsonify(create_json_return('200'))

""" --- READING LIST --- """

# GET
@api.route('/readinglist', methods=["GET"])
def api_get_reading_list():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - readinglist
    """

    user = current_user

    if not user.is_authenticated:
        if "api_key" not in request.args:
            return jsonify(create_json_return('100'))
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id) \
        .count()

    issues = (
        database.session.query(database.Issues)
        .join(database.Issues.readinglist)
        .options(contains_eager(database.Issues.readinglist))
        .filter(database.ReadingLists.reading_list_user_id == user.id)
        .join(database.Issues.read_status)
        .options(contains_eager(database.Issues.read_status))
        .filter(database.ReadIssues.read_user_id == user.id)
        .join(database.Issues.owned_status)
        .options(contains_eager(database.Issues.owned_status))
        .filter(database.OwnedIssues.owned_user_id == user.id)
    ).order_by(database.ReadingLists.reading_list_position.asc()).distinct().offset(offset).limit(limit).all()

    # data = database.ReadingListsSchema(many=True).dump(readinglist)
    data = database.IssuesSchema(many=True).dump(issues)

    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/readinglist', methods=["POST"])
def api_post_reading_list():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - readinglist
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    issue_id = request.json['issue_id']

    current_list = database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id) \
        .all()

    existing_issue = database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id,
                database.ReadingLists.reading_list_issue_id == issue_id) \
        .first()

    if existing_issue is not None:
        return jsonify(create_json_return('409'))

    new_entry = database.ReadingLists(
        reading_list_user_id = user.id,
        reading_list_issue_id = issue_id,
        reading_list_position = len(current_list)+1
    )

    database.session.add(new_entry)
    database.session.commit()

    return jsonify(create_json_return('200'))

# PUT
@api.route('/readinglist', methods=["PUT"])
def api_put_reading_list():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - readinglist
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    allowed_keys = [
        'old_index',
        'new_index'
    ]

    for key, value in request.json['data'].items():
        if key not in allowed_keys:
            return jsonify(create_json_return('400'))

    
    reading_list = database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id) \
        .order_by(database.ReadingLists.reading_list_position) \
        .all()
    
    reading_list.insert(request.json['data']['new_index'], reading_list.pop(request.json['data']['old_index']))

    for item in reading_list:
        item.reading_list_position = reading_list.index(item)
    
    database.session.commit()

    return jsonify(create_json_return('200'))

# DELETE
@api.route('/readinglist', methods=["DELETE"])
def api_delete_reading_list():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - readinglist
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id) \
        .delete()

    return jsonify(create_json_return('200'))

""" --- READING LIST SINGLE ISSUE --- """

# DELETE
@api.route('/readinglist/<issue_id>', methods=["DELETE"])
def api_delete_reading_list_single(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - readinglist
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    check_issue = database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id,
                database.ReadingLists.reading_list_issue_id == issue_id) \
        .first()

    if check_issue is None:
        return jsonify(create_json_return('404'))

    database.session \
        .query(database.ReadingLists) \
        .filter(database.ReadingLists.reading_list_user_id == user.id,
                database.ReadingLists.reading_list_issue_id == issue_id) \
        .delete()

    database.session.commit()

    # TODO: Update order numbers on reading list after the removal of an issue

    return jsonify(create_json_return('200'))

""" --- ALL COLLECTIONS --- """

# GET
@api.route('/collections', methods=["GET"])
def api_get_all_collections():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        if "api_key" not in request.args:
            return jsonify(create_json_return('100'))
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.Collections) \
        .filter(database.Collections.collection_user_id == user.id) \
        .count()

    collections = database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_user_id == user.id,
                    database.Collections.collection_public == 1)) \
        .order_by(database.Collections.collection_id) \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.CollectionsSchema(many=True).dump(collections)
    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/collections', methods=["POST"])
def api_post_all_collections():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection_id = utils.create_new_collection(user.id, request.json['data']['collection_name'])
    if request.json['data']['issue_id']:
        utils.add_issue_to_collection(collection_id, request.json['data']['issue_id'])
    
    return jsonify(create_json_return('200'))

""" --- SINGLE COLLECTION --- """

# GET
@api.route('/collections/<collection_slug>', methods=["GET"])
def api_get_single_collection(collection_slug):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection = database.session \
        .query(database.Collections) \
        .filter(database.Collections.collection_slug == collection_slug) \
        .first()

    if collection.collection_user_id != user.id and collection.collection_public == 'false':
        return create_json_return('409')

    total_results = database.session \
        .query(database.Collections) \
        .filter(database.Collections.collection_slug == collection_slug) \
        .count()

    data = database.CollectionsSchema().dump(collection)
    return create_json_return('200', results=data, total_results=total_results)

# POST
@api.route('/collections/<collection_slug>', methods=["POST"])
def api_post_single_collection(collection_slug):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection = database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_id == collection_slug,
                    database.Collections.collection_slug == collection_slug)) \
        .first()

    if collection is None:
        return jsonify(create_json_return('404'))

    if collection.collection_user_id != user.id:
        return jsonify(create_json_return('401'))

    try:
        issue_id = request.json['data']['issue_id']

        check_issue = database.session \
            .query(database.CollectionLinks) \
            .filter(database.CollectionLinks.collection_link_collection_id == collection_slug,
                    database.CollectionLinks.collection_link_issue_id == issue_id) \
            .first()

        if check_issue is not None:
            return jsonify(create_json_return('409'))

        utils.add_issue_to_collection(collection_slug, issue_id)
    except:
        return jsonify(create_json_return('400'))
    finally:
        return jsonify(create_json_return('200'))

# PUT
@api.route('/collections/<collection_slug>', methods=["PUT"])
def api_put_single_collection(collection_slug):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """
    
    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection = database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_id == collection_slug,
                    database.Collections.collection_slug == collection_slug)) \
        .first()

    if collection is None:
        return jsonify(create_json_return('404'))

    if collection.collection_user_id != user.id:
        return jsonify(create_json_return('401'))

    allowed_keys = [
        'old_index',
        'new_index',
        'collection_name',
        'collection_public',
        'collection_age_rating',
        'collection_description',
        'collection_cover_image'
    ]

    for key, value in request.json['data'].items():
        # print(f'{key} - {value}')
        if key not in allowed_keys:
            return jsonify(create_json_return('400'))
        if key == 'old_index' or key == 'new_index':
            continue
        setattr(collection, key, value)
        # database.session.commit()
    
    database.session.commit()
    
    # print('just before update order')
    if 'old_index' in request.json['data'] and 'new_index' in request.json['data']:
        # print('updating order')
        collection_items = database.session \
            .query(database.CollectionLinks) \
            .filter(database.CollectionLinks.collection_link_collection_id == collection.collection_id) \
            .order_by(database.CollectionLinks.collection_link_issue_position) \
            .all()

        collection_items.insert(request.json['data']['new_index'], collection_items.pop(request.json['data']['old_index']))

        for item in collection_items:
            # print(item)
            item.collection_link_issue_position = collection_items.index(item)

        database.session.commit()
    
    return jsonify(create_json_return('200'))

# DELETE
@api.route('/collections/<collection_slug>', methods=["DELETE"])
def api_delete_single_collection(collection_slug):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection = database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_id == collection_slug,
                    database.Collections.collection_slug == collection_slug)) \
        .first()

    if user.id != collection.collection_user_id:
        return jsonify(create_json_return('403'))

    database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_id == collection_slug,
                    database.Collections.collection_slug == collection_slug)) \
        .delete()

    database.session \
        .query(database.CollectionLinks) \
        .filter(database.CollectionLinks.collection_link_collection_id == collection.collection_id) \
        .delete()

    database.session.commit()

    return jsonify(create_json_return('200'))

""" --- SINGLE COLLECTION ALL ISSUES --- """

# GET
@api.route('/collections/<collection_slug>/issues', methods=["GET"])
def api_get_collection_all_issues(collection_slug):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    collection = database.session \
        .query(database.Collections) \
        .filter(database.Collections.collection_slug == collection_slug) \
        .first()

    if collection.collection_user_id != user.id and collection.collection_public == 'false':
        return create_json_return('409')

    total_results = database.session \
        .query(database.CollectionLinks) \
        .filter(database.CollectionLinks.collection_link_collection_id == collection.collection_id) \
        .count()

    issues = (
        database.session.query(database.Issues)
        .join(database.Issues.collections)
        .options(contains_eager(database.Issues.collections))
        .filter(database.CollectionLinks.collection_link_collection_id == collection.collection_id)
        .join(database.Issues.read_status)
        .options(contains_eager(database.Issues.read_status))
        .filter(database.ReadIssues.read_user_id == user.id)
        .join(database.Issues.owned_status)
        .options(contains_eager(database.Issues.owned_status))
        .filter(database.OwnedIssues.owned_user_id == user.id)
    ).order_by(database.CollectionLinks.collection_link_issue_position.asc()).distinct().offset(offset).limit(limit).all()

    data = database.IssuesSchema(many=True).dump(issues)

    # print(data)

    return create_json_return('200', results=data, total_results=total_results)
    
""" --- SINGLE COLECTION SINGLE ISSUE --- """

# GET

# POST
@api.route('/collections/<collection_slug>/issues/<issue_id>', methods=["POST"])
def api_post_collection_issue(collection_slug, issue_id):
    pass

# DELETE
@api.route('/collections/<collection_slug>/issues/<issue_id>', methods=["DELETE"])
def api_delete_collection_issue(collection_slug, issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - collections
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    collection = database.session \
        .query(database.Collections) \
        .filter(or_(database.Collections.collection_id == collection_slug,
                    database.Collections.collection_slug == collection_slug)) \
        .first()

    if user.id != collection.collection_user_id:
        return jsonify(create_json_return('403'))

    database.session \
        .query(database.CollectionLinks) \
        .filter(database.CollectionLinks.collection_link_collection_id == collection.collection_id,
                database.CollectionLinks.collection_link_issue_id == issue_id) \
        .delete()

    database.session.commit()

    return jsonify(create_json_return('200'))

""" --- SEARCH --- """

# GET
@api.route('/search', methods=["GET"])
def api_get_search():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - search
    """

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    search_results = cv.search(request.args.get('query'), resources=['volume'], offset=offset, limit=limit)
    total_results = len(search_results.results)

    return jsonify(create_json_return('200', results=search_results.results, total_results=total_results))

""" --- SCRAPE --- """


""" --- SCRAPE DIRECTORIES --- """


""" --- DOWNLOADS --- """

# GET
@api.route('/downloads/<issue_id>', methods=['GET'])
def api_get_download_issue(issue_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - downloads
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    issue = database.session \
        .query(database.Issues) \
        .filter(database.Issues.issue_id == issue_id) \
        .first()

    if issue is None:
        return jsonify(create_json_return('404'))

    issue_file = os.path.join(
        folders.StashrFolders().comic_folder(),
        issue.volume.directory.directory_path,
        issue.issue_file_path
    )

    logger.debug(f'DOWNLOAD PATH: {issue_file}')

    if not os.path.isfile(issue_file):
        return jsonify(create_json_return('404'))

    return send_file(issue_file, as_attachment=True, attachment_filename=pathlib.Path(issue_file).name)

""" --- ALL RATINGS --- """

# GET
@api.route('/ratings', methods=["GET"])
def api_get_all_ratings():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - ratings
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.AgeRatings) \
        .count()

    ratings = database.session \
        .query(database.AgeRatings) \
        .all()

    data = database.AgeRatingsSchema(many=True).dump(ratings)
    return create_json_return('200', total_results=total_results, results=data)


""" --- SETTINGS --- """


@api.route('/settings', methods=["GET"])
def api_get_settings():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - settings
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    settings = stashrconfig

    if "SECURITY" in settings:
        settings.pop("SECURITY")

    for section in settings:
        if "mail_password" in settings[section]:
            settings[section].pop('mail_password')

    return create_json_return('200', results=settings)


@api.route('/settings/<section>', methods=["GET"])
def api_get_settings_single_section(section):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - settings
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    disallowed_sections = [
        'security'
    ]

    if section.lower() in disallowed_sections:
        return create_json_return('405')

    try:
        settings = stashrconfig[section.upper()]

        if 'mail_password' in settings:
            settings.pop('mail_password')

    except:
        return create_json_return('404')

    return create_json_return('200', results=settings)


@api.route('/settings/<section>/<setting>', methods=["GET"])
def api_get_settings_single_section_single_settings(section, setting):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - settings
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    disallowed_sections = [
        'security'
    ]

    if section.lower() in disallowed_sections:
        return create_json_return('405')

    try:
        settings = stashrconfig[section.upper()][setting]
    except:
        return create_json_return('404')

    return create_json_return('200', results=settings)


""" --- API CALLS TO PLACE --- """

@api.route('/users', methods=["GET"])
def api_get_users():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - users
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.Users) \
        .count()

    users = database.session \
        .query(database.Users) \
        .order_by(database.Users.id) \
        .offset(offset) \
        .limit(limit) \
        .all()

    data = database.UsersSchema(many=True).dump(users)
    return create_json_return('200', results=data, total_results=total_results)


@api.route('/users/<user_id>', methods=["GET"])
def api_get_single_user(user_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - users
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()
    
    if user is None:
        return jsonify(create_json_return('100'))

    if user.role == 'admin' or user.id == int(user_id):
        total_results = database.session \
            .query(database.Users) \
            .filter(database.Users.id == user_id) \
            .count()

        get_user = database.session \
            .query(database.Users) \
            .filter(database.Users.id == user_id) \
            .first()

        data = database.UsersSchema().dump(get_user)
        # print(type(data))
        return create_json_return('200', results=data, total_results=total_results)
    else:
        return jsonify(create_json_return('401'))


@api.route('/plugins', methods=["GET"])
def api_get_plugins():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - plugins
    """
    from stashr.stashr import pm

    data  = []

    plugins = pm.get_all_plugins

    for item in plugins:
        list_item = {}
        list_item['plugin_name'] = item.plugin_name
        list_item['plugin_description'] = item.plugin_description
        list_item['plugin_version'] = item.plugin_version
        list_item['plugin_author'] = item.plugin_author
        list_item['plugin_url'] = item.plugin_url
        list_item['plugin_license'] = item.plugin_license
        list_item['plugin_state'] = item.plugin_state
        # list_item[]
        data.append(list_item)

    total_results = len(data)

    return create_json_return('200', results=data, total_results=total_results)


""" --- NEW SCRAPE --- """


@api.route('/scrape/directories', methods=['GET'])
def api_get_directories():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - scrape
    """

    user = current_user

    if not user.is_authenticated:
        api_key = request.args.get('api_key')
        if api_key == "":
            return jsonify(create_json_return('100'))
        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == api_key) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))

    offset = request.args.get('offset') if request.args.get('offset') is not None else 0
    limit = request.args.get('limit') if request.args.get('limit') is not None else 100

    total_results = database.session \
        .query(database.ScrapeItems) \
        .count()

    directories = database.session \
        .query(database.ScrapeItems) \
        .order_by(database.ScrapeItems.scrape_directory.asc()) \
        .offset(offset) \
        .limit(limit) \
        .distinct() \
        .all()

    data = database.ScrapeItemsSchema(many=True).dump(directories)

    return create_json_return('200', results=data, total_results=total_results)


@api.route('/scrape/directories/scan', methods=['POST'])
def api_post_directories_scan():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - scrape
    """

    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))

    # CALL TASK HERE
    # utils.scan_directories()
    tasks.scan_directories()

    return jsonify(create_json_return('200'))


@api.route('/scrape/directories/add', methods=['POST'])
def api_post_directories_add():
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - scrape
    """
    
    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))

    """
    matched_directories = database.session \
        .query(database.ScrapeItems) \
        .filter(database.ScrapeItems.scrape_add == 1) \
        .all()

    print(len(matched_directories))

    for item in matched_directories:
        utils.add_scrape_match(item)
    """

    tasks.add_scraped_directories()

    return jsonify(create_json_return('200'))


@api.route('/scrape/directories/edit/<scrape_id>', methods=['PUT'])
def api_put_directories_edit(scrape_id):
    """To Update Later
    This is using docstrings for specifications.
    ---
    tags:
        - scrape
    """
    
    user = current_user

    if not user.is_authenticated:
        if not request.json:
            return jsonify(create_json_return('400'))
        if "api_key" not in request.json:
            return jsonify(create_json_return('400'))
        if request.json['api_key'] == "":
            return jsonify(create_json_return('100'))

        user = database.session \
            .query(database.Users) \
            .filter(database.Users.api_key == request.json['api_key']) \
            .first()

    if user is None:
        return jsonify(create_json_return('100'))

    if user.role != 'admin':
        return jsonify(create_json_return('401'))
    
    allowed_keys = [
        'scrape_add',
        'scrape_candidate'
    ]

    check_directory = database.session \
        .query(database.ScrapeItems) \
        .filter(database.ScrapeItems.scrape_id == scrape_id) \
        .first()

    if check_directory is None:
         return jsonify(create_json_return('404'))

    for key, value in request.json['data'].items():
        if key not in allowed_keys:
            database.session.rollbak()
            return jsonify(create_json_return('400'))
        setattr(check_directory, key, value)

    database.session.merge(check_directory)
    database.session.commit()
    # print(check_directory.scrape_id)

    return jsonify(create_json_return('200'))


""" --- API WRAPPER --- """

"""---------------------

data = {
    status_code: <code>,
    message: <error_message>,
    number_of_total_results: <number>,
    limit: <number>,
    offset: <offset>,
    results: <results>,
}

possible codes
    200: OK
    201: Created
    405: Method Not Allowed
    409: Conflict (item exists)
    404: Not Found
    406: Not Acceptable

---------------------"""

def create_json_return(status_code, total_results=None, results=None, message=None):

    json = {}

    json['status_code'] = status_code

    if message is None:
        json['message'] = return_codes[status_code]
    else:
        json['message'] = message

    if results is not None:
        if total_results is not None:
            json['number_of_total_results'] = total_results
        json['number_of_page_results'] = len(results)
        json['results'] = results

    return json 