package config

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"strings"
)

// LLMFileName is the filename under the Astra config directory.
const LLMFileName = "llm.json"

type llmFileRecord struct {
	APIKey    string `json:"api_key,omitempty"`
	BaseURL   string `json:"base_url,omitempty"`
	Model     string `json:"model,omitempty"`
	FastModel string `json:"fast_model,omitempty"`
}

// LLMConfigDir returns ~/.astra (created by callers when saving).
func LLMConfigDir() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(home, ".astra"), nil
}

// LLMFilePath returns the path to the saved LLM credentials file (~/.astra/llm.json).
func LLMFilePath() (string, error) {
	dir, err := LLMConfigDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, LLMFileName), nil
}

// LoadLLMFile reads ~/.astra/llm.json if it exists. Missing file is not an error.
func LoadLLMFile() (LLMEnv, error) {
	path, err := LLMFilePath()
	if err != nil {
		return LLMEnv{}, err
	}
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return LLMEnv{}, nil
		}
		return LLMEnv{}, err
	}
	var rec llmFileRecord
	if err := json.Unmarshal(data, &rec); err != nil {
		return LLMEnv{}, err
	}
	return LLMEnv{
		APIKey:    rec.APIKey,
		BaseURL:   rec.BaseURL,
		Model:     rec.Model,
		FastModel: rec.FastModel,
	}, nil
}

// SaveLLMFile writes LLM settings to ~/.astra/llm.json with 0600 permissions.
func SaveLLMFile(cfg LLMEnv) error {
	dir, err := LLMConfigDir()
	if err != nil {
		return err
	}
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	path := filepath.Join(dir, LLMFileName)
	rec := llmFileRecord{
		APIKey:    cfg.APIKey,
		BaseURL:   cfg.BaseURL,
		Model:     cfg.Model,
		FastModel: cfg.FastModel,
	}
	data, err := json.MarshalIndent(rec, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(path, data, 0600)
}

// LoadLLMForCLI merges ~/.astra/llm.json with environment variables.
// Environment always wins when set (non-empty for keys / explicit vars).
// Model default applies only when both file and LLM_MODEL are empty.
func LoadLLMForCLI() LLMEnv {
	file, _ := LoadLLMFile()

	apiKey := firstNonEmpty(os.Getenv("LLM_API_KEY"), os.Getenv("OPENAI_API_KEY"))
	if apiKey == "" {
		apiKey = file.APIKey
	}

	baseURL := os.Getenv("LLM_BASE_URL")
	if baseURL == "" {
		baseURL = file.BaseURL
	}

	model := os.Getenv("LLM_MODEL")
	if model == "" {
		model = file.Model
	}
	if model == "" {
		model = "anthropic/claude-sonnet-4"
	}

	fastModel := os.Getenv("LLM_FAST_MODEL")
	if fastModel == "" {
		fastModel = file.FastModel
	}
	if fastModel == "" {
		lower := strings.ToLower(model)
		switch {
		case strings.Contains(lower, "opus"), strings.Contains(lower, "sonnet"):
			fastModel = "anthropic/claude-3.5-haiku"
		case strings.Contains(lower, "gpt-4o") && !strings.Contains(lower, "mini"):
			fastModel = "gpt-4o-mini"
		case strings.Contains(lower, "gpt-4.1") && !strings.Contains(lower, "mini") && !strings.Contains(lower, "nano"):
			fastModel = "gpt-4.1-mini"
		case strings.Contains(lower, "gemini-2.5-pro"):
			fastModel = strings.Replace(model, "pro", "flash", 1)
		}
	}

	return LLMEnv{
		APIKey:    apiKey,
		BaseURL:   baseURL,
		Model:     model,
		FastModel: fastModel,
	}
}
