"""信息差过滤器:view(state, seat, roles) → PlayerView。

这一座位**合法能知道的一切**,通用地由 role.knows 决定 —— 不写 "if 预言家"。
铁律:他人身份、死因(刀/毒/枪/放逐)、私密夜间行为,绝不出现在他人视野里。
PlayerView 是喂给 AI 脑(及真人 UI)的唯一信息源 —— 信息差在这里一次性兜住。
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional

from genesis.werewolf.roles import Role
from genesis.werewolf.state import GameState

# 只有这些事件类型对所有人公开;夜间私密行为绝不在此列(引擎本就不写它们进 transcript,这里再兜一层)
# "judge" = 法官主持播报(天黑/天亮/发言/投票…),全场可闻
PUBLIC_EVENT_TYPES = {"announce", "speech", "sheriff", "sheriff_speech", "exile", "shot", "end", "judge"}


def _event_visible(e: dict, team: str) -> bool:
    """频道可见性:public 看类型白名单;wolf 频道仅狼可见;其余一律不可见(私聊不外泄)。"""
    ch = e.get("channel", "public")
    if ch == "public":
        return e["type"] in PUBLIC_EVENT_TYPES
    if ch == "wolf":
        return team == "wolf"
    return False


@dataclass
class PlayerView:
    # —— 公共(人人可见)——
    seat: int
    role_id: str            # 你当然知道自己的身份
    role_name: str
    team: str
    day: int
    phase: str
    alive: list[int]
    dead: list[dict]        # [{seat, day}] —— 只有"谁哪天出局",无死因、无身份
    sheriff: Optional[int]
    transcript: list[dict]  # 仅公共事件
    votes_log: list[dict]   # 公开历轮投票
    # —— 私有(按 role.knows 授权;未授权一律 None)——
    teammates: Optional[list[int]] = None       # 狼:队友座位
    check_results: Optional[list[dict]] = None  # 预言家:自己的查验史 [{target,result}]
    knife_target: Optional[int] = None          # 女巫:今夜刀型
    potions: Optional[dict] = None              # 女巫:解药/毒药状态
    guard_target: Optional[int] = None          # 守卫(预留)
    # —— 死亡上帝视角(仅出局者):全场身份揭晓 + 全量 transcript(含狼私聊)——
    is_ghost: bool = False
    all_roles: Optional[dict] = None            # 座位 → 真实身份名(仅 ghost 可见)


def _ghost_view(state: GameState, seat: int, role: Role, roles: dict[str, Role]) -> PlayerView:
    """死亡玩家 = 上帝视角:全场身份揭晓 + 全量 transcript(含狼私聊)+ 死因可见。
    纯只读 —— "不能干扰对局"由引擎保证(死亡座位不在 alive_seats,永不被叫去发言/投票/行动)。"""
    return PlayerView(
        seat=seat, role_id=role.id, role_name=role.name, team=role.team,
        day=state.day, phase=state.phase, alive=state.alive_seats(),
        dead=[{"seat": d["seat"], "day": d["day"], "cause": d.get("cause")} for d in state.deaths],  # 鬼可见死因
        sheriff=state.sheriff,
        transcript=list(state.transcript),       # 全量:狼私聊一并可见
        votes_log=list(state.votes_log),
        is_ghost=True,
        all_roles={s: roles[state.role_of(s)].name for s in sorted(state.roles)},
    )


def view(state: GameState, seat: int, roles: dict[str, Role]) -> PlayerView:
    role = roles[state.role_of(seat)]
    if not state.is_alive(seat):                 # 出局者 → 上帝视角观战(看全场,不能干扰)
        return _ghost_view(state, seat, role, roles)
    pv = PlayerView(
        seat=seat, role_id=role.id, role_name=role.name, team=role.team,
        day=state.day, phase=state.phase, alive=state.alive_seats(),
        dead=[{"seat": d["seat"], "day": d["day"]} for d in state.deaths],   # 刻意丢弃 cause
        sheriff=state.sheriff,
        transcript=[e for e in state.transcript if _event_visible(e, role.team)],   # 含狼频道(仅狼)
        votes_log=list(state.votes_log),
    )
    for grant in role.knows:                     # 通用授权:加一项 knows 即自动多看一项,无需改这里以外的逻辑
        if grant == "teammates":
            pv.teammates = [s for s in state.roles if roles[state.role_of(s)].team == "wolf"]
        elif grant == "check_result":
            pv.check_results = list(state.seer_checks.get(seat, []))
        elif grant == "knife_target":
            pv.knife_target = state.last_knife
        elif grant == "potions":
            pv.potions = dict(state.witch_potions.get(seat, {}))
        elif grant == "guard_target":
            pv.guard_target = state.last_knife   # 占位,守卫接入时改读守护记录
    return pv
