"""JsonRepairStrategy: 阶梯②修复(D7/D14;蓝本 VT core/agent/loop.py:341-377)。

处理链: 围栏剥离 → json_repair → json.loads → 可选 normalize 钩子。
业务 schema 相关的变体归一化(如 VT `_normalize_action` 的 DeepSeek 平铺
收拢)**不入库**——零业务假设铁律;业务侧经 `normalize` 注入自带函数。
"""

from __future__ import annotations

import json
import re
from typing import TYPE_CHECKING, Any

from polygateway.errors import ResultInvalidError

if TYPE_CHECKING:
    from collections.abc import Callable

try:
    from json_repair import repair_json
except ImportError as _exc:  # pragma: no cover - 依赖缺失路径
    repair_json = None
    _IMPORT_ERROR = _exc
else:
    _IMPORT_ERROR = None

# VT loop.py:37 同款围栏正则
_CODE_FENCE_RE = re.compile(r"^\s*```(?:json)?\s*\n?|\n?\s*```\s*$")


class JsonRepairStrategy:
    """prompt 约定 + 事后修复策略;不改请求体(request_overlay 恒空)。"""

    def __init__(self, normalize: Callable[[Any], Any] | None = None) -> None:
        if repair_json is None:
            raise ImportError(
                "结构化输出需要 json_repair 包: pip install 'polygateway[structured]'"
            ) from _IMPORT_ERROR
        self._normalize = normalize

    def request_overlay(self, schema: dict[str, Any] | None) -> dict[str, Any]:
        return {}

    def parse(self, text: str) -> Any:
        """修复并解析;失败抛 ResultInvalidError(坏结果 ≠ 坏服务)。"""
        stripped = _CODE_FENCE_RE.sub("", text).strip()
        if not stripped:
            raise ResultInvalidError("结构化输出为空", raw_text=text, repair_error="empty content")
        try:
            data = json.loads(repair_json(stripped))
        except (json.JSONDecodeError, ValueError) as exc:
            raise ResultInvalidError("JSON 修复失败", raw_text=text, repair_error=str(exc)) from exc
        if self._normalize is not None:
            data = self._normalize(data)
        return data
