from decimal import Decimal
from typing import Annotated, Literal

from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg import Connection
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, model_validator
from src.__types.index import GroupBy, QuotaUsage, Tenant
from src.const import SERVICE_SUM_KEY
from src.domain.entities.quota_allocation import QuotaAllocationStatus
from src.routers.management.authorizer import get_tenant
from src.routers.management.connection import get_connection
from src.tracer import tracer
from src.usecases.quota_usage_usecase import QuotaUsageUseCase

router = APIRouter(
    prefix="/quota-usages",
    tags=["クォータ使用実績"],
)


class GetResponse(BaseModel):
    """クォータ使用実績を取得するレスポンスモデル"""

    quota_usages: list[QuotaUsage] = Field(..., description="クォータ使用実績のリスト")

    # model_validatorは定義した順に実行される

    @model_validator(mode="after")
    def ensure_sum(self, info: ValidationInfo) -> "GetResponse":  # noqa: PLR0911
        """集計結果が存在しない場合に、serviceキーとして『sum』を追加する"""
        context = info.context
        if context is None:
            return self

        if len(self.quota_usages) != 0:
            return self

        group_by: list[GroupBy] = context.get("group_by", [])
        if len(group_by) == 0:
            return self

        # group_byで指定されたキーのみを持つQuotaUsageを追加する
        if GroupBy.SERVICE in group_by and GroupBy.MONTH not in group_by and GroupBy.YEAR not in group_by:
            self.quota_usages.append(QuotaUsage(service=SERVICE_SUM_KEY, used=Decimal(0)))
            return self

        if GroupBy.SERVICE in group_by and GroupBy.MONTH in group_by and GroupBy.YEAR not in group_by:
            for month in range(1, 13):
                self.quota_usages.append(QuotaUsage(month=month, service=SERVICE_SUM_KEY, used=Decimal(0)))
            return self

        if GroupBy.SERVICE in group_by and GroupBy.YEAR in group_by and GroupBy.MONTH not in group_by:
            year: int = context.get("year", None)
            if year is not None:
                self.quota_usages.append(QuotaUsage(year=year, service=SERVICE_SUM_KEY, used=Decimal(0)))
            return self

        return self

    @model_validator(mode="after")
    def fill_missing_months(self, info: ValidationInfo) -> "GetResponse":
        """クォータ使用実績が存在しない月を0で埋める"""
        context = info.context
        if context is None:
            return self

        group_by: list[GroupBy] = context.get("group_by", [])
        if len(group_by) == 0:
            return self
        # group_byにmonthが含まれない場合は何もしない
        if GroupBy.MONTH not in group_by:
            return self

        if GroupBy.SERVICE in group_by and GroupBy.YEAR not in group_by:
            # serviceとmonthでグルーピングされている場合、serviceごとに1月から12月までのデータが存在することを保証する
            services = {usage.service for usage in self.quota_usages}
            for service in services:
                existing_months = {usage.month for usage in self.quota_usages if usage.service == service}
                for month in range(1, 13):
                    if month not in existing_months:
                        self.quota_usages.append(QuotaUsage(service=service, month=month, used=Decimal(0)))
            self.quota_usages.sort(key=lambda x: (x.service, x.month))
            return self

        return self

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "quota_usages": [
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 1,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 2,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 3,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 4,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 5,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 6,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 7,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 8,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 9,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 10,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 11,
                    },
                    {
                        "service": "bizmatch",
                        "used": 10000,
                        "month": 12,
                    },
                ],
            },
        },
    )


AllowedQuotaAllocationStatus = Literal[QuotaAllocationStatus.ACTIVE,]


@router.get(
    "",
    summary="取得",
    description="利用実績を取得します。",
    response_model_exclude_none=True,  # group_byで指定されていないフィールドは除外するため
)
@tracer.capture_method
def get(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    group_by: Annotated[list[GroupBy], Query(description="グループ化条件")],
    year: Annotated[int | None, Query(description="分析対象の年")] = None,
    quota_allocation_status: Annotated[
        list[AllowedQuotaAllocationStatus] | None,
        Query(
            description="クォータ割り当てのステータス、指定したステータスの割り当てから使用された実績のみで分析します。",
        ),
    ] = None,
) -> GetResponse:
    # クエリパラメータのデフォルト値としてから配列を利用できないため
    _quota_allocation_status = (
        [] if quota_allocation_status is None else [QuotaAllocationStatus(status) for status in quota_allocation_status]
    )

    # ユースケースが存在しないため、フィルタ条件のyear、quota_allocation_statusの同時使用は禁止とする
    if year is not None and len(_quota_allocation_status) > 0:
        raise HTTPException(
            status_code=400,
            detail="year and quota_allocation_status cannot be specified at the same time.",
        )
    # ユースケースが存在しないため、フィルタ条件のyear、quota_allocation_statusをどちらも使用しないことは禁止とする
    if year is None and len(_quota_allocation_status) == 0:
        raise HTTPException(
            status_code=400,
            detail="Either year or quota_allocation_status must be specified.",
        )

    usecase = QuotaUsageUseCase.create(tenant=tenant, connection=connection)
    quota_usages = usecase.query(
        group_by=group_by,
        year=year,
        quota_allocation_status=_quota_allocation_status,
    )
    return GetResponse.model_validate({"quota_usages": quota_usages}, context={"group_by": group_by, "year": year})
