// Package domain contains pure domain models with no external dependencies.
package domain

import "time"

// AgentProvider identifies which code agent implementation to use.
type AgentProvider string

const (
	// AgentProviderClaudeCode uses Anthropic's Claude Code CLI.
	AgentProviderClaudeCode AgentProvider = "claudecode"

	// AgentProviderOpenCode uses the open-source OpenCode agent.
	AgentProviderOpenCode AgentProvider = "opencode"
)

// ValidAgentProviders returns all valid agent provider values.
func ValidAgentProviders() []AgentProvider {
	return []AgentProvider{AgentProviderClaudeCode, AgentProviderOpenCode}
}

// IsValid returns true if the provider is a known valid value.
func (p AgentProvider) IsValid() bool {
	switch p {
	case AgentProviderClaudeCode, AgentProviderOpenCode:
		return true
	default:
		return false
	}
}

// String returns the string representation of the provider.
func (p AgentProvider) String() string {
	return string(p)
}

// ParseAgentProvider converts a string to AgentProvider with validation.
// Returns empty provider and error if the string is not a valid provider.
func ParseAgentProvider(s string) (AgentProvider, error) {
	p := AgentProvider(s)
	if !p.IsValid() {
		return "", ErrInvalidAgentProvider
	}
	return p, nil
}

// AgentRequest contains parameters for executing a code agent command.
type AgentRequest struct {
	// Prompt is the user's instruction to the agent.
	Prompt string

	// ProjectID identifies the project context.
	ProjectID ProjectID

	// SessionID enables conversation continuation. Empty for new sessions.
	SessionID string

	// AllowedTools specifies which tools the agent may use without prompting.
	// Uses permission rule syntax (e.g., "Bash", "Read", "Bash(git:*)").
	AllowedTools []string

	// Model specifies which LLM to use. Provider-specific.
	// For Claude Code: ignored (uses Claude).
	// For OpenCode: e.g., "claude-sonnet-4-20250514", "gpt-4o".
	Model string

	// WorkingDir is the directory context for the agent. Defaults to /workspace.
	WorkingDir string

	// Timeout is the maximum execution time. Zero means use default.
	Timeout time.Duration

	// Metadata contains additional provider-specific options.
	Metadata map[string]string
}

// Validate checks the AgentRequest for required fields and valid values.
// Returns an error describing the validation failure, or nil if valid.
func (r *AgentRequest) Validate() error {
	if r.Prompt == "" {
		return ErrPromptRequired
	}
	if r.Timeout < 0 {
		return ErrInvalidTimeout
	}
	return nil
}

// AgentEventType categorizes events emitted during agent execution.
type AgentEventType string

const (
	// AgentEventOutput is text output from the agent (stdout equivalent).
	AgentEventOutput AgentEventType = "output"

	// AgentEventToolUse indicates the agent is invoking a tool.
	AgentEventToolUse AgentEventType = "tool_use"

	// AgentEventToolResult contains the result of a tool invocation.
	AgentEventToolResult AgentEventType = "tool_result"

	// AgentEventThinking indicates the agent is processing/reasoning.
	AgentEventThinking AgentEventType = "thinking"

	// AgentEventError indicates an error occurred.
	AgentEventError AgentEventType = "error"

	// AgentEventComplete indicates execution finished.
	AgentEventComplete AgentEventType = "complete"
)

// AgentEvent represents a single event during agent execution.
type AgentEvent struct {
	// Type categorizes this event.
	Type AgentEventType

	// Timestamp when the event occurred.
	Timestamp time.Time

	// Content is the main payload (text output, tool name, error message).
	Content string

	// Stream identifies the output stream ("stdout", "stderr", or empty).
	Stream string

	// ToolName is set for tool_use and tool_result events.
	ToolName string

	// ToolInput contains the tool invocation arguments (for tool_use).
	ToolInput map[string]any

	// Metadata contains additional event-specific data.
	Metadata map[string]any
}

// AgentEventHandler is a callback for receiving agent events during execution.
type AgentEventHandler func(event AgentEvent)

// AgentResult contains the outcome of agent execution.
type AgentResult struct {
	// SessionID identifies this conversation for potential continuation.
	SessionID string

	// ExitCode is 0 for success, non-zero for failure.
	ExitCode int

	// DurationMs is the total execution time in milliseconds.
	DurationMs int64

	// Error contains any execution error (nil on success).
	Error error

	// TokensUsed tracks token consumption (if available from provider).
	TokensUsed *AgentTokenUsage

	// FinalOutput contains the agent's final text response (if any).
	FinalOutput string
}

// Success returns true if the agent completed successfully.
func (r *AgentResult) Success() bool {
	return r.Error == nil && r.ExitCode == 0
}

// AgentTokenUsage tracks token consumption during execution.
type AgentTokenUsage struct {
	InputTokens  int64
	OutputTokens int64
	TotalTokens  int64
}

// AgentCapabilities describes what a code agent implementation supports.
type AgentCapabilities struct {
	// Provider identifies this agent implementation.
	Provider AgentProvider

	// SupportsSessionContinuation indicates --resume/session support.
	SupportsSessionContinuation bool

	// SupportsModelSelection indicates the model can be changed.
	SupportsModelSelection bool

	// SupportsToolControl indicates --allowedTools support.
	SupportsToolControl bool

	// SupportedModels lists available models (empty if not applicable).
	SupportedModels []string

	// DefaultModel is used when none is specified.
	DefaultModel string

	// MaxPromptLength is the maximum prompt size (0 = unlimited).
	MaxPromptLength int

	// SupportsStreaming indicates real-time output streaming.
	SupportsStreaming bool
}
