from __future__ import annotations

from datetime import datetime
from typing import Annotated, Literal

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext, get_current_user, get_db
from app.schemas.savings import (
    SavingsOpportunityActionCreate,
    SavingsOpportunityListRead,
    SavingsOpportunityMutationRead,
    SavingsOpportunityRead,
    SavingsRealizationActionCreate,
    SavingsRealizationCreate,
    SavingsRealizationMutationRead,
)
from app.schemas.savings_insights import (
    SavingsBaselineGenerateRequest,
    SavingsBaselineGenerationRead,
    SavingsInsightAnalysisRead,
    SavingsInsightAnalyzeRequest,
)
from app.services.savings_access_policy import SavingsPermissionError
from app.services.savings_actions import SavingsActionService, SavingsTransitionError
from app.services.savings_baseline_generation import SavingsBaselineGenerationService
from app.services.savings_insight_analysis import SavingsInsightAnalysisService
from app.services.savings_protocol import (
    SavingsIdempotencyConflictError,
    SavingsVersionConflictError,
)
from app.services.savings_query import SavingsQueryService
from app.services.savings_realization import (
    SavingsRealizationError,
    SavingsRealizationService,
)

router = APIRouter(prefix="/savings")
DbSession = Annotated[Session, Depends(get_db)]
CurrentUser = Annotated[CurrentUserContext, Depends(get_current_user)]


@router.post(
    "/baselines/generate",
    response_model=SavingsBaselineGenerationRead,
    summary="冻结租户费用历史基线",
)
def generate_savings_baselines(
    payload: SavingsBaselineGenerateRequest,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsBaselineGenerationRead:
    try:
        return SavingsBaselineGenerationService(db).generate(payload, current_user)
    except Exception as error:
        raise _mutation_http_error(error) from error


@router.post(
    "/insights/analyze",
    response_model=SavingsInsightAnalysisRead,
    summary="分析费用节省候选信号",
)
def analyze_savings_insights(
    payload: SavingsInsightAnalyzeRequest,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsInsightAnalysisRead:
    try:
        return SavingsInsightAnalysisService(db).analyze(payload, current_user)
    except Exception as error:
        raise _mutation_http_error(error) from error


@router.get(
    "/opportunities",
    response_model=SavingsOpportunityListRead,
    summary="查询节省机会台账",
)
def list_savings_opportunities(
    db: DbSession,
    current_user: CurrentUser,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
    status_value: Annotated[str | None, Query(alias="status", max_length=24)] = None,
    source_type: Annotated[str | None, Query(max_length=50)] = None,
    value_kind: Annotated[Literal["cash", "labor"] | None, Query()] = None,
    department_id: Annotated[str | None, Query(max_length=160)] = None,
    project_code: Annotated[str | None, Query(max_length=160)] = None,
    expense_type: Annotated[str | None, Query(max_length=80)] = None,
    supplier_id: Annotated[str | None, Query(max_length=160)] = None,
    city: Annotated[str | None, Query(max_length=160)] = None,
    owner_id: Annotated[str | None, Query(max_length=120)] = None,
    claim_id: Annotated[str | None, Query(max_length=36)] = None,
    created_from: datetime | None = None,
    created_to: datetime | None = None,
    sort: Annotated[
        Literal["created_desc", "created_asc", "due_asc", "estimated_desc"],
        Query(),
    ] = "created_desc",
) -> SavingsOpportunityListRead:
    return SavingsQueryService(db).list_opportunities(
        current_user,
        page=page,
        page_size=page_size,
        status=status_value,
        source_type=source_type,
        value_kind=value_kind,
        department_id=department_id,
        project_code=project_code,
        expense_type=expense_type,
        supplier_id=supplier_id,
        city=city,
        owner_id=owner_id,
        claim_id=claim_id,
        created_from=created_from,
        created_to=created_to,
        sort=sort,
    )


@router.get(
    "/opportunities/{opportunity_id}",
    response_model=SavingsOpportunityRead,
    summary="读取节省机会证据链",
)
def get_savings_opportunity(
    opportunity_id: str,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsOpportunityRead:
    opportunity = SavingsQueryService(db).get_opportunity(opportunity_id, current_user)
    if opportunity is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="节省机会不存在。")
    return opportunity


@router.post(
    "/opportunities/{opportunity_id}/actions",
    response_model=SavingsOpportunityMutationRead,
    summary="执行节省机会状态动作",
)
def execute_savings_opportunity_action(
    opportunity_id: str,
    payload: SavingsOpportunityActionCreate,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsOpportunityMutationRead:
    try:
        return (
            SavingsActionService(db)
            .execute(
                opportunity_id,
                payload,
                current_user,
            )
            .response
        )
    except Exception as error:
        raise _mutation_http_error(error) from error


@router.post(
    "/opportunities/{opportunity_id}/realizations",
    response_model=SavingsRealizationMutationRead,
    summary="记录节省机会实际结果",
)
def record_savings_realization(
    opportunity_id: str,
    payload: SavingsRealizationCreate,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsRealizationMutationRead:
    try:
        return (
            SavingsRealizationService(db)
            .record(
                opportunity_id,
                payload,
                current_user,
            )
            .response
        )
    except Exception as error:
        raise _mutation_http_error(error) from error


@router.post(
    "/realizations/{realization_id}/actions",
    response_model=SavingsRealizationMutationRead,
    summary="确认、拒绝或冲回实际节省",
)
def execute_savings_realization_action(
    realization_id: str,
    payload: SavingsRealizationActionCreate,
    db: DbSession,
    current_user: CurrentUser,
) -> SavingsRealizationMutationRead:
    try:
        return (
            SavingsRealizationService(db)
            .execute_action(
                realization_id,
                payload,
                current_user,
            )
            .response
        )
    except Exception as error:
        raise _mutation_http_error(error) from error


def _mutation_http_error(error: Exception) -> HTTPException:
    if isinstance(error, LookupError):
        return HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(error))
    if isinstance(error, SavingsPermissionError):
        return HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(error))
    if isinstance(error, SavingsVersionConflictError):
        return HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail={"message": str(error), "current_version": error.current_version},
        )
    if isinstance(
        error,
        (
            SavingsIdempotencyConflictError,
            SavingsTransitionError,
            SavingsRealizationError,
        ),
    ):
        return HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(error))
    if isinstance(error, (ValueError, PermissionError)):
        return HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(error))
    raise error
