package agentcore

// AgentEvent is the sealed interface implemented by every event the loop emits.
// Consumers dispatch with a type switch, consistent with Content. pigo covers
// all 10 of pi's event types (PRD FR-24).
type AgentEvent interface {
	isAgentEvent()
	// EventType returns the discriminant string, useful for logging and for
	// serialising events to the stream-json/stdio protocol (US-020).
	EventType() string
}

// Event type discriminants.
const (
	EventAgentStart          = "agent_start"
	EventAgentEnd            = "agent_end"
	EventTurnStart           = "turn_start"
	EventTurnEnd             = "turn_end"
	EventMessageStart        = "message_start"
	EventMessageUpdate       = "message_update"
	EventMessageEnd          = "message_end"
	EventToolExecutionStart  = "tool_execution_start"
	EventToolExecutionUpdate = "tool_execution_update"
	EventToolExecutionEnd    = "tool_execution_end"
	EventCompaction          = "compaction"
	EventCompactionStart     = "compaction_start"
	EventTelemetry           = "telemetry"
	EventSubAgentProgress    = "subagent_progress"
)

// AgentStartEvent is emitted once when a loop run begins. SessionID, when set,
// is the id of the session backing this run; it is carried in the first
// stream-json event so a caller can associate output with a session and resume
// it later (mirrors pi/Claude Code, which put a session id in the first event).
type AgentStartEvent struct {
	SessionID string
}

// AgentEndEvent is emitted once when a loop run ends, carrying the messages
// newly produced during this run (the EventStream result).
type AgentEndEvent struct {
	Messages []AgentMessage
}

// TurnStartEvent marks the start of a turn (a single assistant response cycle).
type TurnStartEvent struct{}

// TurnEndEvent marks the end of a turn, with the assistant message and any tool
// results produced during it.
type TurnEndEvent struct {
	Message     AssistantMessage
	ToolResults []ToolResultMessage
}

// MessageStartEvent is emitted when a message begins streaming.
type MessageStartEvent struct {
	Message AgentMessage
}

// MessageUpdateEvent is emitted for each streaming delta, carrying the current
// partial message and the raw provider-level event that produced it.
type MessageUpdateEvent struct {
	Message               AgentMessage
	AssistantMessageEvent any
}

// MessageEndEvent is emitted when a message finishes streaming.
type MessageEndEvent struct {
	Message AgentMessage
}

// ToolExecutionStartEvent is emitted before a tool runs.
type ToolExecutionStartEvent struct {
	ToolCallID string
	ToolName   string
	Args       any
}

// ToolExecutionUpdateEvent carries a partial result during tool execution.
type ToolExecutionUpdateEvent struct {
	ToolCallID    string
	ToolName      string
	PartialResult AgentToolResult
}

// ToolExecutionEndEvent is emitted when a tool finishes.
type ToolExecutionEndEvent struct {
	ToolCallID string
	ToolName   string
	Result     AgentToolResult
	IsError    bool
}

// CompactionEvent is emitted when the loop compacts the context window, either
// automatically (threshold/overflow) or on an explicit /compact request. It
// carries before/after token counts and how many messages were summarized vs.
// retained. When compaction fails it is still emitted with ErrorMessage set and
// the token/count fields describing the unchanged context, so consumers can
// surface the failure without the session aborting (US-004).
type CompactionEvent struct {
	// Reason is why compaction ran: "manual", "threshold", or "overflow".
	Reason string
	// TokensBefore is the estimated context tokens prior to compaction.
	TokensBefore int
	// TokensAfter is the estimated context tokens after compaction (equals
	// TokensBefore when compaction failed or was a no-op).
	TokensAfter int
	// SummarizedCount is the number of messages folded into the summary.
	SummarizedCount int
	// KeptCount is the number of recent messages retained verbatim.
	KeptCount int
	// ErrorMessage is non-empty when compaction failed; the original context is
	// preserved in that case.
	ErrorMessage string
}

// CompactionStartEvent is emitted immediately before the loop runs compaction,
// so a front-end can show an in-progress "Compacting conversation…" indicator
// while the summarization request is in flight. The matching CompactionEvent is
// emitted when it completes (or fails). Reason mirrors CompactionEvent.Reason.
type CompactionStartEvent struct {
	// Reason is why compaction is running: "manual", "threshold", or "overflow".
	Reason string
	// TokensBefore is the estimated context tokens that triggered compaction.
	TokensBefore int
}

