from __future__ import annotations

from datetime import UTC, date, datetime, timedelta
from decimal import Decimal

from app.models.financial_record import ExpenseClaim, ExpenseClaimItem
from app.models.risk_disposition import RiskDisposition
from app.models.risk_observation import RiskObservation
from app.services.approval_workbench import ApprovalWorkbenchService


def _claim(
    *,
    claim_no: str = "RE-WORKBENCH-1",
    amount: str = "888.00",
    submitted_at: datetime,
    risk_flags: list[dict] | None = None,
    invoice_count: int = 1,
) -> ExpenseClaim:
    claim = ExpenseClaim(
        id=f"claim-{claim_no.lower()}",
        claim_no=claim_no,
        employee_id=None,
        employee_name="张三",
        department_id=None,
        department_name="市场部",
        project_code="PRJ-WORKBENCH",
        expense_type="travel",
        reason="客户现场差旅",
        location="上海",
        amount=Decimal(amount),
        currency="CNY",
        invoice_count=invoice_count,
        occurred_at=submitted_at,
        submitted_at=submitted_at,
        status="submitted",
        approval_stage="直属领导审批",
        risk_flags_json=list(risk_flags or []),
        created_at=submitted_at,
        updated_at=submitted_at,
    )
    claim.items = [
        ExpenseClaimItem(
            id=f"item-{claim_no.lower()}",
            claim_id=claim.id,
            item_date=date(2026, 7, 15),
            item_type="hotel",
            item_reason="住宿",
            item_location="上海",
            item_note="",
            item_amount=Decimal(amount),
            invoice_id="INV-WORKBENCH" if invoice_count else None,
            created_at=submitted_at,
            updated_at=submitted_at,
        )
    ]
    return claim


def test_priority_queue_explains_risk_budget_sla_amount_and_history() -> None:
    now = datetime(2026, 7, 16, 12, 0, tzinfo=UTC)
    claim = _claim(
        amount="60000.00",
        submitted_at=now - timedelta(hours=26),
        risk_flags=[
            {
                "source": "ai_pre_review",
                "severity": "high",
                "disposition": "review",
                "resolution_status": "unresolved",
                "route_decision": {"budget_result": {"metrics": {"after_usage_rate": "96.5"}}},
                "historical_case_evidence": [{"label": "confirmed", "sample_id": "must-not-leak"}],
            }
        ],
    )

    item = ApprovalWorkbenchService.build_item(claim, now=now)

    assert item.priority_score >= 85
    assert item.priority_tier == "urgent"
    assert item.risk_level == "high"
    assert item.sla_overdue is True
    assert item.budget_usage_rate == 96.5
    assert item.suggestion.action == "manual_review"
    assert item.evidence.historical_labels == ["历史已确认，仅供复核"]
    assert "must-not-leak" not in repr(item.model_dump())
    assert {reason.code for reason in item.priority_reasons} >= {
        "open_risk",
        "sla_overdue",
        "budget_pressure",
        "large_amount",
    }


def test_application_evidence_does_not_require_invoice_and_resolved_risk_is_ignored() -> None:
    now = datetime(2026, 7, 16, 12, 0, tzinfo=UTC)
    claim = _claim(
        claim_no="AP-WORKBENCH-1",
        submitted_at=now - timedelta(hours=1),
        invoice_count=0,
        risk_flags=[
            {
                "source": "ai_pre_review",
                "severity": "critical",
                "resolution_status": "resolved",
            }
        ],
    )

    item = ApprovalWorkbenchService.build_item(claim, now=now)

    assert item.evidence.completeness == 1
    assert item.evidence.missing_labels == []
    assert item.risk_level == "low"
    assert item.open_risk_count == 0
    assert item.suggestion.action == "approve_candidate"
    assert item.priority_score == 0


def test_persisted_disposition_is_authoritative_over_stale_claim_risk_flags() -> None:
    now = datetime(2026, 7, 16, 12, 0, tzinfo=UTC)
    claim = _claim(
        submitted_at=now - timedelta(hours=1),
        risk_flags=[
            {
                "severity": "critical",
                "triggered": True,
                "observation_key": "risk:workbench:resolved",
            }
        ],
    )
    observation = RiskObservation(
        id="risk-workbench-resolved",
        tenant_id="default",
        observation_key="risk:workbench:resolved",
        subject_type="expense_claim",
        subject_key=f"claim:{claim.id}",
        subject_label=claim.claim_no,
        claim_id=claim.id,
        claim_no=claim.claim_no,
        risk_type="duplicate_invoice",
        risk_signal="duplicate_invoice",
        title="重复票据风险",
        description="已复核完成。",
        risk_score=95,
        risk_level="critical",
        confidence_score=0.96,
        control_stage="reimbursement",
        control_mode="risk_observation",
        automation_mode="semi_auto_review",
        source="financial_risk_graph",
        algorithm_version="v1",
        status="resolved",
        feedback_status="confirmed",
    )
    disposition = RiskDisposition(
        tenant_id="default",
        observation_id=observation.id,
        adjudication="confirmed",
        lifecycle_status="resolved",
        version=2,
    )

    item = ApprovalWorkbenchService.build_item(
        claim,
        now=now,
        observation_rows=[(observation, disposition)],
    )

    assert item.risk_level == "low"
    assert item.open_risk_count == 0
    assert item.suggestion.action == "approve_candidate"


def test_persisted_low_risk_does_not_hide_unmaterialized_raw_high_risk() -> None:
    now = datetime(2026, 7, 16, 12, 0, tzinfo=UTC)
    claim = _claim(
        submitted_at=now - timedelta(hours=1),
        risk_flags=[
            {
                "source": "attachment_analysis",
                "severity": "high",
                "label": "票据金额异常",
                "triggered": True,
            }
        ],
    )
    observation = RiskObservation(
        id="risk-workbench-low",
        tenant_id="default",
        observation_key="risk:workbench:low",
        subject_type="expense_claim",
        subject_key=f"claim:{claim.id}",
        subject_label=claim.claim_no,
        claim_id=claim.id,
        claim_no=claim.claim_no,
        risk_type="minor_notice",
        risk_signal="minor_notice",
        title="普通提醒",
        description="普通提醒。",
        risk_score=30,
        risk_level="low",
        confidence_score=0.8,
        control_stage="reimbursement",
        control_mode="risk_observation",
        automation_mode="manual_review",
        source="financial_risk_graph",
        algorithm_version="v1",
        status="pending_review",
        feedback_status="unreviewed",
    )

    item = ApprovalWorkbenchService.build_item(
        claim,
        now=now,
        observation_rows=[(observation, None)],
    )

    assert item.risk_level == "high"
    assert item.open_risk_count == 2
    assert item.suggestion.action == "manual_review"
