"""Actor:持续动作模型——动作有时长、移动逐跳走真实路径、可被打断。

(docs/agent-runtime-v2.md §1.2;决议:室内每跳 1–2 模拟分,室外 3–6 模拟分)
Actor 不做决策,只忠实执行"当前动作";决策层(规则桩/认知层)负责换动作。
"""

from __future__ import annotations

from dataclasses import dataclass, field

HOP_INDOOR_S = 100.0    # 室内一跳 ~1.7 模拟分
HOP_OUTDOOR_S = 280.0   # 室外一跳 ~4.7 模拟分


@dataclass
class Action:
    kind: str                 # move / do / sleep / idle
    desc: str                 # 人类可读("前往书房""在翻找储藏室""睡觉")
    until: float = 0.0        # 完成时刻(模拟秒);move 时为"下一跳到达时刻"
    path: list[str] = field(default_factory=list)   # move:剩余逐跳地点
    dest: str = ""            # move:最终目的地
    interruptible: bool = True
    payload: dict = field(default_factory=dict)

    @property
    def done_desc(self) -> str:
        return f"到达{self.dest}" if self.kind == "move" else f"完成了:{self.desc}"


class Actor:
    """单个 agent 的动作执行器。wake() 由调度器在 until 到点时调用。"""

    def __init__(self, agent_id: str, worldstate, clock) -> None:
        self.aid = agent_id
        self.ws = worldstate
        self.clock = clock
        self.current: Action | None = None
        self.blocked: set[str] = set()   # 此人走不了的地点(如密道——只有凶手能用),寻路时绕开

    # —— 指派 ——
    def start_move(self, dest: str) -> Action | None:
        """规划去 dest 的逐跳路径并启动第一跳;不可达返回 None(反馈给决策层)。"""
        if dest in self.blocked:         # 目的地本身就走不了(如非凶手想进密道)
            return None
        src = self.ws.positions.get(self.aid, "")
        hops = self.ws.path(src, dest, self.blocked)
        if not hops:
            return None
        act = Action(kind="move", desc=f"前往{dest}", path=hops, dest=dest)
        act.until = self.clock.sim_seconds + self._hop_cost(hops[0])
        self.current = act
        return act

    def start_do(self, desc: str, duration_s: float, *, interruptible: bool = True, **payload) -> Action:
        act = Action(kind="do", desc=desc, until=self.clock.sim_seconds + duration_s,
                     interruptible=interruptible, payload=payload)
        self.current = act
        return act

    def start_sleep(self, until_hour: float) -> Action:
        """睡到指定时刻(支持跨日)。"""
        now = self.clock.sim_seconds
        day_start = (now // 86400) * 86400
        until = day_start + until_hour * 3600.0
        if until <= now:
            until += 86400.0
        act = Action(kind="sleep", desc="睡觉", until=until)
        self.current = act
        return act

    def idle(self, duration_s: float, desc: str = "若有所思地待着") -> Action:
        act = Action(kind="idle", desc=desc, until=self.clock.sim_seconds + duration_s)
        self.current = act
        return act

    # —— 推进:返回 (状态, 详情) 给运行时 ——
    def advance(self) -> tuple[str, str]:
        """到点推进当前动作。返回:
        ("hop", 新地点) 走了一跳还在途中;("arrived", 地点) 到达;
        ("finished", 描述) 动作完成;("none", "") 无动作。"""
        act = self.current
        if act is None:
            return "none", ""
        if act.kind == "move" and act.path:
            nxt = act.path[0]
            if not self.ws.move(self.aid, nxt):     # 客观失败(路被堵等)也要反馈
                self.current = None
                return "finished", f"去{act.dest}的路走不通,停在了{self.ws.positions.get(self.aid)}"
            act.path.pop(0)
            if act.path:
                act.until = self.clock.sim_seconds + self._hop_cost(act.path[0])
                return "hop", nxt
            self.current = None
            return "arrived", nxt
        self.current = None
        return "finished", act.done_desc

    def interrupt(self) -> str:
        """打断当前动作(若可打断),返回被打断的描述。睡觉会被惊醒。"""
        act = self.current
        if act is None:
            return ""
        if not act.interruptible:
            return ""
        self.current = None
        return act.desc

    def _hop_cost(self, place: str) -> float:
        return HOP_INDOOR_S if self.ws.is_indoor(place) else HOP_OUTDOOR_S
