package config

import "time"

// AuthMethod represents how Anthropic authentication is performed
type AuthMethod string

const (
	// AuthMethodAPIKey uses a traditional API key (pay-per-use billing)
	AuthMethodAPIKey AuthMethod = "api_key"
	// AuthMethodOAuth uses OAuth tokens (subscription-based, Pro/Max plans)
	AuthMethodOAuth AuthMethod = "oauth"
)

// AIConfig holds AI feature configuration
// This is stored in ai.enc (encrypted) in the config directory
type AIConfig struct {
	Enabled        bool   `json:"enabled"`
	Provider       string `json:"provider"`               // DEPRECATED: legacy single provider field, kept for migration
	APIKey         string `json:"api_key"`                // DEPRECATED: legacy single API key, kept for migration
	Model          string `json:"model"`                  // Currently selected default model (format: "provider:model-name")
	ChatModel      string `json:"chat_model,omitempty"`   // Model for interactive chat (defaults to Model)
	PatrolModel    string `json:"patrol_model,omitempty"` // Model for background patrol (defaults to Model, can be cheaper)
	BaseURL        string `json:"base_url"`               // DEPRECATED: legacy base URL, kept for migration
	AutonomousMode bool   `json:"autonomous_mode"`        // when true, AI executes commands without approval
	CustomContext  string `json:"custom_context"`         // user-provided context about their infrastructure

	// Multi-provider credentials - each provider can be configured independently
	AnthropicAPIKey string `json:"anthropic_api_key,omitempty"` // Anthropic API key
	OpenAIAPIKey    string `json:"openai_api_key,omitempty"`    // OpenAI API key
	DeepSeekAPIKey  string `json:"deepseek_api_key,omitempty"`  // DeepSeek API key
	OllamaBaseURL   string `json:"ollama_base_url,omitempty"`   // Ollama server URL (default: http://localhost:11434)
	OpenAIBaseURL   string `json:"openai_base_url,omitempty"`   // Custom OpenAI-compatible base URL (optional)

	// OAuth fields for Claude Pro/Max subscription authentication
	AuthMethod        AuthMethod `json:"auth_method,omitempty"`         // "api_key" or "oauth" (for anthropic only)
	OAuthAccessToken  string     `json:"oauth_access_token,omitempty"`  // OAuth access token (encrypted at rest)
	OAuthRefreshToken string     `json:"oauth_refresh_token,omitempty"` // OAuth refresh token (encrypted at rest)
	OAuthExpiresAt    time.Time  `json:"oauth_expires_at,omitempty"`    // Token expiration time

	// Patrol settings for background AI monitoring
	PatrolEnabled         bool   `json:"patrol_enabled"`                    // Enable background AI health patrol
	PatrolIntervalMinutes int    `json:"patrol_interval_minutes,omitempty"` // How often to run quick patrols (default: 360 = 6 hours)
	PatrolSchedulePreset  string `json:"patrol_schedule_preset,omitempty"`  // User-friendly preset: "15min", "1hr", "6hr", "12hr", "daily", "disabled"
	PatrolAnalyzeNodes    bool   `json:"patrol_analyze_nodes,omitempty"`    // Include Proxmox nodes in patrol
	PatrolAnalyzeGuests   bool   `json:"patrol_analyze_guests,omitempty"`   // Include VMs/containers in patrol
	PatrolAnalyzeDocker   bool   `json:"patrol_analyze_docker,omitempty"`   // Include Docker hosts in patrol
	PatrolAnalyzeStorage  bool   `json:"patrol_analyze_storage,omitempty"`  // Include storage in patrol
	PatrolAutoFix         bool   `json:"patrol_auto_fix,omitempty"`         // When true, patrol can attempt automatic remediation (default: false, observe only)
	AutoFixModel          string `json:"auto_fix_model,omitempty"`          // Model for automatic remediation (defaults to PatrolModel, may want more capable model)

	// Alert-triggered AI analysis - analyze specific resources when alerts fire
	AlertTriggeredAnalysis bool `json:"alert_triggered_analysis,omitempty"` // Enable AI analysis when alerts fire (token-efficient)

	// AI cost controls
	// Budget is expressed as an estimated USD amount over a 30-day window (pro-rated in UI for other ranges).
	CostBudgetUSD30d float64 `json:"cost_budget_usd_30d,omitempty"`
}

