import flask
import flask_sock

from abstra_internals.controllers.main import MainController
from abstra_internals.entities.execution_context import (
    FormContext,
    extract_flask_request,
)
from abstra_internals.logger import AbstraLogger
from abstra_internals.repositories.project.project import FormStage
from abstra_internals.usage import editor_usage
from abstra_internals.utils import is_it_true
from abstra_internals.utils.websockets import bind_ws_with_connection


def _get_user_jwt():
    return flask.request.cookies.get("editor_auth")


def get_editor_bp(controller: MainController):
    bp = flask.Blueprint("editor_forms", __name__)
    sock = flask_sock.Sock(bp)

    @sock.route("/socket")
    def _websocket(ws: flask_sock.Server):
        connection = None
        try:
            context = FormContext(
                request=extract_flask_request(flask.request),
            )

            id = flask.request.args.get("id")
            if id is None:
                return

            form = controller.get_form(id)
            if not form:
                return

            user_jwt = _get_user_jwt()
            connection = controller.repositories.producer.enqueue(
                id, context, user_jwt=user_jwt
            )

            bind_ws_with_connection(ws, connection, block=True)
        except Exception as e:
            AbstraLogger.capture_exception(e)
        finally:
            if connection is not None:
                try:
                    connection.close()
                except Exception as e:
                    AbstraLogger.capture_exception(e)
            ws.close(message="Done")

    @bp.get("/")
    @editor_usage
    def _get_forms():
        return [f.editor_dto for f in controller.get_forms()]

    @bp.get("/<path:id>")
    @editor_usage
    def _get_form(id: str):
        form = controller.get_form(id)
        if not form:
            flask.abort(404)
        return form.editor_dto

    @bp.delete("/<path:id>")
    @editor_usage
    def _delete_form(id: str):
        remove_file = flask.request.args.get(
            "remove_file", default=False, type=is_it_true
        )
        controller.delete_stage(id, remove_file)
        return {"success": True}

    @bp.post("/")
    @editor_usage
    def _create_form():
        data = flask.request.json
        if not data:
            flask.abort(400)
        title = data.get("title")
        file = data.get("file")
        if not title or not file:
            flask.abort(400)
        workflow_position = data.get("position", (0, 0))
        id = data.get("id", None)
        form = controller.create_form(title, file, workflow_position, id)
        return form.editor_dto

    @bp.put("/<path:id>")
    @editor_usage
    def _update_form(id: str):
        data = flask.request.json
        if not data:
            flask.abort(400)

        form = controller.update_stage(id, data)
        if isinstance(form, FormStage):
            return form.editor_dto
        else:
            return None

    return bp