// SubAgentProgressEvent carries structured progress from a running sub-agent
// (dispatched by the task tool). It is reported at the sub-agent's tool
// execution / turn boundaries so a TUI (multi-line status panel) or headless
// mode (stderr line) can display live progress. Elapsed time is intentionally
// omitted: consumers compute it themselves (TUI from tool-start time, headless
// from when the id was first seen) to avoid emitting an event per frame.
type SubAgentProgressEvent struct {
	// ToolCallID is the parent task call's tool-call id, used as the key for
	// the status line.
	ToolCallID string
	// Description is the task call's description, for display (may be empty).
	Description string
	// Activity is the current activity: tool name / phase, e.g. "Editing",
	// "Running bash", "Thinking".
	Activity string
	// Tokens is the estimated sub-agent output token count (0 = unknown).
	Tokens int
}

// ToolTiming records how long one tool invocation took, keyed by tool name in
// TelemetryEvent.ToolDurationsMs. It aggregates repeated calls of the same tool
// so a summary stays compact regardless of turn count.
type ToolTiming struct {
	// Count is how many times the tool was invoked over the run.
	Count int
	// TotalMs is the summed wall-clock duration of every invocation, in
	// milliseconds.
	TotalMs int64
}

// TelemetryEvent is a lightweight, additive observability summary emitted once
// at run end (just before agent_end) so scripts consuming the stream-json
// output can read structured metrics without a new dependency (no
// Prometheus/OTLP). It is purely observational: consumers that ignore it behave
// exactly as before. Metrics covered (observability — structured telemetry collection):
//   - per-tool wall-clock durations (ToolDurationsMs, aggregated by tool name),
//   - how many turns ran (Turns),
//   - how many assistant responses were truncated by the output cap
//     (TruncationCount),
//   - how many times the context was compacted (CompactionCount),
//   - the latest context-utilization ratio (ContextUtilization = used tokens /
//     ContextWindow) and the raw numbers behind it.
type TelemetryEvent struct {
	// Turns is the number of turns (turn_start events) the run executed.
	Turns int
	// ToolDurationsMs maps a tool name to its aggregated timing over the run.
	ToolDurationsMs map[string]ToolTiming
	// TruncationCount is how many assistant responses stopped with reason
	// "length" (truncated by the output token cap), each triggering a resend.
	TruncationCount int
	// CompactionCount is how many successful context compactions occurred.
	CompactionCount int
	// ContextUtilization is the latest used/window ratio in [0,1], or 0 when the
	// context window is unknown. Computed as ContextTokens / ContextWindow.
	ContextUtilization float64
	// ContextTokens is the most recently observed estimated context-token usage.
	ContextTokens int
	// ContextWindow is the model's total context-token budget (0 when unknown).
	ContextWindow int
}

func (AgentStartEvent) isAgentEvent()          {}
func (AgentEndEvent) isAgentEvent()            {}
func (TurnStartEvent) isAgentEvent()           {}
func (TurnEndEvent) isAgentEvent()             {}
func (MessageStartEvent) isAgentEvent()        {}
func (MessageUpdateEvent) isAgentEvent()       {}
func (MessageEndEvent) isAgentEvent()          {}
func (ToolExecutionStartEvent) isAgentEvent()  {}
func (ToolExecutionUpdateEvent) isAgentEvent() {}
func (ToolExecutionEndEvent) isAgentEvent()    {}
func (CompactionEvent) isAgentEvent()          {}
func (CompactionStartEvent) isAgentEvent()     {}
func (TelemetryEvent) isAgentEvent()           {}
func (SubAgentProgressEvent) isAgentEvent()    {}

func (AgentStartEvent) EventType() string          { return EventAgentStart }
func (AgentEndEvent) EventType() string            { return EventAgentEnd }
func (TurnStartEvent) EventType() string           { return EventTurnStart }
func (TurnEndEvent) EventType() string             { return EventTurnEnd }
func (MessageStartEvent) EventType() string        { return EventMessageStart }
func (MessageUpdateEvent) EventType() string       { return EventMessageUpdate }
func (MessageEndEvent) EventType() string          { return EventMessageEnd }
func (ToolExecutionStartEvent) EventType() string  { return EventToolExecutionStart }
func (ToolExecutionUpdateEvent) EventType() string { return EventToolExecutionUpdate }
func (ToolExecutionEndEvent) EventType() string    { return EventToolExecutionEnd }
func (CompactionEvent) EventType() string          { return EventCompaction }
func (CompactionStartEvent) EventType() string     { return EventCompactionStart }
func (TelemetryEvent) EventType() string           { return EventTelemetry }
func (SubAgentProgressEvent) EventType() string    { return EventSubAgentProgress }
