import os
import logging
from rq import Queue
from functools import wraps
import multiprocessing as mp
from time import strftime, gmtime
from flask import Flask, jsonify, request
from werkzeug.utils import secure_filename
from archivingsystem.common import listing
from archivingsystem.database import Database
from archivingsystem.validation import validating
from archivingsystem.archiving import archiving_task

log = logging.getLogger(__name__)

class Endpoint:
    def __init__(self, config: dict, q: Queue) -> None:
        self.app = Flask(__name__)
        self.config = config
        self.q = q
        self.app.add_url_rule("/api/status", "status", self.status, methods=['GET'])
        self.app.add_url_rule("/api/list-files", "list_files", self.list_files, methods=['GET'])
        self.app.add_url_rule("/api/validate-file", "validate_file", self.validate_file, methods=['GET'])
        self.app.add_url_rule("/api/validate-files", "validate_files", self.validate_files, methods=['GET'])
        self.app.add_url_rule("/api/submit-file", "submit_file", self.submit_file, methods=['POST'])

    def authenticate(f):
        """
        Decorator for client authentication. Checks if request header 
        contains valid 'x-access-secret' field.
        """
        @wraps(f)
        def decorated(self, *args, **kwargs):
            secret = None
            # Is secret passed in the request header
            if 'x-access-secret' in request.headers:
                secret = request.headers['x-access-secret']
            # If no secret found
            if not secret:
                log.debug('secret missing')
                return jsonify({
                    'message' : 'Required secret field is missing!'
                    }), 401
    
            # Check if secret value is valid
            if secret == self.config["endpoint"]["secret"]:
                # Return to the route
                return f(self, *args, **kwargs)

            # If secret is invalid
            return jsonify({
                'message' : 'Value of secret is invalid!'
            }), 401
    
        return decorated

    @authenticate
    def status(self):
        return jsonify({'status' : 'online'})

    @authenticate
    def submit_file(self):
        # Check if the post request has the file part
        if 'file' not in request.files:
            log.debug(request)
            resp = jsonify({'message' : 'Required file part in the request is missing!'})
            resp.status_code = 401
            return resp
        
        file = request.files['file']

        # Check if the post request has the owner part
        if 'owner' not in request.form:
            log.debug(request)
            resp = jsonify({'message' : 'Required owner part in the request is missing!'})
            resp.status_code = 401
            return resp
        
        owner = request.form['owner']

        # Check if file was submitted
        if file.filename == '':
            resp = jsonify({'message' : 'No file selected for submition!'})
            resp.status_code = 401
            return resp
        
        # Save file to local directory and add task to queue
        if file:
            filename = secure_filename(file.filename)
            basepath = os.path.join(self.config["tmp_folder"], "toArchive", owner, strftime("%Y%m%d%H%M%S", gmtime()))
            filepath = os.path.join(basepath, filename)

            if not os.path.exists(basepath):
                log.debug(f"Base folder for file {filename} does not exist, creating new")
                os.makedirs(basepath)

            file.save(filepath)

            job = self.q.enqueue(archiving_task, self.config, filepath, owner)
            log.debug(f"Task {job.id} added to {self.q.name} queue, with filepath {filepath}")

            # TODO: Add hook and wait for archivation task to finish
            
            resp = jsonify({'status' : 'File successfully uploaded'})
            resp.status_code = 201
            return resp

    @authenticate
    def list_files(self):
        # Check if the post request has the owner part
        if 'owner' not in request.form:
            log.debug(request)
            resp = jsonify({'message' : 'Required owner part in the request is missing!'})
            resp.status_code = 401
            return resp
        
        owner = request.form['owner']

        # Create database connection
        db = Database(self.config)

        # Get list of all files from databse
        files = listing.all(db, True)

        # Files owned by owner
        owner_files = []

        for file in files:
            # Parse only owners files
            if file.OwnerName == owner:
                # Prepare values
                json_data = {}
                json_data["id"] = file.FileID
                json_data["name"] = file.FileName
                json_data["time_of_first_ts"] = file.TimeOfFirstTS.strftime('%Y-%m-%d %H:%M:%S')
                json_data["expiration"] = file.ExpirationDateTS.strftime('%Y-%m-%d %H:%M:%S')
                # Add json object to list
                owner_files.append(json_data)

        resp = jsonify({'files' : owner_files})
        resp.status_code = 200
        return resp

    @authenticate
    def validate_files(self):

        # Create database connection
        db = Database(self.config)

        # Get list of all files from databse
        invalid_ids = validating.all(db, self.config, True)

        # If invalid_ids is on empty
        if len(invalid_ids) > 0:
            resp = jsonify({'ids' : invalid_ids})
            resp.status_code = 400
            return resp
        else:
            resp = jsonify({'message' : 'All files are valid!'})
            resp.status_code = 201
            return resp

    @authenticate   
    def validate_file(self):
        # Check if the post request has the fileid part
        if 'fileid' not in request.form:
            log.debug(request)
            resp = jsonify({'message' : 'Required fileid part in the request is missing!'})
            resp.status_code = 401
            return resp
        
        fileid = request.form['fileid']

        # Validate file
        valid = validating.single(self.config, fileid, True)

        # If file is not valid
        if not valid:
            resp = jsonify({'message' : 'File is invalid!'})
            resp.status_code = 400
            return resp
        else:
            resp = jsonify({'message' : 'File is valid!'})
            resp.status_code = 201
            return resp

    def run(self):
        """
        Function starts API endpoint in separate thread
        """
        log.debug('Endpoint started')
        mp.Process(name="Endpoint", target=lambda: self.app.run(host=self.config["endpoint"]["host"], port=self.config["endpoint"]["port"], debug=self.config["endpoint"]["debug"], use_reloader=False)).start()