import flask

from ..api import API
from ...usage import usage
from ...settings import Settings
from .utils import send_from_dist
from ...session import StaticSession
from ..runtimes import run_job, run_hook


def get_editor_bp(api: API):
    bp = flask.Blueprint("editor", __name__)

    @bp.route("/api/assets/<path:path>", methods=["GET"])
    def _asset(path):
        return flask.send_from_directory(directory=Settings.root_path, path=path)

    @bp.route("/api/workspace", methods=["GET"])
    @usage
    def get_workspace():
        return api.get_workspace().editor_dto

    @bp.route("/api/workspace", methods=["PUT"])
    @usage
    def update_workspace():
        workspace = api.update_workspace(flask.request.json)
        return workspace.editor_dto

    @bp.route("/api/workspace/root-path", methods=["GET"])
    @usage
    def get_workspace_root_path():
        return str(Settings.root_path.absolute())

    @bp.route("/api/workspace/open-file", methods=["POST"])
    @usage
    def open_file():
        file_path = flask.request.json["path"]
        api.open_file(file_path, create_if_not_exists=True)
        return {"success": True}

    @bp.route("/api/workspace/check-file", methods=["GET"])
    @usage
    def check_file():
        file_path = flask.request.args["path"]
        return {"exists": api.check_file(file_path)}

    @bp.route("/api/workspace/deploy", methods=["POST"])
    @usage
    def deploy():
        api.deploy()
        return {"success": True}

    @bp.route("/api/forms/", methods=["GET"])
    @usage
    def get_forms():
        return [f.editor_dto for f in api.get_forms()]

    @bp.route("/api/forms/<path:path>", methods=["GET"])
    @usage
    def get_form(path: str):
        form = api.get_form(path)
        if not form:
            flask.abort(404)
        return form.editor_dto

    @bp.route("/api/forms/<path:path>", methods=["DELETE"])
    @usage
    def delete_form(path: str):
        api.delete_form(path)
        return {"success": True}

    @bp.route("/api/dashes/<path:path>", methods=["GET"])
    @usage
    def get_dash(path: str):
        dash = api.get_dash(path)
        if not dash:
            flask.abort(404)
        return dash.editor_dto

    @bp.route("/api/forms/", methods=["POST"])
    @usage
    def create_form():
        form = api.create_form()
        return form.editor_dto

    @bp.route("/api/forms/<path:path>", methods=["PUT"])
    @usage
    def update_form(path: str):
        data = flask.request.json
        if not data:
            flask.abort(400)

        form = api.update_form(path, data)
        return form.editor_dto if form else None

    @bp.route("/api/dashes/", methods=["GET"])
    @usage
    def get_dashes():
        return [f.editor_dto for f in api.get_dashes()]

    @bp.route("/api/dashes/", methods=["POST"])
    @usage
    def create_dash():
        dash = api.create_dash()
        return dash.editor_dto

    @bp.route("/api/dashes/<path:path>", methods=["PUT"])
    @usage
    def update_dash(path: str):
        data = flask.request.json
        if not data:
            flask.abort(400)

        dash = api.update_dash(path, data)
        return dash.editor_dto if dash else None

    @bp.route("/api/dashes/<path:path>", methods=["DELETE"])
    @usage
    def delete_dash(path: str):
        api.delete_dash(path)
        return {"success": True}

    @bp.route("/<path:filename>", methods=["GET"])
    @usage
    def spa(filename: str):
        return send_from_dist(filename, "editor.html")

    @bp.route("/", methods=["GET"])
    @usage
    def spa_index():
        return send_from_dist("editor.html", "editor.html")

    @bp.route("/api/hooks/<path:path>", methods=["GET"])
    @usage
    def get_hook(path: str):
        hook = api.get_hook(path)
        if not hook:
            flask.abort(404)
        return hook.editor_dto

    @bp.route("/api/hooks/", methods=["GET"])
    @usage
    def get_hooks():
        return [f.editor_dto for f in api.get_hooks()]

    @bp.route("/api/hooks/", methods=["POST"])
    @usage
    def create_hook():
        hook = api.create_hook()
        return hook.editor_dto

    @bp.route("/api/hooks/<path:path>", methods=["PUT"])
    @usage
    def update_hook(path: str):
        data = flask.request.json
        if not data:
            flask.abort(400)

        hook = api.update_hook(path, data)
        return hook.editor_dto if hook else None

    @bp.route("/api/hooks/<path:path>", methods=["DELETE"])
    @usage
    def delete_hook(path: str):
        api.delete_hook(path)
        return {"success": True}

    @bp.route(
        "/api/hooks/<path:path>/test", methods=["POST", "GET", "PUT", "DELETE", "PATCH"]
    )
    @usage
    def test_hook(path: str):
        hook = api.get_hook(path)
        if not hook:
            flask.abort(404)

        body, status, headers = run_hook(flask.request, hook)
        session = StaticSession.get_session()
        return {
            "body": body,
            "status": status,
            "headers": headers,
            "stdout": "".join(session.stdout if session else []),
            "stderr": "".join(session.stderr if session else []),
        }

    @bp.route("/api/jobs/<path:identifier>", methods=["GET"])
    @usage
    def get_job(identifier: str):
        job = api.get_job(identifier)
        if not job:
            flask.abort(404)
        return job.editor_dto

    @bp.route("/api/jobs/", methods=["GET"])
    @usage
    def get_jobs():
        return [f.editor_dto for f in api.get_jobs()]

    @bp.route("/api/jobs/", methods=["POST"])
    @usage
    def create_job():
        job = api.create_job()
        return job.editor_dto

    @bp.route("/api/jobs/<path:identifier>", methods=["PUT"])
    @usage
    def update_job(identifier: str):
        data = flask.request.json
        if not data:
            flask.abort(400)

        job = api.update_job(identifier, data)
        return job.editor_dto if job else None

    @bp.route("/api/jobs/<path:identifier>", methods=["DELETE"])
    @usage
    def delete_job(identifier: str):
        api.delete_job(identifier)
        return {"success": True}

    @bp.route("/api/jobs/<path:identifier>/test", methods=["POST"])
    @usage
    def test_job(identifier: str):
        job = api.get_job(identifier)
        if not job:
            flask.abort(404)

        run_job(job)
        session = StaticSession.get_session()

        return {
            "stdout": "".join(session.stdout if session else []),
            "stderr": "".join(session.stderr if session else []),
        }

    @bp.route("/api/login", methods=["GET"])
    @usage
    def _get_login():
        return api.get_login()

    @bp.route("/api/login", methods=["POST"])
    @usage
    def _create_login():
        data = flask.request.json
        if not data:
            flask.abort(400)

        return api.create_login(data["token"])

    @bp.route("/api/login", methods=["DELETE"])
    @usage
    def _delete_login():
        return api.delete_login()

    @bp.route("/api/ai/message", methods=["POST"])
    @usage
    def _get_next_message():
        body = flask.request.json
        if not body:
            flask.abort(400)

        streamer = api.send_ai_message(body["messages"], body["runtime"])

        if streamer is None:
            flask.abort(403)

        return flask.Response(streamer, mimetype="text/event-stream")

    return bp
