from __future__ import annotations

import json
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Any

PROFILE_IDS = {
    "emotion": {
        "kobe92-perspective",
        "niepanchongsheng-perspective",
        "chaojiyangjia-perspective",
        "tuixuechaogu-perspective",
        "chenxiaoqun-perspective",
        "zhiyechaoshou-perspective",
    },
    "first_board": {
        "beijingchaojia-perspective",
        "chuangshiji-perspective",
        "xuxiang-perspective",
        "foshanwuyingjiao-perspective",
    },
    "leader": {
        "zhaolaoge-perspective",
        "fangxinxia-perspective",
        "xiaoe-perspective",
        "sunge-perspective",
        "liuyizhonglu-perspective",
    },
    "trend": {
        "zhangdetao-perspective",
        "zhangmengzhu-perspective",
        "zuoshouxinyi-perspective",
    },
    "low_absorption": {
        "qiaobangzhu-perspective",
        "asking-perspective",
        "longfeihu-perspective",
        "ruihexian-perspective",
    },
    "macro": {"shuipi-perspective"},
}


@dataclass(frozen=True, slots=True)
class MentorSkill:
    id: str
    name: str
    description: str
    tagline: str
    focus: tuple[str, ...]
    grade: str
    evidence_label: str
    evidence_note: str
    profile: str
    content: str
    private: bool

    def public(self) -> dict[str, Any]:
        return {
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "tagline": self.tagline,
            "focus": list(self.focus),
            "grade": self.grade,
            "evidence_label": self.evidence_label,
            "evidence_note": self.evidence_note,
            "private": self.private,
        }


class MentorSkillError(ValueError):
    pass


class MentorSkillRegistry:
    def __init__(self, public_root: Path, private_root: Path) -> None:
        self._public_root = public_root
        self._private_root = private_root

    def list(self, include_private: bool) -> tuple[MentorSkill, ...]:
        skills = {item.id: item for item in self._load_root(self._public_root, False)}
        if include_private:
            skills.update({item.id: item for item in self._load_root(self._private_root, True)})
        return tuple(sorted(skills.values(), key=lambda item: (item.name.casefold(), item.id)))

    def get(self, skill_id: str, include_private: bool) -> MentorSkill:
        match = next((item for item in self.list(include_private) if item.id == skill_id), None)
        if match is None:
            raise MentorSkillError("思维模型不存在或当前账号不可见。")
        return match

    def _load_root(self, root: Path, private: bool) -> tuple[MentorSkill, ...]:
        if not root.is_dir():
            return ()
        catalog = _catalog(root)
        result = []
        for directory in sorted(root.iterdir(), key=lambda item: item.name):
            path = directory / "SKILL.md"
            if directory.is_dir() and path.is_file():
                result.append(_read_skill(path, catalog, private))
        return tuple(result)


def _read_skill(path: Path, catalog: dict[str, Any], private: bool) -> MentorSkill:
    if path.stat().st_size > 200_000:
        raise MentorSkillError(f"Skill文件过大：{path.parent.name}")
    content = path.read_text(encoding="utf-8")
    metadata = _frontmatter(content)
    raw_id = metadata.get("name") or path.parent.name
    skill_id = re.sub(r"[^A-Za-z0-9_-]+", "-", raw_id).strip("-").lower()
    if not skill_id:
        raise MentorSkillError(f"Skill缺少有效ID：{path.parent.name}")
    heading = re.search(r"^#\s+(.+?)(?:\s*[·|]\s*.+)?$", content, re.MULTILINE)
    name = heading.group(1).strip() if heading else path.parent.name
    description_raw = metadata.get("description", "")
    purpose = re.search(r"用途[：:]\s*([^\n]+)", description_raw)
    description = purpose.group(1).strip() if purpose else _first_sentence(description_raw)
    tagline_match = re.search(r'^>\s*["“「](.+?)["”」]\s*$', content, re.MULTILINE)
    focus = tuple(
        item.strip()
        for item in re.findall(r"^###\s+模型\d+[：:]\s*(.+)$", content, re.MULTILINE)[:4]
    )
    item = catalog.get(skill_id) if isinstance(catalog.get(skill_id), dict) else {}
    evidence = item.get("evidence") if isinstance(item.get("evidence"), dict) else {}
    grade = str(evidence.get("grade") or "C").upper()
    if grade not in {"A", "B", "C"}:
        grade = "C"
    return MentorSkill(
        id=skill_id,
        name=name.removesuffix("-perspective").strip(),
        description=description,
        tagline=tagline_match.group(1).strip() if tagline_match else "",
        focus=focus,
        grade=grade,
        evidence_label=str(evidence.get("label") or "公开资料"),
        evidence_note=str(evidence.get("note") or "素材等级待进一步核验"),
        profile=_profile(skill_id, f"{description} {' '.join(focus)}"),
        content=content,
        private=private,
    )


def _catalog(root: Path) -> dict[str, Any]:
    path = root / "mentor_catalog.json"
    if not path.is_file():
        return {}
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        raise MentorSkillError(f"思维模型目录无法读取：{root.name}") from exc
    mentors = payload.get("mentors", payload) if isinstance(payload, dict) else {}
    if not isinstance(mentors, dict):
        raise MentorSkillError(f"思维模型目录格式错误：{root.name}")
    return mentors


def _profile(skill_id: str, text: str) -> str:
    for profile, identifiers in PROFILE_IDS.items():
        if skill_id in identifiers:
            return profile
    keywords = (
        ("macro", ("宏观", "政策", "指数", "ETF")),
        ("trend", ("趋势", "资金面", "动能")),
        ("first_board", ("首板", "打板")),
        ("leader", ("龙头", "连板", "空间板")),
        ("low_absorption", ("低吸", "反包", "承接")),
        ("emotion", ("情绪", "周期", "退潮")),
    )
    return next(
        (profile for profile, words in keywords if any(word in text for word in words)),
        "emotion",
    )


def _frontmatter(content: str) -> dict[str, str]:
    if not content.startswith("---"):
        return {}
    end = content.find("\n---", 3)
    if end < 0:
        return {}
    lines = content[3:end].strip().splitlines()
    result: dict[str, str] = {}
    index = 0
    while index < len(lines):
        line = lines[index]
        if ":" not in line:
            index += 1
            continue
        key, value = line.split(":", 1)
        value = value.strip()
        if value == "|":
            block = []
            index += 1
            while index < len(lines) and (lines[index].startswith(" ") or not lines[index].strip()):
                block.append(lines[index].strip())
                index += 1
            result[key.strip()] = "\n".join(block).strip()
            continue
        result[key.strip()] = value.strip('"\'')
        index += 1
    return result


def _first_sentence(value: str) -> str:
    compact = " ".join(line.strip() for line in value.splitlines() if line.strip())
    return re.split(r"[。；]", compact, maxsplit=1)[0].strip()
