from datetime import date
from decimal import Decimal
from typing import Annotated

from fastapi import APIRouter, Depends, Request, status
from fastapi.responses import Response
from psycopg import Connection
from pydantic import BaseModel, Field, model_validator
from src.__types.index import ContractProvider, Distribution
from src.domain.entities.contract_event import ContractEvent
from src.domain.entities.quota_allocation import QuotaAllocationType
from src.infrastructure.clients.cognito_idp_client import CognitoIdpClient, StubCognitoIdpClient
from src.infrastructure.clients.sns_client import SnsClient
from src.infrastructure.contract_event_notifier import (
    ContractEventNotifier,
    NullContractEventNotifier,
)
from src.infrastructure.credential_manager import CredentialManager
from src.logger import logger
from src.routers.contract.authorizer import get_contract_provider
from src.routers.contract.connection import get_connection
from src.settings import Settings
from src.tracer import tracer
from src.usecases.contract_event_usecase import ContractEventUseCase

router = APIRouter(
    prefix="/contract-events",
    tags=["契約イベント"],
)

settings = Settings()


class ContractEventsRequest(BaseModel):
    """契約イベントのリクエストモデル"""

    legal_name: str = Field(..., description="会社名")
    distribution: Distribution = Field(..., description="商流")
    customer_id: str = Field(..., description="取引先ID")
    contract_id: str = Field(..., description="見積書番号")
    type: QuotaAllocationType = Field(..., description="契約タイプ（自動更新 / 随時拡張）")
    amount: int = Field(..., gt=0, description="追加ユニット数")
    valid_from_date: date = Field(..., description="契約開始日")
    valid_to_date: date = Field(..., description="契約終了日")

    @model_validator(mode="after")
    def validate_dates(self) -> "ContractEventsRequest":
        if self.valid_from_date >= self.valid_to_date:
            raise ValueError("valid_from_date must be before valid_to_date")
        return self


@router.post(
    "",
    summary="ユニット契約イベント",
    description="契約情報に基づき、テナント発行（未発行の場合）・クォータ割り当てを行います。",
)
@tracer.capture_method
def post(
    http_request: Request,
    contract_provider: Annotated[ContractProvider, Depends(get_contract_provider)],
    connection: Annotated[Connection, Depends(get_connection)],
    body: ContractEventsRequest,
) -> Response:
    logger.info("request received", request=body.model_dump())
    logger.info(f"contract_provider_id received: {contract_provider.id}")

    if not settings.user_pool_id:
        raise ValueError("user_pool_id is required in settings")
    if not settings.stage:
        raise ValueError("stage is required in settings")
    if not settings.aws_region:
        raise ValueError("aws_region is required in settings")

    lambda_context = http_request.scope.get("aws.context")
    log_group_name = getattr(lambda_context, "log_group_name", "")
    log_stream_name = getattr(lambda_context, "log_stream_name", "")

    # sns_topic_arnが設定されている環境（stg・prod）のみ、契約イベントを通知する
    notifier = (
        ContractEventNotifier(
            sns_client=SnsClient(topic_arn=settings.sns_topic_arn),
            stage=settings.stage,
            region=settings.aws_region,
            user_pool_id=settings.user_pool_id,
            log_group_name=log_group_name,
            log_stream_name=log_stream_name,
        )
        if settings.sns_topic_arn
        else NullContractEventNotifier()
    )
    cognito_client = (
        StubCognitoIdpClient(user_pool_id=settings.user_pool_id or "")
        if settings.stage == "local"
        else CognitoIdpClient(user_pool_id=settings.user_pool_id)
    )
    usecase = ContractEventUseCase.create(
        connection=connection,
        credential_manager=CredentialManager(
            client=cognito_client,
        ),
        notifier=notifier,
    )
    usecase.execute(
        contract_provider=contract_provider,
        contract_event=ContractEvent(**body.model_dump(exclude={"amount"}), amount=Decimal(body.amount)),
    )
    return Response(status_code=status.HTTP_200_OK)
