package config

import (
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/viper"
)

type Config struct {
	Server        ServerConfig   `mapstructure:"server"`
	Database      DatabaseConfig `mapstructure:"database"`
	Providers     ProviderConfig `mapstructure:"providers"`
	EncryptionKey string         `mapstructure:"encryption_key"`
	KeyBytes      []byte
}

type ServerConfig struct {
	Port            int      `mapstructure:"port"`
	Host            string   `mapstructure:"host"`
	AuthTokens      []string `mapstructure:"auth_tokens"`
	WSAllowedOrigin string   `mapstructure:"ws_allowed_origin"`
}

type DatabaseConfig struct {
	Path           string `mapstructure:"path"`
	MaxConnections int    `mapstructure:"max_connections"`
}

type ProviderConfig struct {
	OpenAI   OpenAIConfig   `mapstructure:"openai"`
	Gemini   GeminiConfig   `mapstructure:"gemini"`
	DeepSeek DeepSeekConfig `mapstructure:"deepseek"`
	Moonshot MoonshotConfig `mapstructure:"moonshot"`
	Grok     GrokConfig     `mapstructure:"grok"`
	Ollama   OllamaConfig   `mapstructure:"ollama"`
}

type OpenAIConfig struct {
	APIKeyEnv    string `mapstructure:"api_key_env"`
	BaseURL      string `mapstructure:"base_url"`
	DefaultModel string `mapstructure:"default_model"`
	Enabled      bool   `mapstructure:"enabled"`
}

type GeminiConfig struct {
	APIKeyEnv    string `mapstructure:"api_key_env"`
	BaseURL      string `mapstructure:"base_url"`
	DefaultModel string `mapstructure:"default_model"`
	Enabled      bool   `mapstructure:"enabled"`
}

type DeepSeekConfig struct {
	APIKeyEnv    string `mapstructure:"api_key_env"`
	BaseURL      string `mapstructure:"base_url"`
	DefaultModel string `mapstructure:"default_model"`
	Enabled      bool   `mapstructure:"enabled"`
}

type MoonshotConfig struct {
	APIKeyEnv    string `mapstructure:"api_key_env"`
	BaseURL      string `mapstructure:"base_url"`
	DefaultModel string `mapstructure:"default_model"`
	Enabled      bool   `mapstructure:"enabled"`
}

type GrokConfig struct {
	APIKeyEnv    string `mapstructure:"api_key_env"`
	BaseURL      string `mapstructure:"base_url"`
	DefaultModel string `mapstructure:"default_model"`
	Enabled      bool   `mapstructure:"enabled"`
}

type OllamaConfig struct {
	BaseURL      string   `mapstructure:"base_url"`
	Enabled      bool     `mapstructure:"enabled"`
	DefaultModel string   `mapstructure:"default_model"`
	Models       []string `mapstructure:"models"`
}

