from __future__ import annotations

from datetime import UTC, datetime
from decimal import Decimal

from app.models.financial_record import ExpenseClaim
from app.services.risk_rule_dsl_validator import validate_risk_rule_draft
from app.services.risk_rule_generation_ontology import RiskRuleField
from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor


FIELDS = [
    RiskRuleField("claim.location", "申报地点", "text", "claim", ("目的地", "城市")),
    RiskRuleField("attachment.hotel_city", "住宿城市", "text", "attachment", ("酒店城市",)),
    RiskRuleField("attachment.route_cities", "行程城市", "list", "attachment", ("交通票城市",)),
    RiskRuleField("claim.amount", "申报金额", "number", "claim", ("金额",)),
    RiskRuleField("budget.remaining_amount", "预算可用余额", "number", "budget", ("预算余额",)),
    RiskRuleField("claim.reason", "报销事由", "text", "claim", ("事由",)),
    RiskRuleField("attachment.ocr_text", "票据全文", "text", "attachment", ("OCR",)),
]


def test_validator_rewrites_city_keyword_rule_to_structured_compare() -> None:
    draft = {
        "template_key": "keyword_match_v1",
        "field_keys": ["attachment.hotel_city", "attachment.route_cities", "claim.location"],
        "keywords": ["绕行", "跨城办事"],
        "condition_summary": "检查住宿城市、申报地点、行程城市是否出现规则描述中的风险关键词",
    }

    normalized = validate_risk_rule_draft(
        draft,
        fields=FIELDS,
        natural_language="差旅报销时，住宿或交通票据城市必须与申报目的地一致，未说明绕行时进入复核。",
    )

    assert normalized["template_key"] == "field_compare_v1"
    assert normalized["semantic_type"] == "travel_route_city_consistency"
    assert normalized["keywords"] == []
    assert "city_rule_normalized_to_structured_compare" in normalized["dsl_validation"]["issues"]


def test_validator_rewrites_budget_keyword_rule_to_numeric_compare() -> None:
    draft = {
        "template_key": "keyword_match_v1",
        "field_keys": ["claim.amount", "budget.remaining_amount", "claim.reason"],
        "keywords": ["超预算"],
        "condition_summary": "检查金额字段是否出现预算风险关键词",
    }

    normalized = validate_risk_rule_draft(
        draft,
        fields=FIELDS,
        natural_language="费用申请时，若申报金额超过预算可用余额，则提示风险并要求补充审批说明。",
    )

    assert normalized["template_key"] == "composite_rule_v1"
    assert normalized["keywords"] == []
    assert normalized["conditions"][0]["operator"] == "numeric_compare"
    assert normalized["conditions"][0]["left_fields"] == ["claim.amount"]
    assert normalized["conditions"][0]["right_fields"] == ["budget.remaining_amount"]
    assert "风险关键词" not in normalized["condition_summary"]


def test_validator_builds_numeric_condition_for_empty_composite_fallback() -> None:
    normalized = validate_risk_rule_draft(
        {"template_key": "composite_rule_v1", "field_keys": ["claim.amount", "budget.remaining_amount"]},
        fields=FIELDS,
        natural_language="费用申请时，若申报金额超过预算可用余额，则提示风险。",
    )

    assert normalized["template_key"] == "composite_rule_v1"
    assert normalized["conditions"][0]["operator"] == "numeric_compare"
    assert normalized["hit_logic"] == {"all": ["amount_exceeds_budget"]}
    assert "empty_composite_rule_built_from_structured_fields" in normalized["dsl_validation"]["issues"]


def test_numeric_compare_condition_executes_against_budget_context() -> None:
    manifest = {
        "template_key": "composite_rule_v1",
        "params": {
            "template_key": "composite_rule_v1",
            "conditions": [
                {
                    "id": "amount_exceeds_budget",
                    "operator": "numeric_compare",
                    "left_fields": ["claim.amount"],
                    "right_fields": ["budget.remaining_amount"],
                    "compare": "gt",
                }
            ],
            "hit_logic": {"all": ["amount_exceeds_budget"]},
            "message_template": "申报金额超过预算可用余额。",
        },
    }
    claim = ExpenseClaim(
        claim_no="TEST-BUDGET-RISK",
        employee_name="测试员工",
        department_name="测试部门",
        expense_type="差旅费",
        reason="北京出差",
        location="北京",
        amount=Decimal("1200.00"),
        currency="CNY",
        invoice_count=0,
        occurred_at=datetime(2026, 5, 30, tzinfo=UTC),
        status="draft",
    )

    result = RiskRuleTemplateExecutor().evaluate(
        manifest,
        claim=claim,
        contexts=[{"budget_context": {"remaining_amount": "1000.00"}}],
    )
    assert result is not None
    assert result["message"] == "申报金额超过预算可用余额。"
    assert result["evidence"]["condition_results"]["amount_exceeds_budget"] is True

    claim.amount = Decimal("800.00")
    assert RiskRuleTemplateExecutor().evaluate(
        manifest,
        claim=claim,
        contexts=[{"budget_context": {"remaining_amount": "1000.00"}}],
    ) is None
