// Copyright Contributors to the KubeOpenCode project

package v1alpha1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TaskPhase represents the current phase of a task
// +kubebuilder:validation:Enum=Pending;Queued;Running;Completed;Failed
type TaskPhase string

const (
	// TaskPhasePending means the task has not started yet
	TaskPhasePending TaskPhase = "Pending"
	// TaskPhaseQueued means the task is waiting for Agent capacity.
	// This occurs when the Agent has maxConcurrentTasks set and the limit is reached.
	// The task will automatically transition to Running when capacity becomes available.
	TaskPhaseQueued TaskPhase = "Queued"
	// TaskPhaseRunning means the task is currently executing
	TaskPhaseRunning TaskPhase = "Running"
	// TaskPhaseCompleted means the task execution finished (Pod exited with code 0).
	// This indicates the agent completed its work, not necessarily that the task "succeeded".
	// The actual outcome should be determined by examining the agent's output.
	TaskPhaseCompleted TaskPhase = "Completed"
	// TaskPhaseFailed means the task had a failure
	// (e.g., Pod crashed, unable to schedule, missing Agent, or application error).
	TaskPhaseFailed TaskPhase = "Failed"
)

const (
	// ConditionTypeReady is the condition type for Task readiness
	ConditionTypeReady = "Ready"
	// ConditionTypeQueued is the condition type for Task queuing
	ConditionTypeQueued = "Queued"
	// ConditionTypeStopped is the condition type for Task stop
	ConditionTypeStopped = "Stopped"
	// ReasonAgentError is the reason for Agent errors
	ReasonAgentError = "AgentError"
	// ReasonAgentAtCapacity is the reason for Agent capacity limit
	ReasonAgentAtCapacity = "AgentAtCapacity"
	// ReasonQuotaExceeded is the reason for Agent quota limit
	ReasonQuotaExceeded = "QuotaExceeded"
	// ReasonContextError is the reason for Context errors
	ReasonContextError = "ContextError"
	// ReasonUserStopped is the reason for user-initiated stop
	ReasonUserStopped = "UserStopped"
	// ReasonNoLimits is the reason for no limits configured
	ReasonNoLimits = "NoLimits"
	// ReasonCapacityAvailable is the reason for capacity availability
	ReasonCapacityAvailable = "CapacityAvailable"
	// ReasonPodCreationError is the reason for Pod creation failures
	ReasonPodCreationError = "PodCreationError"
	// ReasonConfigMapCreationError is the reason for ConfigMap creation failures
	ReasonConfigMapCreationError = "ConfigMapCreationError"
	// ReasonPermissionRequired is the reason when agent needs permission approval
	ReasonPermissionRequired = "PermissionRequired"
	// ReasonQuestionAsked is the reason when agent asks a question
	ReasonQuestionAsked = "QuestionAsked"
	// ReasonAgentSuspended is the reason when agent is suspended (scaled to 0)
	ReasonAgentSuspended = "AgentSuspended"
	// ReasonAgentServerNotReady is the reason when agent server is not yet ready
	ReasonAgentServerNotReady = "AgentServerNotReady"
	// ReasonTimeout is the reason when a task is stopped due to exceeding its timeout
	ReasonTimeout = "Timeout"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope="Namespaced",shortName=tk
// +kubebuilder:printcolumn:JSONPath=`.status.phase`,name="Phase",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.agentRef.name`,name="Agent",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.templateRef.name`,name="Template",type=string,priority=1
// +kubebuilder:printcolumn:JSONPath=`.status.podName`,name="Pod",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date

// Task represents a single task execution.
// Task is the primary API for users who want to execute AI-powered tasks.
type Task struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Spec defines the desired state of Task
	Spec TaskSpec `json:"spec"`

	// Status represents the current status of the Task
	// +optional
	Status TaskExecutionStatus `json:"status,omitempty"`
}

// AgentReference specifies which Agent to use for task execution.
// The Agent must be in the same namespace as the Task.
type AgentReference struct {
	// Name of the Agent.
	// +required
	Name string `json:"name"`
}

