from __future__ import annotations

from typing import Annotated

from fastapi import Header, HTTPException, status

from app.api.deps import CurrentUserContext
from app.models.financial_record import ExpenseClaim
from app.schemas.reimbursement import (
    ExpenseClaimActionResponse,
    ExpenseClaimPreReviewRead,
    ExpenseClaimRead,
    ExpenseClaimSubmitPayload,
)
from app.services.document_numbering import is_application_claim_no
from app.services.expense_claim_errors import ExpenseClaimPreReviewBlockedError
from app.services.expense_claim_pre_review_decision import (
    find_pre_review_flag,
    pre_review_public_payload,
)
from app.services.expense_claims import ExpenseClaimService

RequestIdHeader = Annotated[
    str | None,
    Header(
        alias="X-Request-ID",
        description="客户端生成的请求 ID；相同 ID 的预审重试复用同一费用事件。",
    ),
]


def pre_review_expense_claim_or_http_error(
    service: ExpenseClaimService,
    claim_id: str,
    current_user: CurrentUserContext,
    request_id: str | None,
) -> ExpenseClaimRead:
    try:
        claim = service.pre_review_claim(
            claim_id,
            current_user,
            correlation_id=request_id,
            idempotency_key=request_id,
        )
    except ValueError as error:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(error),
        ) from error

    return _claim_response_or_not_found(claim)


def submit_expense_claim_or_http_error(
    service: ExpenseClaimService,
    claim_id: str,
    current_user: CurrentUserContext,
    payload: ExpenseClaimSubmitPayload | None,
) -> ExpenseClaimRead:
    try:
        claim = service.submit_claim(
            claim_id,
            current_user,
            pre_review_id=str(getattr(payload, "pre_review_id", "") or ""),
            pre_review_input_fingerprint=str(
                getattr(payload, "input_fingerprint", "") or ""
            ),
        )
    except ExpenseClaimPreReviewBlockedError as error:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail={
                "code": error.code,
                "message": str(error),
                "review": error.review,
            },
        ) from error
    except ValueError as error:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(error),
        ) from error

    return _claim_response_or_not_found(claim)


def _claim_response_or_not_found(claim: ExpenseClaim | None) -> ExpenseClaimRead:
    if claim is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Claim not found",
        )

    response = ExpenseClaimRead.model_validate(claim)
    payload = pre_review_public_payload(find_pre_review_flag(claim))
    pre_review = (
        ExpenseClaimPreReviewRead.model_validate(payload)
        if payload is not None
        else None
    )
    return response.model_copy(update={"pre_review": pre_review})


def expense_claim_deletion_response(claim: ExpenseClaim) -> ExpenseClaimActionResponse:
    claim_no = str(claim.claim_no or "").strip()
    expense_type = str(claim.expense_type or "").strip().lower()
    document_label = (
        "申请单"
        if is_application_claim_no(claim_no) or expense_type.endswith("_application")
        else "报销单"
    )
    return ExpenseClaimActionResponse(
        message=f"{claim.claim_no} {document_label}已删除。",
        claim_id=claim.id,
        status="deleted",
    )