// AIProvider constants
const (
	AIProviderAnthropic = "anthropic"
	AIProviderOpenAI    = "openai"
	AIProviderOllama    = "ollama"
	AIProviderDeepSeek  = "deepseek"
)

// Default models per provider
const (
	DefaultAIModelAnthropic = "claude-opus-4-5-20251101"
	DefaultAIModelOpenAI    = "gpt-4o"
	DefaultAIModelOllama    = "llama3"
	DefaultAIModelDeepSeek  = "deepseek-chat" // V3.2 with tool-use support
	DefaultOllamaBaseURL    = "http://localhost:11434"
	DefaultDeepSeekBaseURL  = "https://api.deepseek.com/chat/completions"
)

// ModelInfo represents information about an available model
// Deprecated: Use providers.ModelInfo instead - models are now fetched dynamically from APIs
type ModelInfo struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	IsDefault   bool   `json:"is_default,omitempty"`
}

// GetAvailableModels is deprecated - models are now fetched dynamically from provider APIs
// This returns nil; use the /api/ai/models endpoint instead which queries the actual API
func GetAvailableModels(provider string) []ModelInfo {
	return nil
}

// NewDefaultAIConfig returns an AIConfig with sensible defaults
func NewDefaultAIConfig() *AIConfig {
	return &AIConfig{
		Enabled:    false,
		Provider:   AIProviderAnthropic,
		Model:      DefaultAIModelAnthropic,
		AuthMethod: AuthMethodAPIKey,
		// Patrol defaults - enabled when AI is enabled
		// Default to 6 hour intervals (much more token-efficient than 15 min)
		PatrolEnabled:         true,
		PatrolIntervalMinutes: 360, // 6 hours - balance between coverage and token efficiency
		PatrolSchedulePreset:  "6hr",
		PatrolAnalyzeNodes:    true,
		PatrolAnalyzeGuests:   true,
		PatrolAnalyzeDocker:   true,
		PatrolAnalyzeStorage:  true,
		// Alert-triggered analysis is highly token-efficient - enabled by default
		AlertTriggeredAnalysis: true,
	}
}

// IsConfigured returns true if the AI config has enough info to make API calls
// For multi-provider setup, returns true if ANY provider is configured
func (c *AIConfig) IsConfigured() bool {
	if !c.Enabled {
		return false
	}

	// Check multi-provider credentials first (new format)
	if c.HasProvider(AIProviderAnthropic) || c.HasProvider(AIProviderOpenAI) ||
		c.HasProvider(AIProviderDeepSeek) || c.HasProvider(AIProviderOllama) {
		return true
	}

	// Fall back to legacy single-provider check for backward compatibility
	switch c.Provider {
	case AIProviderAnthropic:
		if c.AuthMethod == AuthMethodOAuth {
			return c.OAuthAccessToken != ""
		}
		return c.APIKey != ""
	case AIProviderOpenAI, AIProviderDeepSeek:
		return c.APIKey != ""
	case AIProviderOllama:
		return true
	default:
		return false
	}
}

// HasProvider returns true if the specified provider has credentials configured
func (c *AIConfig) HasProvider(provider string) bool {
	switch provider {
	case AIProviderAnthropic:
		// Anthropic can use API key OR OAuth
		if c.AuthMethod == AuthMethodOAuth && c.OAuthAccessToken != "" {
			return true
		}
		return c.AnthropicAPIKey != ""
	case AIProviderOpenAI:
		return c.OpenAIAPIKey != ""
	case AIProviderDeepSeek:
		return c.DeepSeekAPIKey != ""
	case AIProviderOllama:
		// Ollama is only "configured" if user has explicitly set a base URL
		return c.OllamaBaseURL != ""
	default:
		return false
	}
}