func Load() (*Config, error) {
	v := viper.New()

	// Defaults
	v.SetDefault("server.port", 8080)
	v.SetDefault("server.host", "0.0.0.0")
	v.SetDefault("server.auth_tokens", []string{})
	v.SetDefault("database.path", "./data/llm_proxy.db")
	v.SetDefault("database.max_connections", 10)

	v.SetDefault("providers.openai.api_key_env", "OPENAI_API_KEY")
	v.SetDefault("providers.openai.base_url", "https://api.openai.com/v1")
	v.SetDefault("providers.openai.default_model", "gpt-4o")
	v.SetDefault("providers.openai.enabled", true)

	v.SetDefault("providers.gemini.api_key_env", "GEMINI_API_KEY")
	v.SetDefault("providers.gemini.base_url", "https://generativelanguage.googleapis.com/v1")
	v.SetDefault("providers.gemini.default_model", "gemini-2.0-flash")
	v.SetDefault("providers.gemini.enabled", true)

	v.SetDefault("providers.deepseek.api_key_env", "DEEPSEEK_API_KEY")
	v.SetDefault("providers.deepseek.base_url", "https://api.deepseek.com")
	v.SetDefault("providers.deepseek.default_model", "deepseek-reasoner")
	v.SetDefault("providers.deepseek.enabled", true)

	v.SetDefault("providers.moonshot.api_key_env", "MOONSHOT_API_KEY")
	v.SetDefault("providers.moonshot.base_url", "https://api.moonshot.ai/v1")
	v.SetDefault("providers.moonshot.default_model", "kimi-k2.5")
	v.SetDefault("providers.moonshot.enabled", true)

	v.SetDefault("providers.grok.api_key_env", "GROK_API_KEY")
	v.SetDefault("providers.grok.base_url", "https://api.x.ai/v1")
	v.SetDefault("providers.grok.default_model", "grok-4-1-fast-non-reasoning")
	v.SetDefault("providers.grok.enabled", true)

	v.SetDefault("providers.ollama.base_url", "http://localhost:11434/v1")
	v.SetDefault("providers.ollama.enabled", false)
	v.SetDefault("providers.ollama.models", []string{})

	// Environment variables
	v.SetEnvPrefix("LLM_PROXY")
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "__"))
	v.AutomaticEnv()

	// Explicitly bind keys that might use double underscores in .env
	v.BindEnv("encryption_key", "LLM_PROXY__ENCRYPTION_KEY")
	v.BindEnv("server.port", "LLM_PROXY__SERVER__PORT")
	v.BindEnv("server.host", "LLM_PROXY__SERVER__HOST")
	v.BindEnv("providers.ollama.enabled", "LLM_PROXY__PROVIDERS__OLLAMA__ENABLED")
	v.BindEnv("providers.ollama.base_url", "LLM_PROXY__PROVIDERS__OLLAMA__BASE_URL")
	v.BindEnv("providers.ollama.models", "LLM_PROXY__PROVIDERS__OLLAMA__MODELS")

	// Config file
	v.SetConfigName("config")
	v.SetConfigType("toml")
	v.AddConfigPath(".")
	if envPath := os.Getenv("LLM_PROXY__CONFIG_PATH"); envPath != "" {
		v.SetConfigFile(envPath)
	}

	if err := v.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return nil, fmt.Errorf("failed to read config file: %w", err)
		}
	}

	var cfg Config
	if err := v.Unmarshal(&cfg); err != nil {
		return nil, fmt.Errorf("failed to unmarshal config: %w", err)
	}

	// Manual overrides for nested keys which Viper doesn't always bind correctly with AutomaticEnv + SetEnvPrefix
	if port := os.Getenv("LLM_PROXY__SERVER__PORT"); port != "" {
		fmt.Sscanf(port, "%d", &cfg.Server.Port)

	}
	if host := os.Getenv("LLM_PROXY__SERVER__HOST"); host != "" {
		cfg.Server.Host = host

	}

	// Ollama overrides
	if enabled := os.Getenv("LLM_PROXY__PROVIDERS__OLLAMA__ENABLED"); enabled != "" {
		cfg.Providers.Ollama.Enabled = enabled == "true"
	}
	if baseURL := os.Getenv("LLM_PROXY__PROVIDERS__OLLAMA__BASE_URL"); baseURL != "" {
		cfg.Providers.Ollama.BaseURL = baseURL
	}
	if models := os.Getenv("LLM_PROXY__PROVIDERS__OLLAMA__MODELS"); models != "" {
		cfg.Providers.Ollama.Models = strings.Split(models, ",")
	}

	// Validate encryption key
	if cfg.EncryptionKey == "" {
		return nil, fmt.Errorf("encryption key is required (LLM_PROXY__ENCRYPTION_KEY)")
	}

	keyBytes, err := hex.DecodeString(cfg.EncryptionKey)
	if err != nil {
		keyBytes, err = base64.StdEncoding.DecodeString(cfg.EncryptionKey)
		if err != nil {
			return nil, fmt.Errorf("encryption key must be hex or base64 encoded")
		}
	}

	if len(keyBytes) != 32 {
		return nil, fmt.Errorf("encryption key must be 32 bytes, got %d", len(keyBytes))
	}
	cfg.KeyBytes = keyBytes

	return &cfg, nil
}

func (c *Config) GetAPIKey(provider string) (string, error) {
	var envVar string
	switch provider {
	case "openai":
		envVar = c.Providers.OpenAI.APIKeyEnv
	case "gemini":
		envVar = c.Providers.Gemini.APIKeyEnv
	case "deepseek":
		envVar = c.Providers.DeepSeek.APIKeyEnv
	case "moonshot":
		envVar = c.Providers.Moonshot.APIKeyEnv
	case "grok":
		envVar = c.Providers.Grok.APIKeyEnv
	case "ollama":
		// Ollama doesn't require an API key
		return "", nil
	default:
		return "", fmt.Errorf("unknown provider: %s", provider)
	}

	val := os.Getenv(envVar)
	if val == "" {
		return "", fmt.Errorf("environment variable %s not set for %s", envVar, provider)
	}
	return strings.TrimSpace(val), nil
}
