#!/usr/bin/env python3
import os
import sys

sys.path.insert(0, "/usr/share/opengnsys-modules/python3/dist-packages")


import importlib
import logging
import uuid
import argparse
import yaml
from flask import Flask, request
from flask_executor import Executor
from flask_restx import Api
from flasgger import Swagger
from werkzeug.exceptions import HTTPException
from systemd.journal import JournalHandler


parser = argparse.ArgumentParser(
    prog="api_server.py",
    description="OpenGnsys Repository API Server",
)

debug_enabled = False
listen_host = '0.0.0.0'
listen_port = 8006
parser.add_argument('--debug', action='store_true', help="Enable debug output")
parser.add_argument('--listen', metavar="HOST", help="Listen address")
parser.add_argument('--port', metavar="PORT", help="Listen port")


parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")

args = parser.parse_args()



log = logging.getLogger('api_server')
log.addHandler(JournalHandler())

if args.verbose:
    log.addHandler(logging.StreamHandler(stream=sys.stderr))
    log.setLevel(logging.DEBUG)
else:
    log.setLevel(logging.INFO)

if args.listen:
    listen_host = args.listen

if args.port:
    listen_port = args.port


if args.debug:
    debug_enabled = True


api_base_dir = os.path.dirname(os.path.realpath(__file__))
blueprints_dir = os.path.join(api_base_dir, 'blueprints')
installer_dir = os.path.join(api_base_dir, '../installer')




sys.path.insert(0, installer_dir)





# Create an instance of the Flask class
app = Flask(__name__)
api = Api(app,
          version='0.50',
          title = "OpenGnsys Git API",
          description = "API for managing disk images stored in Git",
          doc = "/apidocs/")


executor = Executor(app)

log.info("Loading blueprints from %s", blueprints_dir)
sys.path.insert(0, blueprints_dir)

for filename in os.listdir(blueprints_dir):
    if filename.endswith('.py'):

        log.info("Loading %s/%s", blueprints_dir, filename)

        module_name = filename.replace(".py", "")
        swagger_file = os.path.join(blueprints_dir, filename.replace(".py", ".yaml"))

        log.info("Importing %s", module_name)
        importlib.invalidate_caches()
        module = importlib.import_module(module_name)
        log.debug("Returned: %s", module)

        app.register_blueprint(module.blueprint)

        if os.path.exists(swagger_file):
            log.info("Loading Swagger documentation from %s...", swagger_file)

            with open(swagger_file, "r", encoding='utf-8') as file:
                swagger_template = yaml.safe_load(file)

            swagger = Swagger(app, template=swagger_template)
        else:
            log.warning("Swagger not found for this module, looked in %s", swagger_file)



@app.errorhandler(HTTPException)
def handle_exception(e):
    """Return JSON for HTTP errors.

    We create and log an error UUID for each error, and use journald's additional fields for easier searching.
    """
    # start with the correct headers and status code from the error
    response = e.get_response()

    errid = uuid.uuid4().hex


    response = {
        "errcode": e.code,
        "errname": e.name,
        "description": e.description,
    }

    log.error("Error ID %s: code %i, name %s, description %s", errid, e.code, e.name, e.description, extra = { "error_id" : errid, "errcode" : e.code, "errname" : e.name, "description" : e.description })

    return response, 500

@app.after_request
def after_request(response):
    log.info("Request from %s: %s %s %s %s", request.remote_addr, request.method, request.scheme, request.full_path, response.status,
             extra = {"remote_addr" : request.remote_addr, "method" : request.method, "scheme" : request.scheme, "full_path" : request.full_path, "status" : response.status})

    if debug_enabled:
        log.debug("Response: %s", response.data, extra = {"response" : response.data})

    return response




# Run the Flask app
if __name__ == '__main__':
    print(f"Map: {app.url_map}")
    app.run(debug=debug_enabled, host=listen_host, port=listen_port)
