import logging
from contextlib import asynccontextmanager
from typing import Any, Callable, Coroutine, Dict, List, Optional

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError, StarletteHTTPException
from starlette.middleware import Middleware

from algo_backend.exception import ApiErrorCodeManage
from algo_backend.intercept import (
    BasicExceptionInterceptor,
    HTTPExceptionInterceptor,
    ValidateExceptionInterceptor,
)
from algo_backend.log import BasicLogStarter
from algo_backend.metrics import (
    PrometheusContext,
    PrometheusTimeCostMetricSetting,
)
from algo_backend.metrics.collector import MetricsScheduleMonitor
from algo_backend.middleware import default_cors_middleware

from .event_list import EventList

logger = logging.getLogger(__name__)


class DefaultAlgoAppGenerator:
    """
    FastApi的App初始化，作用于每个fastapi的worker
    """

    def __init__(
        self,
        *,
        service_name: str,
        app_error_code_prefix: Optional[int] = None,
        custom_start_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
        custom_end_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
        log_starter: Optional[BasicLogStarter] = None,
        intercept_dict: Optional[Dict[Exception, BasicExceptionInterceptor]] = None,
        middlewares: List[Middleware] = (default_cors_middleware,),
        api_time_cost_buckets: Optional[List[int]] = None,
        client_time_cost_buckets: Optional[List[int]] = None,
    ):
        """
        :param service_name: 服务名称，必填
        :param app_error_code_prefix: 错误码前缀，两位数字，如果指定则错误码为8位，如果不填则错误码是6位
        :param custom_start_event: 用户自定义的app时的触发事件列表
        :param custom_end_event: 用户自定义的app结束的触发事件列表
        :param log_starter: 日志启动器
        :param intercept_dict: 拦截器字典，key为异常类型，value为拦截器实例
        :param middlewares: 中间件列表
        :param api_time_cost_buckets: 接口耗时统计的桶,None表示使用默认的,空列表表示不启用这个指标
        :param client_time_cost_buckets: 客户端耗时统计的桶,None表示使用默认的,空列表表示不启用这个指标
        """
        # 注册8位错误码前缀
        ApiErrorCodeManage.set_error_code_prefix_env(app_error_code_prefix)
        self.service_name = service_name

        self.__app: Optional[FastAPI] = FastAPI()

        self.start_event_list = EventList()
        self.end_event_list = EventList()

        self.log_starter = log_starter or BasicLogStarter(service_name=service_name)

        self.intercept_dict = {
            RequestValidationError: ValidateExceptionInterceptor(),
            StarletteHTTPException: HTTPExceptionInterceptor(),
        }
        self.intercept_dict.update(intercept_dict or {})

        self.middleware_list = list(middlewares)

        self.time_cost_metrics_initializer = (
            lambda: PrometheusTimeCostMetricSetting.initialize(
                api_metrics_buckets=api_time_cost_buckets,
                client_metrics_buckets=client_time_cost_buckets,
            )
        )

        self.init_start_event(custom_start_event)
        self.init_end_event(custom_end_event)

    def init_start_event(self, custom_event):
        """
        初始化开始事件
        """
        self.start_event_list.add(self.log_starter.setup_log())
        # 启动系统指标、gc指标的定时采集写入以供多进程时的汇总
        self.start_event_list.add(MetricsScheduleMonitor().run_monitor)
        # 接口计时器和客户端解释器的初始化
        self.start_event_list.add(self.time_cost_metrics_initializer)
        # 插入自定义的开始事件
        for event in custom_event or []:
            self.start_event_list.add(event)

    def init_end_event(self, custom_event):
        """
        初始化结束事件
        """
        # 插入自定义事件
        for event in custom_event or []:
            self.end_event_list.add(event)

    def add_middleware(self, middleware: Middleware) -> "DefaultAlgoAppGenerator":
        """
        添加中间件
        """
        self.middleware_list.append(middleware)
        return self

    def set_log_stater(self, log_starter: BasicLogStarter) -> "DefaultAlgoAppGenerator":
        self.log_starter = log_starter
        return self

    def use_log_loguru(self):
        """
        使用loguru日志框架
        """
        from algo_backend.log.loguru import LoguruStarter

        return self.set_log_stater(LoguruStarter(service_name=self.service_name))

    def use_log_nblog(self):
        """
        使用nblog日志框架，待实现
        """
        ...

    def generate(self):
        self.init_app().add_prometheus_endpoint().add_interceptor()
        return self.app

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

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

    def add_prometheus_endpoint(self):
        PrometheusContext.mount_prometheus_endpoint(self.__app)
        return self

    def add_interceptor(self):
        for exc, interceptor in self.intercept_dict.items():
            self.__app.add_exception_handler(exc, interceptor.intercept)
        return self

    def gen_life_span(self):
        @asynccontextmanager
        async def lifespan(app: FastAPI):
            logger.info("ExecuteStartEvent")
            await self.start_event_list.execute()
            logger.info("Start App")

            yield

            logger.info("ExecuteStopEvent")
            await self.end_event_list.execute()

            logger.info("Goodbye")

        return lifespan
