from typing import Optional, List, Callable, Coroutine, Any

from fastapi import FastAPI, APIRouter
from fastapi.exceptions import RequestValidationError, StarletteHTTPException
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from loguru import logger

from huibiao_framework.backend import (
    ApiErrorCodeManage,
    PrometheusTimeCostMetricSetting,
)
from huibiao_framework.backend.intercept import (
    http_exception_interceptor,
    validate_interceptor,
)
from huibiao_framework.backend.log_utils import ServiceLoggerManager
from huibiao_framework.backend.metrics import PrometheusContext
from huibiao_framework.backend.metrics.collector import MetricsScheduleMonitor


async def default_life_span_start_event():
    logger.info("用户未设置开始事件")


async def default_life_span_end_event():
    logger.info("用户未设置结束事件")


class HuibiaoDefaultAppGenerator:
    def __init__(
        self,
        *,
        app_error_code_prefix: int,
        service_name: str,
        life_span_start_event: Optional[Callable[[], Coroutine[Any, Any, None]]] = None,
        life_span_end_event: Optional[Callable[[], Coroutine[Any, Any, None]]] = None,
        api_routers: List[APIRouter] = None,
    ):
        # 注册8位错误码前缀
        ApiErrorCodeManage.set_error_code_prefix_env(app_error_code_prefix)
        self.service_name = service_name
        self.api_routers = api_routers or []

        self.__app: Optional[FastAPI] = FastAPI()
        self.__life_span_start = life_span_start_event or default_life_span_start_event
        self.__life_span_end = life_span_end_event or default_life_span_end_event

    def generator(self):
        self.init_app()
        self.add_prometheus_endpoint()
        self.add_router(*self.api_routers)
        self.add_default_cost_middleware()
        self.add_generic_exception_handler()
        self.add_http_exception_handler()
        return self.app

    def get_app(self):
        return self.__app

    @property
    def app(self):
        return self.__app

    def init_app(self, **kwargs):
        self.__app = FastAPI(lifespan=self.gen_life_span(), **kwargs)

    def add_prometheus_endpoint(self) -> "HuibiaoDefaultAppGenerator":
        PrometheusContext.mount_prometheus_endpoint(self.__app)
        return self

    def add_router(self, *api_router: APIRouter) -> "HuibiaoDefaultAppGenerator":
        for rt in api_router:
            self.__app.include_router(rt)
        return self

    def add_default_cost_middleware(self) -> "HuibiaoDefaultAppGenerator":
        self.__app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )
        return self

    def add_generic_exception_handler(self) -> "HuibiaoDefaultAppGenerator":
        """
        @app.exception_handler(RequestValidationError)
        async def generic_exception_handler(request: Request, exc: RequestValidationError):
            return validate_interceptor(request=request, exc=exc)
        """
        self.__app.add_exception_handler(RequestValidationError, validate_interceptor)
        return self

    def add_http_exception_handler(self) -> "HuibiaoDefaultAppGenerator":
        """
        @app.exception_handler(StarletteHTTPException)
        async def http_exception_handler(req: Request, exc: StarletteHTTPException):
            return http_exception_interceptor(request=req, exc=exc)
        """
        self.__app.add_exception_handler(
            StarletteHTTPException, http_exception_interceptor
        )
        return self

    def gen_life_span(self):
        @asynccontextmanager
        async def lifespan(app: FastAPI):
            logger.info("Start app")
            # 多worker时，worker进程的日志设置
            ServiceLoggerManager.setup_log(self.service_name)
            # 启动系统指标、gc指标的定时采集写入以供多进程时的汇总
            await MetricsScheduleMonitor().run_monitor()
            # @timing_and_exception_handler的初始化
            PrometheusTimeCostMetricSetting.initialize()

            # 执行用户提供的开始事件
            await self.__life_span_start()

            yield

            # 执行用户提供的结束事件
            await self.__life_span_end()

            # 关闭线程池
            logger.info("Shut down app")

        return lifespan
