from __future__ import annotations

import json
import re
from datetime import UTC, datetime
from typing import Any

from app.core.agent_enums import AgentAssetType
from app.models.agent_asset import AgentAsset
from app.services.risk_rule_flow_diagram import (
    RiskRuleFlowDiagramField,
    RiskRuleFlowDiagramRenderer,
    RiskRuleFlowDiagramSpec,
    build_risk_rule_flow_diagram_details,
)


RISK_RULE_LEVEL_LABELS = {
    "low": "低风险",
    "medium": "中风险",
    "high": "高风险",
    "critical": "极高风险",
}


class AgentAssetRiskRuleLevelMixin:
    _RUNTIME_JSON_BLOCK_PATTERN = re.compile(r"```json\s*\n.*?\n```", re.DOTALL)
    _RISK_LEVEL_TEXT_PATTERN = re.compile(r"(低|中|高)风险")

    def set_risk_rule_level(
        self,
        asset_id: str,
        *,
        risk_level: str,
        actor: str,
        request_id: str | None = None,
    ) -> AgentAsset:
        del asset_id, risk_level, actor, request_id
        raise ValueError("风险等级和分数由评分模型自动计算，不能手动修改。")
        asset = self._resolve_asset(asset_id)
        self._require_json_risk_asset(asset)
        normalized_level = self._normalize_risk_rule_level(risk_level)
        before = {
            **self._asset_snapshot(asset),
            "risk_level": self._resolve_asset_risk_level(asset),
        }

        rule_library, file_name = self._resolve_json_risk_rule_document(asset)
        manifest = self.rule_library_manager.read_rule_library_json(
            library=rule_library,
            file_name=file_name,
        )
        updated_manifest = self._apply_risk_level_to_manifest(asset, manifest, normalized_level)
        self.rule_library_manager.write_rule_library_json(
            library=rule_library,
            file_name=file_name,
            payload=updated_manifest,
        )

        config_json = dict(asset.config_json or {})
        config_json["severity"] = normalized_level
        config_json["risk_level"] = normalized_level
        config_json["risk_level_label"] = RISK_RULE_LEVEL_LABELS[normalized_level]
        asset.config_json = config_json
        self._sync_current_version_runtime_json(asset, updated_manifest)
        updated = self.repository.save_asset(asset)
        self.audit_service.log_action(
            actor=actor,
            action="set_risk_rule_level",
            resource_type=AgentAssetType.RULE.value,
            resource_id=asset.id,
            before_json=before,
            after_json={
                **self._asset_snapshot(updated),
                "risk_level": normalized_level,
            },
            request_id=request_id,
        )
        return updated

    def _apply_risk_level_to_manifest(
        self,
        asset: AgentAsset,
        manifest: dict[str, Any],
        risk_level: str,
    ) -> dict[str, Any]:
        label = RISK_RULE_LEVEL_LABELS[risk_level]
        payload = dict(manifest or {})
        payload["severity"] = risk_level

        outcomes = dict(payload.get("outcomes") or {})
        fail_outcome = dict(outcomes.get("fail") or {})
        fail_outcome["severity"] = risk_level
        outcomes["fail"] = fail_outcome
        outcomes.setdefault("pass", {"severity": "none", "action": "continue"})
        payload["outcomes"] = outcomes

        metadata = dict(payload.get("metadata") or {})
        metadata["risk_level"] = risk_level
        metadata["risk_level_label"] = label
        metadata["risk_level_updated_at"] = datetime.now(UTC).isoformat()
        if metadata.get("business_explanation"):
            metadata["business_explanation"] = self._replace_risk_level_text(
                metadata["business_explanation"],
                label,
            )
        flow = dict(metadata.get("flow") or {})
        flow["fail"] = self._replace_risk_level_text(
            str(flow.get("fail") or f"命中{label}，进入人工复核"),
            label,
        )
        metadata["flow"] = flow
        payload["metadata"] = metadata

        if payload.get("description"):
            payload["description"] = self._replace_risk_level_text(payload["description"], label)

        params = dict(payload.get("params") or {})
        params_flow = params.get("flow")
        if isinstance(params_flow, dict):
            next_params_flow = dict(params_flow)
            next_params_flow["fail"] = self._replace_risk_level_text(
                str(next_params_flow.get("fail") or f"命中{label}，进入人工复核"),
                label,
            )
            params["flow"] = next_params_flow
        payload["params"] = params

        payload["flow_diagram_svg"] = self._build_risk_level_flow_diagram_svg(
            asset,
            payload,
            risk_level=risk_level,
        )
        return payload

    def _build_risk_level_flow_diagram_svg(
        self,
        asset: AgentAsset,
        manifest: dict[str, Any],
        *,
        risk_level: str,
    ) -> str:
        metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {}
        flow = metadata.get("flow") if isinstance(metadata.get("flow"), dict) else {}
        params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {}
        fields = self._resolve_risk_rule_diagram_fields(manifest)
        details = build_risk_rule_flow_diagram_details(manifest, fields)
        risk_label = RISK_RULE_LEVEL_LABELS[risk_level]
        return RiskRuleFlowDiagramRenderer().render(
            RiskRuleFlowDiagramSpec(
                title=self._clean_risk_level_text(manifest.get("name")) or asset.name,
                domain_label=self._resolve_risk_rule_domain_label(asset, manifest),
                severity=risk_level,
                severity_label=risk_label,
                fields=tuple(fields),
                start=self._clean_risk_level_text(flow.get("start")) or "业务单据提交",
                evidence=self._clean_risk_level_text(flow.get("evidence")) or "读取规则字段",
                decision=(
                    self._clean_risk_level_text(flow.get("decision"))
                    or self._clean_risk_level_text(params.get("condition_summary"))
                    or "判断是否命中风险"
                ),
                basis=(
                    self._clean_risk_level_text(metadata.get("condition_summary"))
                    or self._clean_risk_level_text(params.get("condition_summary"))
                    or "根据规则字段判断是否命中风险"
                ),
                pass_text=self._clean_risk_level_text(flow.get("pass")) or "未命中风险，继续流转",
                fail_text=self._clean_risk_level_text(flow.get("fail"))
                or f"命中{risk_label}，进入人工复核",
                fact_lines=details["fact_lines"],
                condition_lines=details["condition_lines"],
                hit_logic=str(details["hit_logic"] or ""),
            )
        )

    def _resolve_risk_rule_diagram_fields(
        self,
        manifest: dict[str, Any],
    ) -> list[RiskRuleFlowDiagramField]:
        inputs = manifest.get("inputs") if isinstance(manifest.get("inputs"), dict) else {}
        rows = inputs.get("fields") if isinstance(inputs.get("fields"), list) else []
        fields: list[RiskRuleFlowDiagramField] = []
        for row in rows:
            if not isinstance(row, dict):
                continue
            key = self._clean_risk_level_text(row.get("key"))
            label = self._clean_risk_level_text(row.get("label")) or key
            if key or label:
                fields.append(RiskRuleFlowDiagramField(key=key, label=label))
        return fields

    def _resolve_risk_rule_domain_label(
        self,
        asset: AgentAsset,
        manifest: dict[str, Any],
    ) -> str:
        config_json = dict(asset.config_json or {})
        return (
            self._clean_risk_level_text(manifest.get("risk_category"))
            or self._clean_risk_level_text(config_json.get("risk_category"))
            or self._clean_risk_level_text(config_json.get("expense_category_label"))
            or "报销"
        )

    def _sync_current_version_runtime_json(
        self,
        asset: AgentAsset,
        manifest: dict[str, Any],
    ) -> None:
        version_name = self._resolve_working_version(asset)
        if not version_name:
            return
        version = self.repository.get_version(asset.id, version_name)
        if version is None or version.content_type != "markdown":
            return

        runtime_block = f"```json\n{json.dumps(manifest, ensure_ascii=False, indent=2)}\n```"
        content = str(version.content or "")
        if self._RUNTIME_JSON_BLOCK_PATTERN.search(content):
            version.content = self._RUNTIME_JSON_BLOCK_PATTERN.sub(
                lambda _: runtime_block,
                content,
                count=1,
            )
        else:
            version.content = f"{content.rstrip()}\n\n## 运行时 JSON\n\n{runtime_block}"
        self.db.add(version)

    def _resolve_asset_risk_level(self, asset: AgentAsset) -> str:
        config_json = dict(asset.config_json or {})
        return self._normalize_risk_rule_level(config_json.get("severity") or "medium")

    @classmethod
    def _replace_risk_level_text(cls, value: Any, risk_label: str) -> str:
        text = str(value or "").strip()
        if not text:
            return text
        if cls._RISK_LEVEL_TEXT_PATTERN.search(text):
            return cls._RISK_LEVEL_TEXT_PATTERN.sub(risk_label, text)
        return text

    @staticmethod
    def _normalize_risk_rule_level(value: Any) -> str:
        normalized = str(value or "").strip().lower()
        if normalized not in RISK_RULE_LEVEL_LABELS:
            raise ValueError("风险等级仅支持 low、medium、high、critical。")
        return normalized

    @staticmethod
    def _clean_risk_level_text(value: Any) -> str:
        return str(value or "").strip()
