"""角色-能力模型:把角色定义(roles/*.json)载入为 Role,并校验它只用引擎认识的"能力词表"。

可扩展性的落点:引擎只认下面这组原语,角色靠组合它们而成。
- 加角色 = 加一个 json(只要用的能力都在词表内);
- 加机制 = 先往词表里加一个原语、再加数据。绝不写 "if 角色==X"。
"""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from pathlib import Path

# —— 能力词表(引擎内置原语;扩这里才算动引擎)——
NIGHT_ACTIONS = {"none", "kill", "check", "save_poison", "guard"}   # guard 已预留(9人局未用)
KNOWS = {"teammates", "check_result", "knife_target", "potions", "guard_target"}
ON_DEATH = {"none", "hunter_shoot", "wolf_king_shoot"}
PASSIVES = {"none", "idiot_reveal", "elder_tough"}
COUNTS_AS = {"wolf", "god", "villager"}      # 屠边归边
TEAMS = {"wolf", "good"}


@dataclass(frozen=True)
class Role:
    """一个角色的声明式定义(纯数据;行为由引擎按这些字段分发)。"""

    id: str
    name: str
    team: str                  # wolf | good
    counts_as: str             # wolf | god | villager(屠边判定归边)
    night_action: str          # 词表 NIGHT_ACTIONS
    night_order: int           # 夜间结算顺序(0=不在夜间行动)
    knows: tuple[str, ...]     # 信息差授权(驱动 perception 过滤器)
    on_death: str
    passive: str
    vote_weight: float
    persona_hint: str = ""

    def acts_at_night(self) -> bool:
        return self.night_action != "none"


def _validate(role: Role) -> None:
    """早失败:角色用了词表外的能力 → 立刻报错,而不是运行时静默错乱。"""
    bad = []
    if role.team not in TEAMS: bad.append(f"team={role.team}")
    if role.counts_as not in COUNTS_AS: bad.append(f"counts_as={role.counts_as}")
    if role.night_action not in NIGHT_ACTIONS: bad.append(f"night.action={role.night_action}")
    if role.on_death not in ON_DEATH: bad.append(f"on_death={role.on_death}")
    if role.passive not in PASSIVES: bad.append(f"passive={role.passive}")
    for k in role.knows:
        if k not in KNOWS: bad.append(f"knows:{k}")
    if bad:
        raise ValueError(f"角色 {role.id} 用了能力词表外的字段(需先扩词表):{', '.join(bad)}")


def role_from_dict(d: dict) -> Role:
    night = d.get("night") or {}
    role = Role(
        id=d["id"], name=d.get("name", d["id"]),
        team=d.get("team", "good"), counts_as=d.get("counts_as", "villager"),
        night_action=night.get("action", "none"), night_order=int(night.get("order", 0)),
        knows=tuple(d.get("knows") or []),
        on_death=d.get("on_death", "none"), passive=d.get("passive", "none"),
        vote_weight=float(d.get("vote_weight", 1.0)), persona_hint=d.get("persona_hint", ""))
    _validate(role)
    return role


def load_roles(roles_dir: str | Path) -> dict[str, Role]:
    """载入一个目录下所有 *.json 角色定义 → {role_id: Role}。"""
    roles_dir = Path(roles_dir)
    out: dict[str, Role] = {}
    for p in sorted(roles_dir.glob("*.json")):
        r = role_from_dict(json.loads(p.read_text(encoding="utf-8")))
        out[r.id] = r
    if not out:
        raise FileNotFoundError(f"{roles_dir} 下没有角色定义")
    return out
