from __future__ import annotations

from typing import Any

from app.services.expense_claim_risk_stage import (
    infer_risk_domain,
    normalize_risk_actionability,
    normalize_risk_business_stage,
    normalize_risk_visibility_scope,
    with_risk_business_stage,
)


def _normalize_basic_rule_refs(value: Any) -> list[dict[str, str]]:
    if not isinstance(value, list):
        return []
    refs: list[dict[str, str]] = []
    seen: set[tuple[str, str]] = set()
    for item in value:
        if not isinstance(item, dict):
            continue
        code = str(item.get("code") or item.get("rule_code") or "").strip()
        sheet = str(item.get("sheet") or item.get("rule_sheet") or "").strip()
        if not code and not sheet:
            continue
        key = (code, sheet)
        if key in seen:
            continue
        seen.add(key)
        refs.append(
            {
                "code": code,
                "sheet": sheet,
                "name": str(item.get("name") or "").strip(),
                "component": str(item.get("component") or "").strip(),
            }
        )
    return refs


def build_platform_risk_flag(
    manifest: dict[str, Any],
    *,
    message: str,
    evidence: dict[str, Any],
    default_business_stage: str,
) -> dict[str, Any]:
    outcomes = manifest.get("outcomes") if isinstance(manifest.get("outcomes"), dict) else {}
    fail_outcome = outcomes.get("fail") if isinstance(outcomes.get("fail"), dict) else {}
    severity = str(fail_outcome.get("severity") or "medium").strip().lower() or "medium"
    default_action = "block" if severity in {"high", "critical"} else "manual_review"
    action = str(fail_outcome.get("action") or default_action).strip()
    label = str(manifest.get("name") or manifest.get("rule_code") or "风险规则命中").strip()
    metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {}
    applies_to = manifest.get("applies_to") if isinstance(manifest.get("applies_to"), dict) else {}
    raw_stages = applies_to.get("business_stages")
    applies_to_stage = raw_stages[0] if isinstance(raw_stages, list) and raw_stages else None
    business_stage = normalize_risk_business_stage(
        manifest.get("business_stage") or metadata.get("business_stage") or applies_to_stage,
        default=default_business_stage,
    )
    risk_domain = infer_risk_domain(manifest)
    # 申请单阶段：信息完整性/差旅/金额类风险申请人可自行补充修正，对申请人可见；
    # 预算走预算审批人；其余（画像/审批流程）仍走领导可见。
    if risk_domain == "budget":
        default_visibility_scope = "budget_manager"
        default_actionability = "budget_governance"
    elif business_stage == "expense_application" and risk_domain in {"policy", "trip", "amount"}:
        default_visibility_scope = "submitter"
        default_actionability = "fixable_by_submitter"
    elif business_stage == "expense_application":
        default_visibility_scope = "leader"
        default_actionability = "review_decision"
    else:
        default_visibility_scope = "submitter"
        default_actionability = "fixable_by_submitter"
    visibility_scope = normalize_risk_visibility_scope(
        metadata.get("visibility_scope") or manifest.get("visibility_scope"),
        default_visibility_scope,
    )
    actionability = normalize_risk_actionability(
        metadata.get("actionability") or manifest.get("actionability"),
        default_actionability,
    )
    finance_rule_code = str(
        manifest.get("finance_rule_code")
        or metadata.get("finance_rule_code")
        or manifest.get("basic_rule_code")
        or metadata.get("basic_rule_code")
        or ""
    ).strip()
    finance_rule_sheet = str(
        manifest.get("finance_rule_sheet")
        or metadata.get("finance_rule_sheet")
        or manifest.get("basic_rule_sheet")
        or metadata.get("basic_rule_sheet")
        or ""
    ).strip()
    basic_rule_code = str(
        manifest.get("basic_rule_code") or metadata.get("basic_rule_code") or finance_rule_code
    ).strip()
    basic_rule_sheet = str(
        manifest.get("basic_rule_sheet") or metadata.get("basic_rule_sheet") or finance_rule_sheet
    ).strip()
    basic_rule_refs = _normalize_basic_rule_refs(
        manifest.get("basic_rule_refs") or metadata.get("basic_rule_refs")
    )
    if not basic_rule_refs and (basic_rule_code or basic_rule_sheet):
        basic_rule_refs = [
            {
                "code": basic_rule_code,
                "sheet": basic_rule_sheet,
                "name": "",
                "component": "",
            }
        ]

    flag = {
        "source": "submission_review",
        "hit_source": "rule_center",
        "rule_type": "risk",
        "rule_code": str(manifest.get("rule_code") or "").strip(),
        "rule_version": str(manifest.get("_rule_version") or "v1.0.0").strip(),
        "basic_rule_code": basic_rule_code,
        "basic_rule_sheet": basic_rule_sheet,
        "basic_rule_refs": basic_rule_refs,
        "finance_rule_code": finance_rule_code,
        "finance_rule_sheet": finance_rule_sheet,
        "severity": severity,
        "action": action,
        "label": label,
        "message": message,
        "evidence": evidence,
        "risk_domain": risk_domain,
        "visibility_scope": visibility_scope,
        "actionability": actionability,
    }
    if manifest.get("_release_stage"):
        flag["release_stage"] = str(manifest.get("_release_stage") or "")
    if manifest.get("_release_mode"):
        flag["release_mode"] = str(manifest.get("_release_mode") or "")
    return with_risk_business_stage(
        flag,
        business_stage,
    )
