package domain

import (
	"encoding/json"
	"time"
)

// TaskEventType represents the type of task event.
type TaskEventType string

const (
	// TaskEventQueued is fired when a task is added to the queue.
	TaskEventQueued TaskEventType = "queued"
	// TaskEventStarted is fired when a task begins execution.
	TaskEventStarted TaskEventType = "started"
	// TaskEventCompleted is fired when a task finishes successfully.
	TaskEventCompleted TaskEventType = "completed"
	// TaskEventFailed is fired when a task fails.
	TaskEventFailed TaskEventType = "failed"
	// TaskEventCancelled is fired when a task is cancelled.
	TaskEventCancelled TaskEventType = "cancelled"
	// TaskEventRetrying is fired when a task is being retried.
	TaskEventRetrying TaskEventType = "retrying"
	// TaskEventCheckpointSaved is fired when a checkpoint is saved.
	TaskEventCheckpointSaved TaskEventType = "checkpoint_saved"
	// TaskEventGPUAssigned is fired when a GPU is assigned.
	TaskEventGPUAssigned TaskEventType = "gpu_assigned"
)

// TaskEvent represents an event in a task's lifecycle.
// Events are stored in Redis Streams for append-only audit trails.
type TaskEvent struct {
	Timestamp time.Time       `json:"timestamp"`
	TaskID    string          `json:"task_id"`
	EventType TaskEventType   `json:"event_type"`
	Who       string          `json:"who"`
	Data      json.RawMessage `json:"data,omitempty"`
}

// EventDataStarted contains data for the "started" event.
type EventDataStarted struct {
	WorkerID   string   `json:"worker_id"`
	Image      string   `json:"image,omitempty"`
	GPUDevices []string `json:"gpu_devices,omitempty"`
}

// EventDataFailed contains data for the "failed" event.
type EventDataFailed struct {
	Error       string `json:"error"`
	Phase       string `json:"phase"`
	Recoverable bool   `json:"recoverable"`
}

// EventDataGPUAssigned contains data for the "gpu_assigned" event.
type EventDataGPUAssigned struct {
	GPUEnvVar  string   `json:"gpu_env_var,omitempty"`
	GPUDevices []string `json:"gpu_devices"`
}

// NewTaskEvent creates a new task event with the current timestamp.
func NewTaskEvent(taskID string, eventType TaskEventType, who string) TaskEvent {
	return TaskEvent{
		TaskID:    taskID,
		EventType: eventType,
		Timestamp: time.Now().UTC(),
		Who:       who,
	}
}

// WithData adds data to the event.
func (e TaskEvent) WithData(data any) (TaskEvent, error) {
	encoded, err := json.Marshal(data)
	if err != nil {
		return e, err
	}
	e.Data = encoded
	return e, nil
}

// ParseData parses the event data into the provided type.
func (e TaskEvent) ParseData(out any) error {
	if len(e.Data) == 0 {
		return nil
	}
	return json.Unmarshal(e.Data, out)
}