// GetConfiguredProviders returns a list of all providers with credentials configured
func (c *AIConfig) GetConfiguredProviders() []string {
	var providers []string
	if c.HasProvider(AIProviderAnthropic) {
		providers = append(providers, AIProviderAnthropic)
	}
	if c.HasProvider(AIProviderOpenAI) {
		providers = append(providers, AIProviderOpenAI)
	}
	if c.HasProvider(AIProviderDeepSeek) {
		providers = append(providers, AIProviderDeepSeek)
	}
	if c.HasProvider(AIProviderOllama) {
		providers = append(providers, AIProviderOllama)
	}
	return providers
}

// GetAPIKeyForProvider returns the API key for the specified provider
func (c *AIConfig) GetAPIKeyForProvider(provider string) string {
	switch provider {
	case AIProviderAnthropic:
		if c.AnthropicAPIKey != "" {
			return c.AnthropicAPIKey
		}
		// Fall back to legacy API key if provider matches
		if c.Provider == AIProviderAnthropic {
			return c.APIKey
		}
	case AIProviderOpenAI:
		if c.OpenAIAPIKey != "" {
			return c.OpenAIAPIKey
		}
		if c.Provider == AIProviderOpenAI {
			return c.APIKey
		}
	case AIProviderDeepSeek:
		if c.DeepSeekAPIKey != "" {
			return c.DeepSeekAPIKey
		}
		if c.Provider == AIProviderDeepSeek {
			return c.APIKey
		}
	}
	return ""
}

// GetBaseURLForProvider returns the base URL for the specified provider
func (c *AIConfig) GetBaseURLForProvider(provider string) string {
	switch provider {
	case AIProviderOllama:
		if c.OllamaBaseURL != "" {
			return c.OllamaBaseURL
		}
		// Fall back to legacy BaseURL if provider matches
		if c.Provider == AIProviderOllama && c.BaseURL != "" {
			return c.BaseURL
		}
		return DefaultOllamaBaseURL
	case AIProviderOpenAI:
		if c.OpenAIBaseURL != "" {
			return c.OpenAIBaseURL
		}
		return "" // Uses default OpenAI URL
	case AIProviderDeepSeek:
		return DefaultDeepSeekBaseURL
	}
	return ""
}

// IsUsingOAuth returns true if OAuth authentication is configured for Anthropic
func (c *AIConfig) IsUsingOAuth() bool {
	return c.AuthMethod == AuthMethodOAuth && c.OAuthAccessToken != ""
}

// ParseModelString parses a model string in "provider:model-name" format
// Returns the provider and model name. If no provider prefix, attempts to detect.
func ParseModelString(model string) (provider, modelName string) {
	// Check for explicit provider prefix
	for _, p := range []string{AIProviderAnthropic, AIProviderOpenAI, AIProviderDeepSeek, AIProviderOllama} {
		prefix := p + ":"
		if len(model) > len(prefix) && model[:len(prefix)] == prefix {
			return p, model[len(prefix):]
		}
	}

	// No prefix - try to detect from model name patterns
	switch {
	case len(model) >= 6 && model[:6] == "claude":
		return AIProviderAnthropic, model
	case len(model) >= 3 && (model[:3] == "gpt" || model[:2] == "o1" || model[:2] == "o3" || model[:2] == "o4"):
		return AIProviderOpenAI, model
	case len(model) >= 8 && model[:8] == "deepseek":
		return AIProviderDeepSeek, model
	default:
		// Assume Ollama for unrecognized models (local models have varied names)
		return AIProviderOllama, model
	}
}

// FormatModelString creates a "provider:model-name" format string
func FormatModelString(provider, modelName string) string {
	return provider + ":" + modelName
}

// GetBaseURL returns the base URL, using defaults where appropriate
// DEPRECATED: Use GetBaseURLForProvider instead
func (c *AIConfig) GetBaseURL() string {
	if c.BaseURL != "" {
		return c.BaseURL
	}
	switch c.Provider {
	case AIProviderOllama:
		return DefaultOllamaBaseURL
	case AIProviderDeepSeek:
		return DefaultDeepSeekBaseURL
	}
	return ""
}

