from quart import Quart, request, abort, Response
import os
import sys
import traceback
import asyncio
import json
from threading import Thread
from utils import getNewPort, setInterval, isRunning, writeTracebackToFile, writePortToFileWhenServerIsListening
from omnis_calls import sendResponse, sendError
from omnis_modules import call
from sse import SSEConnection
import urllib3
urllib3.disable_warnings()

def uncaughtException(exc_type, exc_value, exc_traceback):
    traceback_formated = traceback.format_exception(
        exc_type, exc_value, exc_traceback)
    traceback_string = "".join(traceback_formated)
    if sys.argv[3]:
        writeTracebackToFile(sys.argv[3], traceback_string)
    else:
        print(traceback_string, file=sys.stderr)
    sys.exit(-1)


sys.excepthook = uncaughtException


app = Quart(__name__)


@app.before_request
async def check_remote_addr():
    if not (request.remote_addr == '127.0.0.1' or request.remote_addr == '::1' or request.remote_addr == '::ffff:127.0.0.1'):
        abort(403, 'Forbidden')


@app.route('/call/<module>/<methodname>', methods=['POST'])
async def callModule(module, methodname):
    try:
        params = await request.get_json()
        response = SSEConnection()

        async def user_task():
            try:
                result = await call(str(module), str(methodname), params, response)
                await sendResponse(result, response)
            except asyncio.CancelledError:
                await response.close()
            except Exception as e:
                print("ERROR: exception in user_task:", str(e))
                await sendError(str(e), response, 500)

        task = asyncio.create_task(user_task())

        async def stream():
            try:
                yield ": connected\n\n"
                async for msg in response.receive():
                    yield msg
            except asyncio.CancelledError:
                task.cancel()
                await response.close()
                raise

        return Response(
            stream(),
            mimetype="text/event-stream",
            headers={
                "Cache-Control": "no-cache",
                "X-Accel-Buffering": "no"
            },
        )

    except Exception as e:
        print("ERROR: exception in callModule:", str(e))
        return Response(str(e), status=500)

@app.route('/alive', methods=['GET'])
async def alive():
    return Response("OK")

if __name__ == '__main__':
    port = getNewPort()
    setInterval(0.5, isRunning, int(sys.argv[2]))
    Thread(target=writePortToFileWhenServerIsListening, args=(port,)).start()

    import hypercorn.asyncio
    from hypercorn.config import Config
    config = Config()
    config.bind = [f'localhost:{port}']
    config.certfile = os.path.join(os.path.dirname(__file__), 'cert.pem')
    config.keyfile = os.path.join(os.path.dirname(__file__), 'key.pem')
    config.http2 = True

    async def start_server():
        await hypercorn.asyncio.serve(app, config, shutdown_trigger=lambda: asyncio.Future())

    asyncio.run(start_server())
