from __future__ import annotations

from typing import Any


BUDGET_RISK_STAGES = {"budget_execution", "budget_control", "budget_review"}


def is_budget_risk_manifest(manifest: dict[str, Any]) -> bool:
    """判断规则是否属于预算治理风险，而不是普通费用行为风险。"""

    if not isinstance(manifest, dict):
        return False

    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 {}
    rule_code = str(manifest.get("rule_code") or "").strip().lower()
    finance_rule_code = str(
        manifest.get("finance_rule_code") or metadata.get("finance_rule_code") or ""
    ).strip().lower()

    if rule_code.startswith("risk.budget.") or rule_code.startswith("budget."):
        return True
    if finance_rule_code.startswith("budget."):
        return True
    if _normalized_text(manifest.get("risk_domain") or metadata.get("risk_domain")) == "budget":
        return True

    domains = {_normalized_text(value) for value in _as_list(applies_to.get("domains"))}
    if "budget" in domains and not domains.difference({"budget"}):
        return True

    stages = {
        _normalized_text(value)
        for value in [
            *_as_list(manifest.get("business_stage")),
            *_as_list(metadata.get("business_stage")),
            *_as_list(applies_to.get("business_stages")),
        ]
    }
    if stages & BUDGET_RISK_STAGES:
        return True

    category_text = " ".join(
        str(value or "")
        for value in (
            manifest.get("risk_category"),
            metadata.get("risk_category"),
            manifest.get("name"),
        )
    )
    if "预算" in category_text and any(key.startswith("budget.") for key in _iter_field_keys(manifest)):
        return True

    return any(key.startswith("budget.") for key in _iter_field_keys(manifest))


def _iter_field_keys(value: Any) -> list[str]:
    keys: list[str] = []

    def visit(node: Any) -> None:
        if isinstance(node, dict):
            for key, item in node.items():
                normalized_key = str(key or "").strip()
                if normalized_key in {
                    "key",
                    "field",
                    "left",
                    "right",
                    "field_key",
                    "fieldKey",
                }:
                    _append_key(item)
                elif normalized_key in {
                    "fields",
                    "field_keys",
                    "fieldKeys",
                    "search_fields",
                    "searchFields",
                    "left_fields",
                    "leftFields",
                    "right_fields",
                    "rightFields",
                    "left_group",
                    "leftGroup",
                    "right_group",
                    "rightGroup",
                    "date_fields",
                    "range_start_fields",
                    "range_end_fields",
                }:
                    for child in _as_list(item):
                        _append_key(child)
                visit(item)
            return
        if isinstance(node, list):
            for item in node:
                visit(item)

    def _append_key(item: Any) -> None:
        text = str(item or "").strip().lower()
        if text and text not in keys:
            keys.append(text)

    visit(value)
    return keys


def _as_list(value: Any) -> list[Any]:
    if isinstance(value, list):
        return value
    if isinstance(value, (tuple, set)):
        return list(value)
    if value in (None, ""):
        return []
    return [value]


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