// TaskSpec defines the Task configuration.
// Exactly one of agentRef or templateRef must be set.
//
// +kubebuilder:validation:XValidation:rule="has(self.agentRef) || has(self.templateRef)",message="either agentRef or templateRef must be specified"
// +kubebuilder:validation:XValidation:rule="!(has(self.agentRef) && has(self.templateRef))",message="only one of agentRef or templateRef can be specified"
type TaskSpec struct {
	// Description is the task instruction/prompt.
	// The controller creates ${WORKSPACE_DIR}/task.md with this content
	// (where WORKSPACE_DIR is configured in Agent.spec.workspaceDir, defaulting to "/workspace").
	// This is the primary way to tell the agent what to do.
	//
	// Example:
	//   description: "Update all dependencies and create a PR"
	// +optional
	Description *string `json:"description,omitempty"`

	// Contexts provides additional context for the task.
	// Contexts are processed in array order, with later contexts taking precedence.
	//
	// Context priority (lowest to highest):
	//   1. Agent/Template contexts (defaults)
	//   2. Task.contexts (Task-specific contexts)
	//   3. Task.description (highest, becomes ${WORKSPACE_DIR}/task.md)
	//
	// Example:
	//   contexts:
	//     - type: Text
	//       text: "Always use conventional commits"
	//     - type: Git
	//       mountPath: src
	//       git:
	//         repository: https://github.com/org/repo
	//         ref: main
	// +optional
	Contexts []ContextItem `json:"contexts,omitempty"`

	// AgentRef references a running Agent in the same namespace.
	// The Task creates a lightweight Pod that connects to the Agent's server
	// via `opencode run --attach`.
	// Exactly one of agentRef or templateRef must be set.
	// +optional
	AgentRef *AgentReference `json:"agentRef,omitempty"`

	// TemplateRef references an AgentTemplate in the same namespace.
	// The Task creates an ephemeral Pod using the template's configuration.
	// The Pod runs standalone `opencode run` and has the same lifecycle as the Task.
	// Exactly one of agentRef or templateRef must be set.
	// +optional
	TemplateRef *AgentTemplateReference `json:"templateRef,omitempty"`

	// Timeout specifies the maximum duration for task execution.
	// The timeout clock starts when the Task enters the Running phase (status.startTime),
	// not when the Task is created. Queue time (Pending/Queued phases) is excluded.
	//
	// If the Task is still running after this duration, the controller stops it
	// by deleting the Pod (SIGTERM). The Task transitions to Completed with
	// condition Stopped, reason "Timeout".
	//
	// If not set, the Task runs indefinitely (no timeout).
	//
	// Example: "30m", "1h", "2h30m"
	// +optional
	Timeout *metav1.Duration `json:"timeout,omitempty"`
}

// SessionInfo contains information about the OpenCode session associated with a Task.
// This enables correlation between Kubernetes Tasks and OpenCode conversation sessions.
type SessionInfo struct {
	// ID is the OpenCode session ID (e.g., "ses_ff34a1b2...").
	// +optional
	ID string `json:"id,omitempty"`

	// Title is the session title used when creating the session.
	// Format: "kubeopencode/<namespace>/<task-name>"
	// +optional
	Title string `json:"title,omitempty"`

	// Summary is populated when the Task completes with session statistics.
	// +optional
	Summary *SessionSummary `json:"summary,omitempty"`
}

// SessionSummary contains aggregated statistics from an OpenCode session.
type SessionSummary struct {
	// MessageCount is the total number of messages in the session.
	// +optional
	MessageCount int32 `json:"messageCount,omitempty"`

	// TokenUsage is the total token consumption.
	// +optional
	TokenUsage *TokenUsage `json:"tokenUsage,omitempty"`

	// Cost is the total estimated cost in USD (as string for precision).
	// +optional
	Cost string `json:"cost,omitempty"`

	// FilesChanged is the number of files modified during the session.
	// +optional
	FilesChanged int32 `json:"filesChanged,omitempty"`

	// Additions is the total lines added.
	// +optional
	Additions int32 `json:"additions,omitempty"`

	// Deletions is the total lines deleted.
	// +optional
	Deletions int32 `json:"deletions,omitempty"`
}

// TokenUsage contains token consumption details.
type TokenUsage struct {
	// Input tokens consumed.
	// +optional
	Input int64 `json:"input,omitempty"`

	// Output tokens generated.
	// +optional
	Output int64 `json:"output,omitempty"`

	// Reasoning tokens used (for models that support reasoning).
	// +optional
	Reasoning int64 `json:"reasoning,omitempty"`

	// Cache tokens (cache hits).
	// +optional
	Cache int64 `json:"cache,omitempty"`
}

// TaskExecutionStatus defines the observed state of Task
type TaskExecutionStatus struct {
	// ObservedGeneration is the most recent generation observed by the controller.
	// +optional
	ObservedGeneration int64 `json:"observedGeneration,omitempty"`

	// Execution phase
	// +optional
	Phase TaskPhase `json:"phase,omitempty"`

	// AgentRef is the resolved Agent reference used for this task.
	// Only set when the task was created with agentRef.
	// +optional
	AgentRef *AgentReference `json:"agentRef,omitempty"`

	// TemplateRef is the resolved AgentTemplate reference used for this task.
	// Only set when the task was created with templateRef.
	// +optional
	TemplateRef *AgentTemplateReference `json:"templateRef,omitempty"`

	// Kubernetes Pod name
	// +optional
	PodName string `json:"podName,omitempty"`

	// Session contains information about the OpenCode session created for this Task.
	// Only populated for agentRef Tasks where the session can be resolved.
	// +optional
	Session *SessionInfo `json:"session,omitempty"`

	// Start time
	// +optional
	StartTime *metav1.Time `json:"startTime,omitempty"`

	// Completion time
	// +optional
	CompletionTime *metav1.Time `json:"completionTime,omitempty"`

	// Kubernetes standard conditions
	// +optional
	// +listType=map
	// +listMapKey=type
	Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// TaskList contains a list of Task
type TaskList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []Task `json:"items"`
}
