"""L0 自动分类引擎 — 从自由文本推断 category/confidence/scope

纯规则实现，不依赖 LLM。供 learn MCP 工具和 observe hook 使用。
"""

from __future__ import annotations

import json
import logging
import re

logger = logging.getLogger(__name__)

# --- 分类规则（顺序敏感，首命中返回） ---
# 优先级: constraint > preference > root_cause > decision > revert > tradeoff
#          > pattern > architecture > config > code(默认)
# L3b (constraint/preference) 匹配最高优先级 — 不衰减
# L3a (pattern) 用于跨场景归纳

_CATEGORY_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
    # L3b: 世界观 — 不衰减
    ("constraint", re.compile(
        r"(?:must\s+(?:always|never|not)|^(?:always|never)\s+\w+|"
        r"(?:必须|禁止|不[允准许]许|强制|不可|绝不|一定要))",
        re.IGNORECASE,
    )),
    ("preference", re.compile(
        r"(?:"
        # English: comparison structure (X over/instead of/rather than Y)
        r"prefer(?:s|red)?\s+\w+.*?\s+(?:over|instead\s+of|rather\s+than)\s+\w+"
        # English: prefer + action (prefer using/to use X)
        r"|prefer(?:s|red)?\s+(?:using|to\s+use|to\s+\w+)\s+\w+"
        # English: preference for X (noun form)
        r"|preference\s+for\s+\w+"
        # English: preferred approach/method
        r"|preferred\s+(?:approach|method|way)\s+"
        # English: recommended to use/keep/avoid
        r"|recommended\s+to\s+(?:use|keep|avoid)\s+\w+"
        # Chinese: 优先/偏好 + action (± comparison)
        r"|(?:优先|偏好)\s*(?:用|使用|选择|考虑)\s*\S+"
        # Chinese: 推荐/建议 + 使用
        r"|(?:推荐|建议)使用\s*\S+"
        # Chinese: 倾向于用
        r"|倾向于\s*(?:用|使用)\s*\S+"
        r")",
        re.IGNORECASE,
    )),
    # L3b: 世界观 — 隐含价值判断（优先级低于 constraint/preference）
    ("worldview", re.compile(
        r"(?:"
        # English: comparison value (X is better/worse than Y)
        r"\w+\s+is\s+(?:better|worse|superior|inferior)\s+(?:than|to)\s+\w+"
        # English: the right/best/proper way
        r"|the\s+(?:right|best|proper|correct|ideal)\s+(?:way|approach|practice)"
        # English: habit signals (I usually/always/generally)
        r"|I\s+(?:usually|always|generally|typically|normally)\s+\w+"
        # English: long-term signals (from now on / going forward)
        r"|(?:from\s+now\s+on|going\s+forward|in\s+the\s+future)\b"
        # Chinese: comparison (X 比 Y 好, X > Y)
        r"|[\w\u4e00-\u9fff]+\s*(?:比|>|优于)\s*[\w\u4e00-\u9fff]+\s*(?:好|强|快|稳)"
        # Chinese: value judgment (更好的做法/最佳实践)
        r"|(?:更好的|最[佳好]的?)\s*(?:做法|方式|实践|方案)"
        # Chinese: habit (我一般用/我习惯/以后都)
        r"|(?:我一般|我习惯|以后都|今后都)\s*\S+"
        r")",
        re.IGNORECASE,
    )),
    # L2: 战术记忆
    ("root_cause", re.compile(
        r"(?:root\s*cause|caused?\s*by|because|the\s+issue\s+was|"
        r"原因|根因|导致|问题出在|之所以|是因为|由于)",
        re.IGNORECASE,
    )),
    ("decision", re.compile(
        r"(?:chose|decided|选择|决定|instead\s+of|rather\s+than|"
        r"we\s+should|采用|改[为用]|不[用再])",
        re.IGNORECASE,
    )),
    ("revert", re.compile(
        r"(?:revert(?:ed)?|回滚|rollback|undo|撤销)",
        re.IGNORECASE,
    )),
    ("tradeoff", re.compile(
        r"(?:tradeoff|trade-off|权衡|pros?\s+and\s+cons?|"
        r"advantage|disadvantage|优[缺]点|然而|不过|(?<![不])但(?:是|却))",
        re.IGNORECASE,
    )),
    # L3a: 战略模式 — 跨场景归纳
    ("pattern", re.compile(
        r"(?:pattern|规律|every\s+time|whenever|总是|"
        r"recurring|反复出现|common\s+(?:issue|problem|pattern))",
        re.IGNORECASE,
    )),
    ("architecture", re.compile(
        r"(?:architecture|模块|module|pipeline|系统设计|system\s+design|"
        r"depends\s+on|依赖|层[级次]|分层)",
        re.IGNORECASE,
    )),
    ("config", re.compile(
        r"(?:config(?:uration)?|配置|environment|env\s*var|环境变量|"
        r"设置|setting|port\s*\d|url|版本|version\s*\d)",
        re.IGNORECASE,
    )),
]

