from __future__ import annotations

from datetime import UTC, datetime
from typing import Any

from app.models.financial_record import ExpenseClaim
from app.services.expense_claim_risk_stage import (
    risk_business_stage_for_claim,
    with_risk_business_stage,
)
from app.services.expense_claim_workflow_constants import (
    BUDGET_MANAGER_APPROVAL_STAGE,
    DIRECT_MANAGER_APPROVAL_STAGE,
    FINANCE_APPROVAL_STAGE,
)


class ExpenseClaimWorkflowRepairMixin:
    def _repair_duplicate_budget_approval_stages(
        self,
        claims: list[ExpenseClaim],
    ) -> list[ExpenseClaim]:
        """只修改当前事务中的对象，不在底层辅助方法里提交事务。"""

        return [
            claim
            for claim in claims
            if claim is not None and self._repair_duplicate_budget_approval_stage(claim)
        ]

    def _repair_duplicate_budget_approval_stage(self, claim: ExpenseClaim) -> bool:
        if self._is_expense_application_claim(claim):
            return False
        if str(claim.status or "").strip().lower() != "submitted":
            return False
        if str(claim.approval_stage or "").strip() != BUDGET_MANAGER_APPROVAL_STAGE:
            return False
        if self._has_duplicate_budget_stage_repair_flag(claim):
            return False

        approval_event = self._find_duplicate_budget_handoff_event(claim)
        if approval_event is None:
            return False

        claim.approval_stage = FINANCE_APPROVAL_STAGE
        claim.risk_flags_json = [
            *list(claim.risk_flags_json or []),
            self._build_duplicate_budget_stage_repair_flag(approval_event),
        ]
        return True

    def _find_duplicate_budget_handoff_event(self, claim: ExpenseClaim) -> dict[str, Any] | None:
        flags = [
            flag
            for flag in list(claim.risk_flags_json or [])
            if isinstance(flag, dict)
            and str(flag.get("source") or "").strip() == "manual_approval"
            and str(flag.get("event_type") or "").strip() == "expense_claim_approval"
            and str(flag.get("previous_approval_stage") or "").strip()
            == DIRECT_MANAGER_APPROVAL_STAGE
            and str(flag.get("next_approval_stage") or "").strip() == BUDGET_MANAGER_APPROVAL_STAGE
        ]
        for flag in reversed(flags):
            operator = self._normalize_repair_identity(flag.get("operator"))
            next_approver_name = self._normalize_repair_identity(flag.get("next_approver_name"))
            if operator and next_approver_name and operator == next_approver_name:
                return flag

            budget_manager = self._access_policy.resolve_department_budget_manager(claim)
            if budget_manager is None:
                continue
            if operator and operator == self._normalize_repair_identity(budget_manager.name):
                return flag
        return None

    def _has_duplicate_budget_stage_repair_flag(self, claim: ExpenseClaim) -> bool:
        return any(
            isinstance(flag, dict)
            and str(flag.get("source") or "").strip() == "approval_flow_repair"
            and str(flag.get("event_type") or "").strip()
            == "duplicate_budget_approval_stage_repaired"
            for flag in list(claim.risk_flags_json or [])
        )

    def _build_duplicate_budget_stage_repair_flag(
        self, approval_event: dict[str, Any]
    ) -> dict[str, Any]:
        return with_risk_business_stage(
            {
                "source": "approval_flow_repair",
                "event_type": "duplicate_budget_approval_stage_repaired",
                "severity": "info",
                "label": "重复预算审批已跳过",
                "message": "系统识别直属领导与预算管理者为同一人，已跳过重复预算审批并流转至财务审批。",
                "previous_approval_stage": BUDGET_MANAGER_APPROVAL_STAGE,
                "next_approval_stage": FINANCE_APPROVAL_STAGE,
                "related_approval_event_id": approval_event.get("approval_event_id"),
                "budget_approval_merged": True,
                "budget_approval_merged_reason": "direct_manager_is_department_budget_approver",
                "created_at": datetime.now(UTC).isoformat(),
            },
            risk_business_stage_for_claim(is_application_claim=False),
        )

    @staticmethod
    def _normalize_repair_identity(value: Any) -> str:
        return str(value or "").strip().lower()
