from __future__ import annotations

from typing import Any

from app.services.risk_rule_flow_diagram import (
    RiskRuleFlowDiagramField,
    RiskRuleFlowDiagramRenderer,
    build_risk_rule_flow_diagram_spec,
)
from app.services.risk_rule_generation_ontology import RiskRuleField


def build_risk_rule_explainability_artifacts(
    payload: dict[str, Any],
    *,
    fields: list[RiskRuleField],
    domain_label: str,
    risk_level: str,
    risk_level_label: str,
) -> dict[str, Any]:
    diagram_fields = tuple(
        RiskRuleFlowDiagramField(key=field.key, label=field.label) for field in fields
    )
    semantic_plan = build_semantic_plan(
        payload,
        fields=diagram_fields,
        domain_label=domain_label,
        risk_level=risk_level,
        risk_level_label=risk_level_label,
    )
    flow_model = build_flow_model(
        payload,
        fields=diagram_fields,
        semantic_plan=semantic_plan,
        risk_level=risk_level,
        risk_level_label=risk_level_label,
    )
    flow_explanation = build_flow_explanation(flow_model)
    flow_diagram_svg = build_flow_diagram_svg(
        payload,
        fields=diagram_fields,
        flow_model=flow_model,
        domain_label=domain_label,
        risk_level=risk_level,
        risk_level_label=risk_level_label,
    )
    return {
        "semantic_plan": semantic_plan,
        "flow_model": flow_model,
        "flow_explanation": flow_explanation,
        "flow_diagram_svg": flow_diagram_svg,
    }


def build_semantic_plan(
    payload: dict[str, Any],
    *,
    fields: tuple[RiskRuleFlowDiagramField, ...],
    domain_label: str,
    risk_level: str,
    risk_level_label: str,
) -> dict[str, Any]:
    params = _read_dict(payload.get("params"))
    metadata = _read_dict(payload.get("metadata"))
    outcomes = _read_dict(payload.get("outcomes"))
    fail = _read_dict(outcomes.get("fail"))
    return {
        "rule_intent": _text(payload.get("description"))
        or _text(metadata.get("natural_language"))
        or _text(payload.get("name")),
        "scope": {
            "domain_label": domain_label,
            "business_stage": _text(params.get("business_stage"))
            or _text(metadata.get("business_stage")),
            "business_stage_label": _text(params.get("business_stage_label"))
            or _text(metadata.get("business_stage_label")),
            "expense_category": _text(metadata.get("expense_category")),
            "expense_category_label": _text(metadata.get("expense_category_label"))
            or _text(payload.get("risk_category")),
        },
        "required_fields": [
            {
                "label": field.label or field.key,
                "field": field.key,
                "display": _field_display(field),
            }
            for field in fields
        ],
        "judgment_steps": _build_judgment_steps(params, fields),
        "exception_conditions": _build_exception_conditions(params),
        "risk_action": {
            "risk_level": risk_level,
            "risk_level_label": risk_level_label,
            "risk_score": fail.get("risk_score") or metadata.get("risk_score"),
            "decision": fail.get("action") or "manual_review",
            "message": _text(params.get("message_template"))
            or _text(params.get("condition_summary"))
            or "命中后进入人工复核。",
        },
    }


def build_flow_model(
    payload: dict[str, Any],
    *,
    fields: tuple[RiskRuleFlowDiagramField, ...],
    semantic_plan: dict[str, Any],
    risk_level: str,
    risk_level_label: str,
) -> dict[str, Any]:
    params = _read_dict(payload.get("params"))
    metadata = _read_dict(payload.get("metadata"))
    flow = _read_dict(metadata.get("flow"))
    conditions = _read_list(params.get("conditions"))
    nodes: list[dict[str, Any]] = [
        {
            "id": "start",
            "type": "start",
            "title": "业务输入",
            "description": _text(flow.get("start")) or "业务单据提交",
        },
        {
            "id": "evidence",
            "type": "evidence",
            "title": "字段事实",
            "description": _text(flow.get("evidence")) or "读取规则字段并形成判断事实",
            "fields": [field.key for field in fields],
        },
    ]
    for index, condition in enumerate(conditions, start=1):
        if not isinstance(condition, dict):
            continue
        nodes.append(
            {
                "id": _condition_id(condition, index),
                "type": "decision",
                "title": _condition_title(condition, index),
                "description": _condition_description(condition),
                "operator": _text(condition.get("operator")),
                "fields": _condition_fields(condition),
            }
        )
    if len(nodes) == 2:
        nodes.append(
            {
                "id": "decision",
                "type": "decision",
                "title": "判断依据",
                "description": _text(params.get("condition_summary"))
                or _text(flow.get("decision"))
                or "判断是否命中风险",
                "fields": [field.key for field in fields],
            }
        )
    nodes.extend(
        [
            {
                "id": "pass",
                "type": "pass",
                "title": "不命中风险",
                "description": _text(flow.get("pass")) or "继续业务流转",
            },
            {
                "id": "hit",
                "type": "risk",
                "title": f"命中{risk_level_label}",
                "description": _text(flow.get("fail"))
                or f"命中{risk_level_label}，进入人工复核",
                "risk_level": risk_level,
            },
        ]
    )
    edges = _build_edges([node["id"] for node in nodes if node["id"] not in {"pass", "hit"}])
    return {
        "version": "1.0",
        "source": "json_dsl",
        "nodes": nodes,
        "edges": edges,
        "risk_level": risk_level,
        "risk_level_label": risk_level_label,
        "semantic_plan_ref": semantic_plan.get("rule_intent", ""),
    }