# --- scope 提取正则 ---

_FILE_PATH_RE = re.compile(
    r"(?:^|[\s\"'`(,])(/?\w[\w./-]*\.(?:py|rs|js|ts|tsx|go|md|toml|yaml|yml|json|sql|sh|css|html))"
    r"(?=[\s\"'`),.:;]|$)"
)

_BACKTICK_ENTITY_RE = re.compile(r"`(\w[\w.]*)(?:\(\))?`")

_PASCAL_CASE_RE = re.compile(r"\b([A-Z][a-z]+(?:[A-Z][a-z]+)+)\b")

_CAUSAL_RE = re.compile(
    r"(?:because|caused?\s*by|原因|根因|导致|since|due\s+to|之所以)",
    re.IGNORECASE,
)


def classify_category(text: str) -> str:
    """L0 规则分类 — 正则首命中返回，默认 'code'"""
    for category, pattern in _CATEGORY_PATTERNS:
        if pattern.search(text):
            return category
    return "code"


def estimate_confidence(text: str, context: str = "") -> float:
    """L0 置信度评估 — 基于内容丰富度信号

    base=0.8, 每个信号 +0.05, cap=0.95
    """
    conf = 0.80

    # 包含文件路径
    if _FILE_PATH_RE.search(text):
        conf += 0.05

    # 包含代码引用（backtick 或函数名模式）
    if "`" in text or re.search(r"\b\w+\(\)", text):
        conf += 0.05

    # 包含因果解释
    if _CAUSAL_RE.search(text):
        conf += 0.05

    # context 非空且有实质内容
    if context and len(context.strip()) > 20:
        conf += 0.05

    return min(conf, 0.95)


def extract_scope(text: str) -> dict:
    """L0 scope 提取 — 从文本自动识别文件路径、实体名、模块名

    Returns:
        {"scope_files": [...], "scope_entities": [...], "scope_modules": [...]}
    """
    # 文件路径
    scope_files = list(dict.fromkeys(_FILE_PATH_RE.findall(text)))

    # 实体名：backtick 包裹 + PascalCase
    entities: list[str] = []
    for m in _BACKTICK_ENTITY_RE.finditer(text):
        name = m.group(1)
        # 过滤纯小写短词（like `the`）和文件路径（已在 scope_files 中）
        if len(name) >= 2 and not any(name.endswith(ext) for ext in (".py", ".js", ".rs", ".ts")):
            entities.append(name)
    for m in _PASCAL_CASE_RE.finditer(text):
        name = m.group(1)
        if name not in entities:
            entities.append(name)

    # 模块名：从文件路径提取父目录
    modules: list[str] = []
    for fp in scope_files:
        parts = fp.strip("/").split("/")
        if len(parts) >= 2:
            # 取倒数第二个目录作为模块
            mod = parts[-2]
            if mod not in modules and mod not in ("src", "lib", "app", "tests"):
                modules.append(mod)

    return {
        "scope_files": scope_files,
        "scope_entities": entities,
        "scope_modules": modules,
    }


def build_context_json(insight: str, context: str) -> str:
    """构建 context_json — 合并用户提供的 context + 来源标记

    如果 context 是合法 JSON 则解析合并，否则作为 user_context 字段。
    """
    result: dict = {"source": "learn_tool"}

    if context and context.strip():
        try:
            parsed = json.loads(context)
            if isinstance(parsed, dict):
                result.update(parsed)
            else:
                result["user_context"] = context
        except (json.JSONDecodeError, ValueError):
            result["user_context"] = context

    return json.dumps(result, ensure_ascii=False)
