"""Observer 共享 — LLM 输出解析（XML + JSON）"""

from __future__ import annotations

import json
import logging
import re

logger = logging.getLogger(__name__)


def parse_xml_observation(raw: str, provider_name: str = "observer") -> dict | None:
    """从 LLM XML 输出中提取 observation — regex 容错解析

    XML 格式比 JSON 对小模型 (3B) 更可靠：固定标签用 regex 提取，
    即使 LLM 在标签周围加了多余文字也能正确提取。

    Returns:
        observation dict（含 type/title/facts/concepts/files），
        或 None 表示解析失败或 skip 信号
    """
    if not raw or not raw.strip():
        return None

    # Skip 信号：LLM 判断此工具调用不值得记录
    if "<observation/>" in raw or "<observation />" in raw:
        return None

    result: dict = {}
    for tag in ("type", "title"):
        m = re.search(rf"<{tag}>(.*?)</{tag}>", raw, re.DOTALL)
        if m:
            result[tag] = m.group(1).strip()

    if not result.get("type"):
        logger.debug("%s: XML 无 <type> 标签: %s", provider_name, raw[:100])
        return None

    # narrative（#241 — 可选，向后兼容）
    narrative_m = re.search(r"<narrative>(.*?)</narrative>", raw, re.DOTALL)
    result["narrative"] = narrative_m.group(1).strip() if narrative_m else None

    result["facts"] = [
        f.strip() for f in re.findall(r"<fact>(.*?)</fact>", raw, re.DOTALL)
        if f.strip()
    ]
    result["concepts"] = [
        c.strip() for c in re.findall(r"<concept>(.*?)</concept>", raw, re.DOTALL)
        if c.strip()
    ]
    result["files"] = [
        f.strip() for f in re.findall(r"<file>(.*?)</file>", raw, re.DOTALL)
        if f.strip()
    ]

    return result


def parse_llm_json_array(raw: str, provider_name: str = "observer") -> list[dict] | None:
    """从 LLM 输出中提取 JSON 数组 — 容错解析（旧格式，向后兼容）

    LLM 可能在 JSON 前后输出说明文字，此函数提取第一个 [...] 并解析。

    Returns:
        fact dict 列表，或 None 表示解析失败
    """
    if not raw or not raw.strip():
        return None

    text = raw.strip()

    # 尝试提取 JSON 数组
    start = text.find("[")
    end = text.rfind("]")
    if start == -1 or end == -1 or end <= start:
        logger.debug("%s 输出无 JSON 数组: %s", provider_name, text[:100])
        return None

    try:
        facts = json.loads(text[start:end + 1])
        if not isinstance(facts, list):
            return None
        return facts
    except json.JSONDecodeError as e:
        logger.debug("%s JSON 解析失败: %s", provider_name, e)
        return None