def build_flow_explanation(flow_model: dict[str, Any]) -> list[dict[str, str]]:
    rows = []
    for node in _read_list(flow_model.get("nodes")):
        if not isinstance(node, dict):
            continue
        if node.get("type") in {"start", "evidence", "decision", "risk", "pass"}:
            rows.append(
                {
                    "node_id": _text(node.get("id")),
                    "title": _text(node.get("title")),
                    "description": _text(node.get("description")),
                }
            )
    return rows


def build_flow_diagram_svg(
    payload: dict[str, Any],
    *,
    fields: tuple[RiskRuleFlowDiagramField, ...],
    flow_model: dict[str, Any] | None = None,
    domain_label: str,
    risk_level: str,
    risk_level_label: str,
) -> str:
    renderer = RiskRuleFlowDiagramRenderer()
    return renderer.render(build_risk_rule_flow_diagram_spec(
        payload,
        fields=fields,
        flow_model=flow_model,
        domain_label=domain_label,
        severity=risk_level,
        severity_label=risk_level_label,
    ))


def _build_judgment_steps(
    params: dict[str, Any],
    fields: tuple[RiskRuleFlowDiagramField, ...],
) -> list[dict[str, Any]]:
    conditions = _read_list(params.get("conditions"))
    if not conditions:
        return [
            {
                "id": "decision",
                "operator": _text(params.get("template_key")),
                "description": _text(params.get("condition_summary")) or "判断规则字段是否满足条件。",
                "fields": [field.key for field in fields],
            }
        ]
    steps = []
    for index, condition in enumerate(conditions, start=1):
        if isinstance(condition, dict):
            steps.append(
                {
                    "id": _condition_id(condition, index),
                    "operator": _text(condition.get("operator")),
                    "description": _condition_description(condition),
                    "fields": _condition_fields(condition),
                }
            )
    return steps


def _build_exception_conditions(params: dict[str, Any]) -> list[dict[str, Any]]:
    keywords = _read_string_list(params.get("exception_keywords"))
    fields = _read_string_list(params.get("exception_fields"))
    if not keywords and not fields:
        return []
    return [{"fields": fields, "keywords": keywords, "effect": "作为复核或降级依据，不替代结构化判断"}]


def _build_edges(decision_node_ids: list[str]) -> list[dict[str, str]]:
    if not decision_node_ids:
        return []
    edges = [{"from": "start", "to": "evidence", "label": "开始"}]
    previous = "evidence"
    for node_id in decision_node_ids:
        if node_id in {"start", "evidence"}:
            continue
        edges.append({"from": previous, "to": node_id, "label": "进入判断"})
        previous = node_id
    edges.append({"from": previous, "to": "pass", "label": "否"})
    edges.append({"from": previous, "to": "hit", "label": "是"})
    return edges


def _condition_id(condition: dict[str, Any], index: int) -> str:
    return _text(condition.get("id")) or f"condition_{index}"


def _condition_title(condition: dict[str, Any], index: int) -> str:
    operator = _text(condition.get("operator")) or "condition"
    return _text(condition.get("title")) or f"判断 {index}: {operator}"


def _condition_description(condition: dict[str, Any]) -> str:
    operator = _text(condition.get("operator"))
    if operator in {"not_in_scope", "not_in_set", "not_overlap"}:
        return "左侧字段集合与右侧字段集合无交集时成立。"
    if operator in {"in_scope", "overlap"}:
        return "左侧字段集合与右侧字段集合存在交集时成立。"
    if operator == "date_outside_range":
        return "日期字段早于开始日期或晚于结束日期时成立。"
    if operator == "numeric_compare":
        return "数值字段与预算、阈值或金额字段比较后满足超额、低于或等于等关系时成立。"
    if operator == "duplicate_value":
        return "同一票据号、附件编号或业务唯一键在规则范围内重复出现时成立。"
    if operator in {"contains_any", "not_contains_any"}:
        return "检查文本字段是否包含指定说明关键词。"
    if operator in {"exists_any", "exists_all", "all_present"}:
        return "检查规则要求字段是否已提供。"
    return _text(condition.get("description")) or "执行规则条件判断。"


def _condition_fields(condition: dict[str, Any]) -> list[str]:
    keys: list[str] = []
    for name in (
        "fields",
        "left_fields",
        "right_fields",
        "date_fields",
        "range_start_fields",
        "range_end_fields",
        "exception_fields",
    ):
        for key in _read_string_list(condition.get(name)):
            if key not in keys:
                keys.append(key)
    for name in ("left", "right"):
        value = _text(condition.get(name))
        if value and value not in keys:
            keys.append(value)
    return keys


def _field_display(field: RiskRuleFlowDiagramField) -> str:
    if field.label and field.label != field.key:
        return f"{field.label}[{field.key}]"
    return field.label or field.key


def _read_dict(value: Any) -> dict[str, Any]:
    return value if isinstance(value, dict) else {}


def _read_list(value: Any) -> list[Any]:
    return value if isinstance(value, list) else []


def _read_string_list(value: Any) -> list[str]:
    return [_text(item) for item in _read_list(value) if _text(item)]


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