"""执行轨迹记录器

在 ReActEngine 执行过程中记录完整的执行轨迹（每步动作、输入输出、耗时、Token 用量），
为反思和可观测性提供数据。
"""

import time
import uuid
from dataclasses import dataclass, field
from typing import Any, Callable


@dataclass
class TraceStep:
    """单步执行轨迹"""

    step: int
    action: str  # "tool_call" | "llm_call" | "final_answer"
    tool_name: str | None = None
    input_data: dict | None = None
    output_data: Any = None
    duration_ms: int = 0
    tokens_used: int = 0
    error: str | None = None

    def to_dict(self) -> dict:
        d = {
            "step": self.step,
            "action": self.action,
            "duration_ms": self.duration_ms,
            "tokens_used": self.tokens_used,
        }
        if self.tool_name is not None:
            d["tool_name"] = self.tool_name
        if self.input_data is not None:
            d["input_data"] = self.input_data
        if self.output_data is not None:
            d["output_data"] = self.output_data
        if self.error is not None:
            d["error"] = self.error
        return d


@dataclass
class ExecutionTrace:
    """完整执行轨迹"""

    task_id: str
    agent_name: str
    skill_name: str | None = None
    steps: list[TraceStep] = field(default_factory=list)
    total_duration_ms: int = 0
    total_tokens: int = 0
    outcome: str = "success"  # "success" | "failure" | "partial"
    quality_score: float = 1.0  # 0.0 - 1.0

    def to_dict(self) -> dict:
        return {
            "task_id": self.task_id,
            "agent_name": self.agent_name,
            "skill_name": self.skill_name,
            "steps": [s.to_dict() for s in self.steps],
            "total_duration_ms": self.total_duration_ms,
            "total_tokens": self.total_tokens,
            "outcome": self.outcome,
            "quality_score": self.quality_score,
        }


class TraceRecorder:
    """执行轨迹记录器

    用法:
        recorder = TraceRecorder()
        recorder.start_trace(task_id="t1", agent_name="agent1")
        recorder.record_step(step=1, action="llm_call", ...)
        recorder.record_step(step=2, action="tool_call", tool_name="search", ...)
        trace = recorder.end_trace(outcome="success")
    """

    def __init__(
        self,
        task_id: str = "",
        agent_name: str = "",
        skill_name: str | None = None,
        on_trace_complete: Callable[[ExecutionTrace], None] | None = None,
    ):
        self._trace: ExecutionTrace | None = None
        self._completed_trace: ExecutionTrace | None = None
        self._completed: bool = False
        self._step_start_time: float = 0
        self._trace_start_time: float = 0
        self._on_trace_complete = on_trace_complete
        # 如果构造时提供了参数，自动 start_trace
        if task_id:
            self.start_trace(task_id=task_id, agent_name=agent_name, skill_name=skill_name)

    def start_trace(
        self,
        task_id: str = "",
        agent_name: str = "",
        skill_name: str | None = None,
    ) -> None:
        """开始记录执行轨迹"""
        tid = task_id or str(uuid.uuid4())
        self._trace = ExecutionTrace(
            task_id=tid,
            agent_name=agent_name,
            skill_name=skill_name,
        )
        self._completed = False
        self._trace_start_time = time.monotonic()

    def record_step(
        self,
        step: int,
        action: str,
        tool_name: str | None = None,
        input_data: dict | None = None,
        output_data: Any = None,
        duration_ms: int = 0,
        tokens_used: int = 0,
        error: str | None = None,
    ) -> None:
        """记录一个执行步骤"""
        if self._trace is None or self._completed:
            return

        trace_step = TraceStep(
            step=step,
            action=action,
            tool_name=tool_name,
            input_data=input_data,
            output_data=output_data,
            duration_ms=duration_ms,
            tokens_used=tokens_used,
            error=error,
        )
        self._trace.steps.append(trace_step)

    def end_trace(
        self,
        outcome: str = "success",
        quality_score: float = 1.0,
    ) -> ExecutionTrace:
        """结束执行轨迹记录并返回 ExecutionTrace"""
        if self._trace is None:
            # 未 start_trace 就 end_trace，返回一个空的默认轨迹
            self._trace = ExecutionTrace(
                task_id="unknown",
                agent_name="",
            )

        self._trace.outcome = outcome
        self._trace.quality_score = quality_score

        # 计算总耗时
        if self._trace_start_time > 0:
            self._trace.total_duration_ms = int(
                (time.monotonic() - self._trace_start_time) * 1000
            )

        # 计算总 token
        self._trace.total_tokens = sum(s.tokens_used for s in self._trace.steps)

        result = self._trace
        self._completed = True
        self._completed_trace = result
        self._trace = None

        if self._on_trace_complete is not None:
            self._on_trace_complete(result)

        return result

    def get_trace(self) -> ExecutionTrace | None:
        """获取当前执行轨迹（end_trace 后返回已完成的轨迹）"""
        return self._completed_trace if self._completed else self._trace

    def start_step_timer(self) -> None:
        """开始计时当前步骤"""
        self._step_start_time = time.monotonic()

    def elapsed_ms(self) -> int:
        """获取自 start_step_timer 以来的毫秒数"""
        if self._step_start_time == 0:
            return 0
        return int((time.monotonic() - self._step_start_time) * 1000)
