from __future__ import annotations

import uuid
from datetime import UTC, datetime
from decimal import Decimal, InvalidOperation
from typing import Any

from app.api.deps import CurrentUserContext
from app.models.approval_action import ApprovalActionLedger
from app.models.financial_record import ExpenseClaim
from app.services.budget import BudgetService
from app.services.expense_claim_risk_gate import ExpenseClaimRiskGate
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 (
    APPLICATION_LINK_STATUS_STAGE,
    BUDGET_MANAGER_APPROVAL_STAGE,
    DIRECT_MANAGER_APPROVAL_STAGE,
    FINANCE_APPROVAL_STAGE,
    PAYMENT_PAID_STAGE,
    PAYMENT_PAID_STATUS,
    PAYMENT_PENDING_STAGE,
    PAYMENT_PENDING_STATUS,
)
from app.services.savings_payment_reversal import SavingsPaymentReversalService
from app.services.savings_realization import SavingsRealizationService


class ExpenseClaimApprovalFlowMixin:
    def approve_claim(
        self,
        claim_id: str,
        current_user: CurrentUserContext,
        *,
        opinion: str | None = None,
        request_id: str | None = None,
        expected_status: str | None = None,
        expected_approval_stage: str | None = None,
        task_id: str | None = None,
        expected_task_version: int | None = None,
    ):
        normalized_opinion = str(opinion or "").strip()
        return self._execute_claim_action(
            action="approve",
            claim_id=claim_id,
            current_user=current_user,
            request_id=request_id,
            expected_status=expected_status,
            expected_approval_stage=expected_approval_stage,
            payload={
                "opinion": normalized_opinion,
                "task_id": str(task_id or "").strip() or None,
                "expected_task_version": expected_task_version,
            },
            executor=lambda claim, ledger, normalized_request_id: self._approve_claim_once(
                claim,
                current_user,
                opinion=normalized_opinion,
                ledger=ledger,
                request_id=normalized_request_id,
            ),
            task_id=task_id,
            expected_task_version=expected_task_version,
        )

    def _approve_claim_once(
        self,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
        *,
        opinion: str,
        ledger: ApprovalActionLedger,
        request_id: str,
    ) -> ExpenseClaim:

        normalized_status = str(claim.status or "").strip().lower()
        if normalized_status != "submitted":
            raise ValueError("只有审批中的单据可以审批通过。")

        previous_stage = str(claim.approval_stage or "").strip()
        is_application_claim = self._is_expense_application_claim(claim)
        business_stage = risk_business_stage_for_claim(is_application_claim=is_application_claim)
        next_budget_manager = None
        merged_budget_approval = False
        route_decision_flag: dict[str, Any] | None = None
        generated_draft = None
        if previous_stage == DIRECT_MANAGER_APPROVAL_STAGE:
            if not getattr(claim, "_approval_task_authorized", False) and not (
                self._access_policy.can_approve_claim(current_user, claim)
            ):
                raise ValueError("只有当前直属领导审批人可以审批通过该单据。")
            self._ensure_claim_has_no_blocking_risk(claim, current_user)
            approval_source = "manual_approval"
            event_type = (
                "expense_application_approval" if is_application_claim else "expense_claim_approval"
            )
            label = "领导审批通过"
            route_decision_flag = self._build_approval_route_decision(
                claim,
                is_application_claim=is_application_claim,
            )
            requires_budget_review = bool(route_decision_flag.get("requires_budget_review"))
            if is_application_claim:
                merged_budget_approval = (
                    requires_budget_review
                    and self._access_policy.is_department_p8_budget_monitor(current_user, claim)
                )
                if merged_budget_approval:
                    label = "领导及预算审核通过"
                    next_status = "approved"
                    next_stage = APPLICATION_LINK_STATUS_STAGE
                    default_message = (
                        "{operator} 已完成直属领导和预算管理者审核，申请流程完成并生成报销草稿。"
                    )
                elif requires_budget_review:
                    next_budget_manager = self._access_policy.resolve_department_budget_manager(
                        claim
                    )
                    if next_budget_manager is None:
                        raise ValueError(
                            "未找到同部门 P8 预算审批人，无法流转预算审批。请先配置预算审批人。"
                        )
                    next_status = "submitted"
                    next_stage = BUDGET_MANAGER_APPROVAL_STAGE
                    default_message = (
                        "{operator} 已确认直属领导审核，因预算或风险关注项流转至预算管理者审批。"
                    )
                else:
                    next_status = "approved"
                    next_stage = APPLICATION_LINK_STATUS_STAGE
                    default_message = (
                        "{operator} 已确认直属领导审核，系统判断预算充足且无风险，"
                        "申请流程完成并生成报销草稿。"
                    )
            else:
                merged_budget_approval = (
                    requires_budget_review
                    and self._access_policy.is_department_p8_budget_monitor(current_user, claim)
                )
                if merged_budget_approval:
                    label = "领导及预算审核通过"
                    next_status = "submitted"
                    next_stage = FINANCE_APPROVAL_STAGE
                    default_message = (
                        "{operator} 已完成直属领导和预算管理者审核，流转至{next_stage}。"
                    )
                elif requires_budget_review:
                    next_budget_manager = self._access_policy.resolve_department_budget_manager(
                        claim
                    )
                    if next_budget_manager is None:
                        raise ValueError(
                            "未找到同部门 P8 预算审批人，无法流转预算审批。请先配置预算审批人。"
                        )
                    next_status = "submitted"
                    next_stage = BUDGET_MANAGER_APPROVAL_STAGE
                    default_message = (
                        "{operator} 已审批通过，因预算或风险关注项流转至预算管理者审批。"
                    )
                else:
                    next_status = "submitted"
                    next_stage = FINANCE_APPROVAL_STAGE
                    default_message = (
                        "{operator} 已审批通过，系统判断预算充足且无风险，流转至{next_stage}。"
                    )
        elif previous_stage == BUDGET_MANAGER_APPROVAL_STAGE:
            if not getattr(claim, "_approval_task_authorized", False) and not (
                self._access_policy.can_approve_claim(current_user, claim)
            ):
                raise ValueError("只有当前预算管理者可以审批通过该单据。")
            self._ensure_claim_has_no_blocking_risk(claim, current_user)
            approval_source = "budget_approval"
            event_type = (
                "expense_application_budget_approval"
                if is_application_claim
                else "expense_claim_budget_approval"
            )
            label = "预算管理者审核通过"
            if is_application_claim:
                next_status = "approved"
                next_stage = APPLICATION_LINK_STATUS_STAGE
                default_message = "{operator} 已完成预算审核，申请流程完成并生成报销草稿。"
            else:
                next_status = "submitted"
                next_stage = FINANCE_APPROVAL_STAGE
                default_message = "{operator} 已完成预算审核，流转至{next_stage}。"
        elif previous_stage == FINANCE_APPROVAL_STAGE:
            if is_application_claim:
                raise ValueError("费用申请需先完成预算管理者审批。")
            if not getattr(claim, "_approval_task_authorized", False) and not (
                self._access_policy.can_approve_claim(current_user, claim)
            ):
                raise ValueError("只有财务人员可以完成财务终审。")
            self._ensure_claim_has_no_blocking_risk(claim, current_user)
            approval_source = "finance_approval"
            event_type = "expense_claim_finance_approval"
            label = "财务审核通过"
            next_status = PAYMENT_PENDING_STATUS
            next_stage = PAYMENT_PENDING_STAGE
            default_message = "{operator} 已完成财务审核，进入待付款。"
        else:
            raise ValueError("当前节点不支持审批通过。")

        approval_opinion = str(opinion or "").strip()
        if (
            previous_stage == BUDGET_MANAGER_APPROVAL_STAGE
            and self._budget_approval_opinion_required(claim)
            and not approval_opinion
        ):
            raise ValueError("预算已超过警戒值，预算管理者需填写审批意见后才能通过。")
        if (
            previous_stage == DIRECT_MANAGER_APPROVAL_STAGE
            and merged_budget_approval
            and self._budget_approval_opinion_required(claim)
            and not approval_opinion
        ):
            raise ValueError("预算已超过警戒值，预算管理者需填写审批意见后才能通过。")
        if (
            previous_stage in {DIRECT_MANAGER_APPROVAL_STAGE, BUDGET_MANAGER_APPROVAL_STAGE}
            and not approval_opinion
        ):
            approval_opinion = "同意"

        before_json = self._serialize_claim(claim)
        operator = self._access_policy.resolve_current_user_display_name(current_user)
        budget_flags: list[dict[str, Any]] = []
        if approval_source == "finance_approval" and not is_application_claim:
            consumed_budget_flag = self._consume_budget_for_finance_approval(claim, current_user)
            if consumed_budget_flag is not None:
                budget_flags.append(consumed_budget_flag)
        approval_flag = with_risk_business_stage(
            {
                "source": approval_source,
                "event_type": event_type,
                "approval_event_id": str(uuid.uuid4()),
                "severity": "info",
                "label": label,
                "message": approval_opinion
                or default_message.format(operator=operator, next_stage=next_stage),
                "opinion": approval_opinion,
                "operator": operator,
                "operator_username": current_user.username,
                "operator_role_codes": [
                    str(item).strip().lower()
                    for item in current_user.role_codes
                    if str(item).strip()
                ],
                "previous_status": str(claim.status or "").strip(),
                "previous_approval_stage": previous_stage,
                "next_status": next_status,
                "next_approval_stage": next_stage,
                "created_at": datetime.now(UTC).isoformat(),
            },
            business_stage,
        )
        if merged_budget_approval:
            approval_flag.update(
                {
                    "budget_approval_merged": True,
                    "budget_approval_merged_reason": "direct_manager_is_department_budget_approver",
                }
            )
        if next_budget_manager is not None:
            approval_flag.update(
                {
                    "next_approver_name": str(next_budget_manager.name or "").strip(),
                    "next_approver_employee_id": next_budget_manager.id,
                    "next_approver_grade": str(next_budget_manager.grade or "").strip(),
                    "next_approver_role_code": (
                        self._access_policy.resolve_budget_approval_role_code(
                            next_budget_manager,
                        )
                    ),
                }
            )
        if route_decision_flag is not None:
            approval_flag["route_decision"] = {
                "requires_budget_review": route_decision_flag.get("requires_budget_review"),
                "route": route_decision_flag.get("route"),
                "reasons": route_decision_flag.get("reasons", []),
                "budget_result": route_decision_flag.get("budget_result", {}),
                "current_risk_count": route_decision_flag.get("current_risk_count", 0),
                "historical_risk_count": route_decision_flag.get("historical_risk_count", 0),
            }

        claim.status = next_status
        claim.approval_stage = next_stage
        if claim.submitted_at is None:
            claim.submitted_at = datetime.now(UTC)
        if is_application_claim and next_stage == APPLICATION_LINK_STATUS_STAGE:
            if previous_stage == BUDGET_MANAGER_APPROVAL_STAGE:
                approval_flag["leader_opinion"] = self._resolve_latest_approval_opinion(
                    claim,
                    source="manual_approval",
                )
                approval_flag["budget_opinion"] = approval_opinion
            elif merged_budget_approval:
                approval_flag["leader_opinion"] = approval_opinion
                approval_flag["budget_opinion"] = approval_opinion
            elif (
                previous_stage == DIRECT_MANAGER_APPROVAL_STAGE
                and route_decision_flag is not None
                and not route_decision_flag.get("requires_budget_review")
            ):
                approval_flag["leader_opinion"] = approval_opinion
                approval_flag["budget_opinion"] = "系统动态路由跳过预算复核"
            generated_draft = self._create_reimbursement_draft_from_application(
                application_claim=claim,
                approval_flag=approval_flag,
                operator=operator,
            )
            transferred_budget_flag = self._transfer_application_budget_to_reimbursement(
                application_claim=claim,
                draft_claim=generated_draft,
                current_user=current_user,
            )
            if transferred_budget_flag is not None:
                budget_flags.append(transferred_budget_flag)
                generated_draft.risk_flags_json = self._append_budget_flags(
                    generated_draft.risk_flags_json,
                    transferred_budget_flag,
                    business_stage="reimbursement",
                )
        approval_flags: list[Any] = list(claim.risk_flags_json or [])
        if route_decision_flag is not None:
            approval_flags.append(route_decision_flag)
        approval_flags.append(approval_flag)
        claim.risk_flags_json = self._append_budget_flags(
            approval_flags,
            budget_flags,
            business_stage=business_stage,
        )

        correlation_id = str(approval_flag.get("approval_event_id") or uuid.uuid4())
        structured_event_type = "approval_stage_completed"
        if is_application_claim and next_status == "approved":
            structured_event_type = "application_approved"
        elif not is_application_claim and next_status == PAYMENT_PENDING_STATUS:
            structured_event_type = "claim_approved"
        expense_case, _event = self._expense_cases.record_claim_event(
            claim,
            event_type=structured_event_type,
            actor_id=current_user.username,
            tenant_id=getattr(current_user, "tenant_id", None),
            correlation_id=correlation_id,
            idempotency_key=correlation_id,
            previous_status=str(before_json.get("status") or ""),
            previous_approval_stage=previous_stage,
            extra_payload={
                "workflow_event_type": event_type,
                "opinion": approval_opinion,
                "route_requires_budget_review": bool(
                    route_decision_flag and route_decision_flag.get("requires_budget_review")
                ),
            },
        )
        claim._approval_task_business_event = _event
        if generated_draft is not None:
            self._expense_cases.record_claim_event(
                generated_draft,
                event_type="reimbursement_draft_generated",
                actor_id="system",
                tenant_id=getattr(current_user, "tenant_id", None),
                correlation_id=correlation_id,
                idempotency_key=correlation_id,
                causation_id=_event.id,
                previous_status="",
                previous_approval_stage="",
                extra_payload={
                    "application_claim_id": claim.id,
                    "application_claim_no": claim.claim_no,
                },
                expense_case=expense_case,
                relation_type="generated_reimbursement",
                update_case_state=False,
            )

        self.audit_service.log_action(
            actor=operator,
            action="expense_claim.approve",
            resource_type="expense_claim",
            resource_id=claim.id,
            before_json=before_json,
            after_json=self._serialize_claim(claim),
            request_id=request_id,
            commit=False,
        )

        return claim

    def _ensure_claim_has_no_blocking_risk(
        self,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
    ) -> None:
        ExpenseClaimRiskGate(self.db).ensure_approvable(
            claim,
            tenant_id=current_user.tenant_id,
        )

    def mark_claim_paid(
        self,
        claim_id: str,
        current_user: CurrentUserContext,
        *,
        request_id: str | None = None,
        expected_status: str | None = None,
        expected_approval_stage: str | None = None,
    ):
        return self._execute_claim_action(
            action="pay",
            claim_id=claim_id,
            current_user=current_user,
            request_id=request_id,
            expected_status=expected_status,
            expected_approval_stage=expected_approval_stage,
            payload={},
            executor=lambda claim, ledger, normalized_request_id: self._mark_claim_paid_once(
                claim,
                current_user,
                ledger=ledger,
                request_id=normalized_request_id,
            ),
        )

    def _mark_claim_paid_once(
        self,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
        *,
        ledger: ApprovalActionLedger | None,
        request_id: str,
        payment_context: dict[str, Any] | None = None,
    ) -> ExpenseClaim:

        normalized_status = str(claim.status or "").strip().lower()
        if normalized_status == PAYMENT_PAID_STATUS:
            raise ValueError("该报销单已付款，无需重复确认。")
        if normalized_status != PAYMENT_PENDING_STATUS:
            raise ValueError("只有待付款状态的报销单可以确认已付款。")
        if not self._access_policy.can_mark_claim_paid(current_user, claim):
            raise ValueError("只有财务人员或高级财务人员可以确认付款，且不能处理本人单据。")

        before_json = self._serialize_claim(claim)
        operator = self._access_policy.resolve_current_user_display_name(current_user)
        previous_stage = str(claim.approval_stage or "").strip()
        external_context = dict(payment_context or {})
        payment_event_id = str(
            external_context.get("connector_event_id") or uuid.uuid4()
        ).strip()
        is_external_payment = bool(external_context)
        payment_flag = with_risk_business_stage(
            {
                "source": "external_payment" if is_external_payment else "payment",
                "event_type": (
                    "expense_claim_external_payment_settled"
                    if is_external_payment
                    else "expense_claim_payment_completed"
                ),
                "payment_event_id": payment_event_id,
                "severity": "info",
                "label": "外部结算完成" if is_external_payment else "付款完成",
                "message": (
                    f"已通过 {external_context.get('provider')} 的可信回执完成付款匹配，"
                    "报销单进入已付款。"
                    if is_external_payment
                    else f"{operator} 已确认付款，报销单进入已付款。"
                ),
                "operator": operator,
                "operator_username": current_user.username,
                "operator_role_codes": [
                    str(item).strip().lower()
                    for item in current_user.role_codes
                    if str(item).strip()
                ],
                "previous_status": str(claim.status or "").strip(),
                "previous_approval_stage": previous_stage,
                "next_status": PAYMENT_PAID_STATUS,
                "next_approval_stage": PAYMENT_PAID_STAGE,
                "created_at": datetime.now(UTC).isoformat(),
                **external_context,
            },
            "reimbursement",
        )

        archived_applications = self._archive_linked_applications_after_reimbursement_paid(
            reimbursement_claim=claim,
            payment_flag=payment_flag,
            operator=operator,
            current_user=current_user,
        )
        if archived_applications:
            payment_flag["archived_application_claims"] = archived_applications

        claim.status = PAYMENT_PAID_STATUS
        claim.approval_stage = PAYMENT_PAID_STAGE
        claim.risk_flags_json = [*list(claim.risk_flags_json or []), payment_flag]

        payment_correlation_id = str(
            external_context.get("correlation_id")
            or payment_flag.get("payment_event_id")
            or uuid.uuid4()
        )
        expense_case, payment_event = self._expense_cases.record_claim_event(
            claim,
            event_type="payment_completed",
            actor_id=current_user.username,
            tenant_id=getattr(current_user, "tenant_id", None),
            correlation_id=payment_correlation_id,
            idempotency_key=payment_correlation_id,
            previous_status=str(before_json.get("status") or ""),
            previous_approval_stage=previous_stage,
            extra_payload={
                "archived_applications": archived_applications,
                **(
                    {"external_payment_evidence": external_context}
                    if external_context
                    else {}
                ),
            },
        )
        SavingsRealizationService(self.db).realize_paid_claim(
            claim,
            payment_event,
            current_user,
        )
        for archived_application in archived_applications:
            application_claim = self.db.get(
                ExpenseClaim,
                str(archived_application.get("application_claim_id") or ""),
            )
            if application_claim is None:
                raise RuntimeError("付款关联的申请单已不存在，无法记录归档事件。")
            archive_event_id = str(archived_application.get("archive_event_id") or "").strip()
            self._expense_cases.record_claim_event(
                application_claim,
                event_type="application_archived",
                actor_id=current_user.username,
                tenant_id=getattr(current_user, "tenant_id", None),
                correlation_id=payment_correlation_id,
                idempotency_key=archive_event_id,
                causation_id=payment_event.id,
                previous_status=str(archived_application.get("previous_status") or ""),
                previous_approval_stage=str(
                    archived_application.get("previous_approval_stage") or ""
                ),
                extra_payload={
                    "reimbursement_claim_id": claim.id,
                    "reimbursement_claim_no": claim.claim_no,
                },
                expense_case=expense_case,
                relation_type="application",
                update_case_state=False,
            )

        self.audit_service.log_action(
            actor=operator,
            action="expense_claim.mark_paid",
            resource_type="expense_claim",
            resource_id=claim.id,
            before_json=before_json,
            after_json=self._serialize_claim(claim),
            request_id=request_id,
            commit=False,
        )

        return claim

    def mark_claim_paid_from_connector(
        self,
        claim: ExpenseClaim,
        *,
        tenant_id: str,
        provider: str,
        connector_event_id: str,
        external_event_id: str,
        content_hash: str,
        verification_level: str,
        evidence_classification: str,
        external_reference_tail: str,
        correlation_id: str,
    ) -> ExpenseClaim:
        """可信连接器专用入口；调用方必须已完成签名和精确对账。"""

        connector_user = CurrentUserContext(
            username=f"financial-connector:{provider}",
            name=f"财务连接器 {provider}",
            role_codes=["finance"],
            is_admin=False,
            tenant_id=tenant_id,
        )
        return self._mark_claim_paid_once(
            claim,
            connector_user,
            ledger=None,
            request_id=f"connector:{external_event_id}"[:120],
            payment_context={
                "provider": provider,
                "connector_event_id": connector_event_id,
                "external_event_id": external_event_id,
                "content_hash": content_hash,
                "verification_level": verification_level,
                "evidence_classification": evidence_classification,
                "external_reference_tail": external_reference_tail,
                "correlation_id": correlation_id,
            },
        )

    def reopen_claim_after_connector_reversal(
        self,
        claim: ExpenseClaim,
        *,
        tenant_id: str,
        provider: str,
        connector_event_id: str,
        external_event_id: str,
        origin_connector_event_id: str,
        content_hash: str,
        verification_level: str,
        evidence_classification: str,
        correlation_id: str,
    ) -> ExpenseClaim:
        """退款/冲回以追加事实重开付款，不删除原付款或申请归档事实。"""

        if str(claim.status or "").strip().lower() != PAYMENT_PAID_STATUS:
            raise ValueError("只有已付款报销单可以由可信退款/冲回事件重开。")
        before_json = self._serialize_claim(claim)
        previous_stage = str(claim.approval_stage or "").strip()
        reversal_flag = with_risk_business_stage(
            {
                "source": "external_payment_reversal",
                "event_type": "expense_claim_external_payment_reversed",
                "reversal_event_id": connector_event_id,
                "origin_payment_event_id": origin_connector_event_id,
                "external_event_id": external_event_id,
                "provider": provider,
                "content_hash": content_hash,
                "severity": "warning",
                "label": "外部付款已冲回",
                "message": "可信退款/冲回回执已到达，报销单重开为待付款。",
                "previous_status": str(claim.status or "").strip(),
                "previous_approval_stage": previous_stage,
                "next_status": PAYMENT_PENDING_STATUS,
                "next_approval_stage": PAYMENT_PENDING_STAGE,
                "created_at": datetime.now(UTC).isoformat(),
            },
            "reimbursement",
        )
        claim.status = PAYMENT_PENDING_STATUS
        claim.approval_stage = PAYMENT_PENDING_STAGE
        claim.risk_flags_json = [*list(claim.risk_flags_json or []), reversal_flag]
        _, reversal_event = self._expense_cases.record_claim_event(
            claim,
            event_type="payment_reversed",
            actor_id=f"financial-connector:{provider}",
            tenant_id=tenant_id,
            correlation_id=correlation_id,
            idempotency_key=connector_event_id,
            causation_id=origin_connector_event_id,
            previous_status=str(before_json.get("status") or ""),
            previous_approval_stage=previous_stage,
            extra_payload={
                "connector_event_id": connector_event_id,
                "external_event_id": external_event_id,
                "origin_connector_event_id": origin_connector_event_id,
                "content_hash": content_hash,
                "provider": provider,
                "verification_level": verification_level,
                "evidence_classification": evidence_classification,
                "savings_reversal_candidate": True,
            },
        )
        connector_user = CurrentUserContext(
            username=f"financial-connector:{provider}",
            name=f"财务连接器 {provider}",
            role_codes=["finance"],
            is_admin=False,
            tenant_id=tenant_id,
        )
        SavingsPaymentReversalService(self.db).reconcile(
            claim,
            reversal_event,
            connector_user,
        )
        self.audit_service.log_action(
            actor=f"financial-connector:{provider}",
            action="expense_claim.reopen_after_payment_reversal",
            resource_type="expense_claim",
            resource_id=claim.id,
            before_json=before_json,
            after_json=self._serialize_claim(claim),
            request_id=f"connector:{external_event_id}"[:120],
            commit=False,
        )
        return claim

    @staticmethod
    def _resolve_latest_approval_opinion(claim, *, source: str) -> str:
        for flag in reversed(list(claim.risk_flags_json or [])):
            if not isinstance(flag, dict):
                continue
            if str(flag.get("source") or "").strip() != source:
                continue
            opinion = str(flag.get("opinion") or flag.get("message") or "").strip()
            if opinion:
                return opinion
        return ""

    def _budget_approval_opinion_required(self, claim) -> bool:
        budget_result = BudgetService(self.db).analyze_claim_budget(claim)
        metrics = (
            budget_result.get("metrics") if isinstance(budget_result.get("metrics"), dict) else {}
        )
        context = (
            budget_result.get("budget_context")
            if isinstance(budget_result.get("budget_context"), dict)
            else {}
        )

        over_budget_amount = self._budget_decimal(metrics.get("over_budget_amount"))
        if over_budget_amount > Decimal("0.00"):
            return True

        after_usage_rate = self._budget_decimal(metrics.get("after_usage_rate"))
        claim_amount_ratio = self._budget_decimal(metrics.get("claim_amount_ratio"))
        warning_threshold = self._budget_decimal(context.get("warning_threshold") or "80.00")
        return max(after_usage_rate, claim_amount_ratio) >= warning_threshold

    @staticmethod
    def _budget_decimal(value: Any) -> Decimal:
        try:
            return Decimal(str(value if value is not None else "0")).quantize(Decimal("0.01"))
        except (InvalidOperation, ValueError):
            return Decimal("0.00")
