import dataclasses
import os.path
from typing import TYPE_CHECKING

import aiofiles
from nestipy_metadata import Reflect
from openapidocs.common import Serializer
from openapidocs.v3 import OpenAPI

from nestipy.common.http_ import Request, Response
from .constant import OPENAPI_HANDLER_METADATA

if TYPE_CHECKING:
    from ..core.nestipy_application import NestipyApplication


@dataclasses.dataclass
class _Document:
    json: str
    yml: str
    obj: dict


class SwaggerModule:

    @classmethod
    def _create_document(cls, app: "NestipyApplication", config: OpenAPI) -> _Document:
        paths = app.get_openapi_paths()
        config.paths = paths
        serializer = Serializer()
        document_json = serializer.to_json(config)
        document_yml = serializer.to_yaml(config)
        document_obj = serializer.to_obj(config)
        return _Document(document_json, document_yml, document_obj)

    @classmethod
    def setup(cls, path: str, app: "NestipyApplication", config: OpenAPI):
        def register_open_api():
            document: _Document = cls._create_document(app, config)
            api_path = f"/{path.strip('/')}"

            async def openapi_json_handler(_req: Request, res: Response, _next_fn):
                return await res.status(200).header('Content-Type', 'application/json').send(document.json)

            async def openapi_yml_handler(_req: Request, res: Response, _next_fn):
                return await res.status(200).header('Content-Type', 'text/yml').send(document.yml)

            async def openapi_swagger_handler(_req: Request, res: Response, _next_fn):
                file = await aiofiles.open(os.path.join(os.path.dirname(__file__), 'swagger.html'), 'r')
                response = await res.status(200).html(await file.read())
                await file.close()
                return response

            http_adapter = app.get_adapter()
            http_adapter.get('/openapi.json', openapi_json_handler, {})
            http_adapter.get('/openapi.yml', openapi_yml_handler, {})
            http_adapter.get(api_path, openapi_swagger_handler, {})

        Reflect.set_metadata(app, OPENAPI_HANDLER_METADATA, register_open_api)
