from __future__ import annotations

from typing import Any

_SEVERITY_WEIGHT = {
    "critical": 0,
    "high": 1,
    "medium": 2,
    "low": 3,
    "info": 4,
    "pass": 5,
}

_BLOCKING_RISK_SEVERITIES = {"high", "critical", "danger"}
_NON_RISK_SOURCES = {
    "application_detail",
    "application_handoff",
    "application_link",
    "application_link_sync",
    "application_submission",
    "approval",
    "approval_log",
    "approval_routing",
    "budget_approval",
    "expense_claim_approval",
    "expense_claim_finance_approval",
    "finance_approval",
    "manual_approval",
    "manual_return",
    "payment",
    "reminder",
    "sla_reminder",
    "urge",
}
_NON_RISK_EVENTS = {
    "expense_application_budget_approval",
    "expense_application_reimbursement_deleted",
    "expense_application_submission",
    "expense_application_to_reimbursement_draft",
    "expense_claim_approval",
    "expense_claim_finance_approval",
    "expense_claim_payment_completed",
    "expense_reimbursement_application_linked",
    "reminder",
    "sla_reminder",
    "urge",
}
_CLOSED_RISK_STATES = {
    "accepted",
    "false_positive",
    "ignored",
    "resolved",
    "waived",
}


def _text(value: Any) -> str:
    return str(value or "").strip()


def _severity_weight(flag: dict[str, Any]) -> int:
    severity = _text(flag.get("severity") or flag.get("tone") or flag.get("level")).lower()
    return _SEVERITY_WEIGHT.get(severity, 9)


def claim_risk_flag_severity(flag: dict[str, Any]) -> str:
    severity = _text(
        flag.get("severity")
        or flag.get("risk_level")
        or flag.get("riskLevel")
        or flag.get("tone")
        or flag.get("level")
    ).lower()
    return "high" if severity == "danger" else severity


def is_open_claim_risk_flag(flag: dict[str, Any]) -> bool:
    """识别仍需处理的业务风险，排除审批、付款等流程轨迹。"""

    source = _text(flag.get("source")).lower()
    event_type = _text(flag.get("event_type") or flag.get("eventType")).lower()
    if source in _NON_RISK_SOURCES or event_type in _NON_RISK_EVENTS:
        return False
    if _text(flag.get("actionability")).lower() == "system_trace":
        return False
    states = {
        _text(flag.get("resolution_status") or flag.get("resolutionStatus")).lower(),
        _text(flag.get("status")).lower(),
        _text(flag.get("feedback_status") or flag.get("feedbackStatus")).lower(),
        _text(flag.get("adjudication")).lower(),
    }
    if states & _CLOSED_RISK_STATES or bool(flag.get("resolved")):
        return False
    severity = claim_risk_flag_severity(flag)
    return bool(
        severity in {"medium", *_BLOCKING_RISK_SEVERITIES}
        or flag.get("triggered") is True
        or _text(flag.get("disposition")).lower() in {"fix", "review"}
    )


def is_blocking_claim_risk_flag(flag: dict[str, Any]) -> bool:
    if not (
        is_open_claim_risk_flag(flag)
        and claim_risk_flag_severity(flag) in _BLOCKING_RISK_SEVERITIES
    ):
        return False
    actionability = _text(flag.get("actionability")).lower()
    disposition = _text(flag.get("disposition")).lower()
    return actionability not in {"advisory_only", "route_review"} and disposition != "review"


def claim_risk_flag_observation_key(
    flag: dict[str, Any],
    *,
    claim_id: str,
) -> str:
    explicit_key = _text(flag.get("observation_key") or flag.get("observationKey"))
    if explicit_key:
        return explicit_key
    source = _text(flag.get("source")).lower()
    hit_source = _text(flag.get("hit_source") or flag.get("hitSource")).lower()
    rule_code = _text(flag.get("rule_code") or flag.get("ruleCode"))
    signal = _text(flag.get("risk_signal") or flag.get("riskSignal"))
    if source in {"platform_risk", "platform_risk_rule", "rule_center"} or hit_source == (
        "rule_center"
    ):
        suffix = rule_code or signal
        if suffix:
            return f"risk:{claim_id}:platform:{suffix}"
    return ""


def _list_values(value: Any) -> list[Any]:
    if isinstance(value, list):
        return value
    if _text(value):
        return [value]
    return []


def _item_ids(flag: dict[str, Any]) -> list[str]:
    values: list[Any] = [
        flag.get("item_id"),
        flag.get("itemId"),
        *_list_values(flag.get("item_ids")),
        *_list_values(flag.get("itemIds")),
    ]
    item_ids: list[str] = []
    seen: set[str] = set()
    for value in values:
        item_id = _text(value)
        if not item_id or item_id in seen:
            continue
        seen.add(item_id)
        item_ids.append(item_id)
    return item_ids


def _card_text(flag: dict[str, Any]) -> str:
    return " ".join(
        _text(flag.get(key))
        for key in (
            "label",
            "title",
            "name",
            "message",
            "summary",
            "reason",
            "description",
            "rule_code",
            "ruleCode",
        )
    )


def _duplicate_group(flag: dict[str, Any]) -> str:
    text = _card_text(flag)
    if any(
        term in text
        for term in (
            "多城市行程",
            "中转",
            "多地拜访",
            "改签",
            "多地出差",
            "后续行程",
            "行程终点异常",
            "连续闭环",
        )
    ) and any(
        term in text for term in ("待说明", "未说明", "缺少说明", "原因", "说明", "不一致", "异常")
    ):
        return "route-explanation"
    if any(term in text for term in ("票据城市", "申报目的地", "行程城市", "酒店票据地点")) and any(
        term in text for term in ("不一致", "不匹配", "额外中转", "绕行")
    ):
        return "travel-city-consistency"
    if any(term in text for term in ("住宿", "酒店", "宾馆")) and any(
        term in text for term in ("超标", "超出", "报销标准", "住宿标准", "差标")
    ):
        return "hotel-over-standard"

    rule_code = _text(flag.get("rule_code") or flag.get("ruleCode"))
    if rule_code:
        return f"rule:{rule_code}"
    return ""


def _same_stage(left: dict[str, Any], right: dict[str, Any]) -> bool:
    left_stage = _text(left.get("business_stage") or left.get("businessStage"))
    right_stage = _text(right.get("business_stage") or right.get("businessStage"))
    return not left_stage or not right_stage or left_stage == right_stage


def _same_issue(left: dict[str, Any], right: dict[str, Any]) -> bool:
    if not _same_stage(left, right):
        return False

    left_group = _duplicate_group(left)
    if not left_group or left_group != _duplicate_group(right):
        return False

    left_items = _item_ids(left)
    right_items = _item_ids(right)
    if not left_items or not right_items:
        return True
    return any(item_id in right_items for item_id in left_items)


def dedupe_claim_risk_flags(flags: list[Any] | None) -> list[Any]:
    """Remove lower-severity duplicate business risk flags at the data source."""

    normalized_flags = list(flags or [])
    deduped: list[Any] = []
    for index, flag in enumerate(normalized_flags):
        if not isinstance(flag, dict):
            deduped.append(flag)
            continue

        current_weight = _severity_weight(flag)
        is_shadowed = False
        for other_index, other in enumerate(normalized_flags):
            if other_index == index or not isinstance(other, dict):
                continue
            if not _same_issue(flag, other):
                continue

            other_weight = _severity_weight(other)
            if other_weight < current_weight or (
                other_weight == current_weight and other_index < index
            ):
                is_shadowed = True
                break

        if not is_shadowed:
            deduped.append(flag)
    return deduped