// GetModel returns the model, using defaults where appropriate
func (c *AIConfig) GetModel() string {
	if c.Model != "" {
		return c.Model
	}
	switch c.Provider {
	case AIProviderAnthropic:
		return DefaultAIModelAnthropic
	case AIProviderOpenAI:
		return DefaultAIModelOpenAI
	case AIProviderOllama:
		return DefaultAIModelOllama
	case AIProviderDeepSeek:
		return DefaultAIModelDeepSeek
	default:
		return ""
	}
}

// GetChatModel returns the model for interactive chat conversations
// Falls back to the main Model if ChatModel is not set
func (c *AIConfig) GetChatModel() string {
	if c.ChatModel != "" {
		return c.ChatModel
	}
	return c.GetModel()
}

// GetPatrolModel returns the model for background patrol analysis
// Falls back to the main Model if PatrolModel is not set
func (c *AIConfig) GetPatrolModel() string {
	if c.PatrolModel != "" {
		return c.PatrolModel
	}
	return c.GetModel()
}

// GetAutoFixModel returns the model for automatic remediation actions
// Falls back to PatrolModel, then to the main Model if AutoFixModel is not set
// Auto-fix may warrant a more capable model since it takes actions
func (c *AIConfig) GetAutoFixModel() string {
	if c.AutoFixModel != "" {
		return c.AutoFixModel
	}
	return c.GetPatrolModel()
}

// ClearOAuthTokens clears OAuth tokens (used when switching back to API key auth)
func (c *AIConfig) ClearOAuthTokens() {
	c.OAuthAccessToken = ""
	c.OAuthRefreshToken = ""
	c.OAuthExpiresAt = time.Time{}
}

// ClearAPIKey clears the API key (used when switching to OAuth auth)
func (c *AIConfig) ClearAPIKey() {
	c.APIKey = ""
}

// GetPatrolInterval returns the patrol interval as a duration
// Uses the preset if set, otherwise falls back to custom minutes
func (c *AIConfig) GetPatrolInterval() time.Duration {
	// If preset is set, use it
	if c.PatrolSchedulePreset != "" {
		switch c.PatrolSchedulePreset {
		case "15min":
			return 15 * time.Minute
		case "1hr":
			return 1 * time.Hour
		case "6hr":
			return 6 * time.Hour
		case "12hr":
			return 12 * time.Hour
		case "daily":
			return 24 * time.Hour
		case "disabled":
			return 0 // Signal that scheduled patrol is disabled
		}
	}

	// Fall back to custom minutes if set
	// BUT: If PatrolIntervalMinutes is the old default (15), migrate to new default (360 = 6hr)
	// This provides better token efficiency for existing installations
	if c.PatrolIntervalMinutes > 0 {
		// Migrate old 15-minute default to new 6-hour default
		if c.PatrolIntervalMinutes == 15 && c.PatrolSchedulePreset == "" {
			return 6 * time.Hour
		}
		return time.Duration(c.PatrolIntervalMinutes) * time.Minute
	}

	return 6 * time.Hour // default to 6 hours
}

// PresetToMinutes converts a patrol schedule preset to minutes
func PresetToMinutes(preset string) int {
	switch preset {
	case "15min":
		return 15
	case "1hr":
		return 60
	case "6hr":
		return 360
	case "12hr":
		return 720
	case "daily":
		return 1440
	case "disabled":
		return 0
	default:
		return 360 // default 6hr
	}
}

// IsPatrolEnabled returns true if patrol should run
// Note: Patrol uses local heuristics and doesn't require an AI API key
func (c *AIConfig) IsPatrolEnabled() bool {
	// If preset is "disabled", patrol is disabled
	if c.PatrolSchedulePreset == "disabled" {
		return false
	}
	return c.PatrolEnabled
}

// IsAlertTriggeredAnalysisEnabled returns true if AI should analyze resources when alerts fire
func (c *AIConfig) IsAlertTriggeredAnalysisEnabled() bool {
	return c.AlertTriggeredAnalysis
}
