package runtimeevents

import (
	"crypto/rand"
	"encoding/hex"
	"time"
)

// Kind is a stable, dot-separated event name (e.g. "wake.received").
type Kind string

// Severity classifies an event for log/dashboard filtering.
type Severity string

const (
	SeverityDebug Severity = "debug"
	SeverityInfo  Severity = "info"
	SeverityWarn  Severity = "warn"
	SeverityError Severity = "error"
)

// Event is the daemon-internal runtime event envelope.
//
// The envelope carries cross-kind metadata for routing, filtering, and
// correlation. Kind-specific data lives in the Data field as a typed
// EventData implementation.
type Event struct {
	ID       string    `json:"id"`
	Time     time.Time `json:"time"`
	Kind     Kind      `json:"kind"`
	Severity Severity  `json:"severity,omitempty"`

	Context EventContext `json:"context,omitempty"`
	Trace   EventTrace   `json:"trace,omitempty"`

	Data EventData `json:"data,omitempty"`
}

// EventContext holds who/where/how metadata for routing and filtering.
type EventContext struct {
	AgentID       string `json:"agent_id,omitempty"`
	ServerAgentID string `json:"server_agent_id,omitempty"`
	ServerID      string `json:"server_id,omitempty"`
	SessionID     string `json:"session_id,omitempty"`
	Runtime       string `json:"runtime,omitempty"`
	Model         string `json:"model,omitempty"`
	WorkDir       string `json:"work_dir,omitempty"`
	PID           int    `json:"pid,omitempty"`
}

// EventTrace holds correlation fields for linking events across a
// wake/turn/message chain.
type EventTrace struct {
	MessageID      string `json:"message_id,omitempty"`
	WakeToken      string `json:"wake_token,omitempty"`
	Target         string `json:"target,omitempty"`
	ReplyMessageID string `json:"reply_message_id,omitempty"`
	TurnID         string `json:"turn_id,omitempty"`
	CorrelationID  string `json:"correlation_id,omitempty"`
	CauseID        string `json:"cause_id,omitempty"`
}

// NewEvent creates a new Event with a random ID and the current time.
func NewEvent(kind Kind, opts ...EventOption) Event {
	ev := Event{
		ID:   newEventID(),
		Time: time.Now(),
		Kind: kind,
	}
	for _, opt := range opts {
		opt(&ev)
	}
	return ev
}

// NewDataEvent creates a new Event with its Kind derived from the EventData.
func NewDataEvent(data EventData, opts ...EventOption) Event {
	ev := Event{
		ID:   newEventID(),
		Time: time.Now(),
		Kind: data.EventKind(),
		Data: data,
	}
	for _, opt := range opts {
		opt(&ev)
	}
	return ev
}

func newEventID() string {
	var b [8]byte
	_, _ = rand.Read(b[:])
	return hex.EncodeToString(b[:])
}

// --- Kind constants ---------------------------------------------------------

// Session lifecycle
const (
	KindSessionStarting     Kind = "runtime.session.starting"
	KindSessionStarted      Kind = "runtime.session.started"
	KindSessionSleeping     Kind = "runtime.session.sleeping"
	KindSessionStopped      Kind = "runtime.session.stopped"
	KindSessionStateChanged Kind = "runtime.session.state_changed"
)

// Agent catalog
const (
	KindAgentCatalogChanged Kind = "agent.catalog.changed"
)

// Delivery
const (
	KindDeliveryAccepted Kind = "delivery.accepted"
	KindDeliveryDeferred Kind = "delivery.deferred"
	KindDeliveryPending  Kind = "delivery.pending"
	KindDeliveryAcked    Kind = "delivery.surfaced"
	KindDeliveryReplayed Kind = "delivery.replayed"
)

// Wake-to-reply
const (
	KindWakeReceived  Kind = "wake.received"
	KindWakeReplySent Kind = "wake.reply_sent"
	KindWakeNoReply   Kind = "wake.completed_without_visible_reply"
	KindWakeFailed    Kind = "wake.failed"
)

// Turn-level events
const (
	KindTurnSessionInit      Kind = "turn.session_init"
	KindTurnThinking         Kind = "turn.thinking"
	KindTurnText             Kind = "turn.text"
	KindTurnToolCall         Kind = "turn.tool_call"
	KindTurnToolOutput       Kind = "turn.tool_output"
	KindTurnSubagentActivity Kind = "turn.subagent_activity"
	KindTurnEnded            Kind = "turn.ended"
	KindTurnIdle             Kind = "turn.idle"
	KindTurnCompacting       Kind = "turn.compacting"
	KindTurnCompacted        Kind = "turn.compacted"
	KindTurnError            Kind = "turn.error"
	KindPromptInput          Kind = "turn.prompt_input"
)

// Recovery
const (
	KindRecoveryPointPrepared    Kind = "recovery.point_prepared"
	KindRecoveryRestoreStarted   Kind = "recovery.restore_started"
	KindRecoveryRestoreCompleted Kind = "recovery.restore_completed"
	KindRecoveryGCCompleted      Kind = "recovery.gc_completed"
	KindContextWindowExceeded    Kind = "recovery.context_window_exceeded"
)

// Usage & server message
const (
	KindUsageTokens           Kind = "usage.tokens"
	KindServerMessageReceived Kind = "server.message.received"
	KindServerMessageSent     Kind = "server.message.sent"
)

// Runtime diagnostics
const (
	KindRuntimeStartFailed         Kind = "runtime.start_failed"
	KindRuntimeExitedUnexpectedly  Kind = "runtime.exited_unexpectedly"
	KindRuntimeEventParseFailed    Kind = "runtime.event_parse_failed"
	KindRuntimeRetrying            Kind = "runtime.retrying"
	KindRuntimeResumeSafetyChanged Kind = "runtime.resume_safety_changed"
)

// Heartbeat
const (
	KindRuntimeHeartbeat      Kind = "runtime.heartbeat"
	KindRuntimeHeartbeatStale Kind = "runtime.heartbeat_stale"
)

// Bus self-diagnostics
const (
	KindBusSubscriberDropped Kind = "eventbus.subscriber_dropped"
	KindBusSinkFailed        Kind = "eventbus.sink_failed"
)
