// PicoClaw - Ultra-lightweight personal AI agent
// License: MIT
//
// Copyright (c) 2026 PicoClaw contributors

package config

import (
	"encoding/json"
	"slices"
	"strings"
)

type migratable interface {
	Migrate() (*Config, error)
}

// buildModelWithProtocol constructs a model string with protocol prefix.
// If the model already contains a "/" (indicating it has a protocol prefix), it is returned as-is.
// Otherwise, the protocol prefix is added.
func buildModelWithProtocol(protocol, model string) string {
	if strings.Contains(model, "/") {
		// Model already has a protocol prefix, return as-is
		return model
	}
	return protocol + "/" + model
}

// v0ConvertProvidersToModelList converts the old providersConfigV0 to a slice of ModelConfig.
// This enables backward compatibility with existing configurations.
// It preserves the user's configured model from agents.defaults.model when possible.
func v0ConvertProvidersToModelList(cfg *configV0) []modelConfigV0 {
	if cfg == nil {
		return nil
	}

	// providerMigrationConfig defines how to migrate a provider from old config to new format.
	type providerMigrationConfig struct {
		// providerNames are the possible names used in agents.defaults.provider
		providerNames []string
		// protocol is the protocol prefix for the model field
		protocol string
		// buildConfig creates the ModelConfig from ProviderConfig
		buildConfig func(p providersConfigV0) (modelConfigV0, bool)
	}

	// Get user's configured provider and model
	userProvider := strings.ToLower(cfg.Agents.Defaults.Provider)
	userModel := cfg.Agents.Defaults.GetModelName()

	p := cfg.Providers

	var result []modelConfigV0

	// Track if we've applied the legacy model name fix (only for first provider)
	legacyModelNameApplied := false

	// Define migration rules for each provider
	migrations := []providerMigrationConfig{
		{
			providerNames: []string{"openai", "gpt"},
			protocol:      "openai",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.OpenAI.APIKey == "" && p.OpenAI.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "openai",
					Model:          "openai/gpt-5.4",
					APIKey:         p.OpenAI.APIKey,
					APIBase:        p.OpenAI.APIBase,
					Proxy:          p.OpenAI.Proxy,
					RequestTimeout: p.OpenAI.RequestTimeout,
					AuthMethod:     p.OpenAI.AuthMethod,
				}, true
			},
		},
		{
			providerNames: []string{"anthropic", "claude"},
			protocol:      "anthropic",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Anthropic.APIKey == "" && p.Anthropic.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "anthropic",
					Model:          "anthropic/claude-sonnet-4.6",
					APIKey:         p.Anthropic.APIKey,
					APIBase:        p.Anthropic.APIBase,
					Proxy:          p.Anthropic.Proxy,
					RequestTimeout: p.Anthropic.RequestTimeout,
					AuthMethod:     p.Anthropic.AuthMethod,
				}, true
			},
		},
		{
			providerNames: []string{"litellm"},
			protocol:      "litellm",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.LiteLLM.APIKey == "" && p.LiteLLM.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "litellm",
					Model:          "litellm/auto",
					APIKey:         p.LiteLLM.APIKey,
					APIBase:        p.LiteLLM.APIBase,
					Proxy:          p.LiteLLM.Proxy,
					RequestTimeout: p.LiteLLM.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"openrouter"},
			protocol:      "openrouter",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.OpenRouter.APIKey == "" && p.OpenRouter.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "openrouter",
					Model:          "openrouter/auto",
					APIKey:         p.OpenRouter.APIKey,
					APIBase:        p.OpenRouter.APIBase,
					Proxy:          p.OpenRouter.Proxy,
					RequestTimeout: p.OpenRouter.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"groq"},
			protocol:      "groq",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Groq.APIKey == "" && p.Groq.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "groq",
					Model:          "groq/llama-3.1-70b-versatile",
					APIKey:         p.Groq.APIKey,
					APIBase:        p.Groq.APIBase,
					Proxy:          p.Groq.Proxy,
					RequestTimeout: p.Groq.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"zhipu", "glm"},
			protocol:      "zhipu",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Zhipu.APIKey == "" && p.Zhipu.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "zhipu",
					Model:          "zhipu/glm-4",
					APIKey:         p.Zhipu.APIKey,
					APIBase:        p.Zhipu.APIBase,
					Proxy:          p.Zhipu.Proxy,
					RequestTimeout: p.Zhipu.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"vllm"},
			protocol:      "vllm",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.VLLM.APIKey == "" && p.VLLM.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "vllm",
					Model:          "vllm/auto",
					APIKey:         p.VLLM.APIKey,
					APIBase:        p.VLLM.APIBase,
					Proxy:          p.VLLM.Proxy,
					RequestTimeout: p.VLLM.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"gemini", "google"},
			protocol:      "gemini",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Gemini.APIKey == "" && p.Gemini.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "gemini",
					Model:          "gemini/gemini-pro",
					APIKey:         p.Gemini.APIKey,
					APIBase:        p.Gemini.APIBase,
					Proxy:          p.Gemini.Proxy,
					RequestTimeout: p.Gemini.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"nvidia"},
			protocol:      "nvidia",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Nvidia.APIKey == "" && p.Nvidia.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "nvidia",
					Model:          "nvidia/meta/llama-3.1-8b-instruct",
					APIKey:         p.Nvidia.APIKey,
					APIBase:        p.Nvidia.APIBase,
					Proxy:          p.Nvidia.Proxy,
					RequestTimeout: p.Nvidia.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"ollama"},
			protocol:      "ollama",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Ollama.APIKey == "" && p.Ollama.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "ollama",
					Model:          "ollama/llama3",
					APIKey:         p.Ollama.APIKey,
					APIBase:        p.Ollama.APIBase,
					Proxy:          p.Ollama.Proxy,
					RequestTimeout: p.Ollama.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"moonshot", "kimi"},
			protocol:      "moonshot",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Moonshot.APIKey == "" && p.Moonshot.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "moonshot",
					Model:          "moonshot/kimi",
					APIKey:         p.Moonshot.APIKey,
					APIBase:        p.Moonshot.APIBase,
					Proxy:          p.Moonshot.Proxy,
					RequestTimeout: p.Moonshot.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"shengsuanyun"},
			protocol:      "shengsuanyun",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.ShengSuanYun.APIKey == "" && p.ShengSuanYun.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "shengsuanyun",
					Model:          "shengsuanyun/auto",
					APIKey:         p.ShengSuanYun.APIKey,
					APIBase:        p.ShengSuanYun.APIBase,
					Proxy:          p.ShengSuanYun.Proxy,
					RequestTimeout: p.ShengSuanYun.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"deepseek"},
			protocol:      "deepseek",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.DeepSeek.APIKey == "" && p.DeepSeek.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "deepseek",
					Model:          "deepseek/deepseek-chat",
					APIKey:         p.DeepSeek.APIKey,
					APIBase:        p.DeepSeek.APIBase,
					Proxy:          p.DeepSeek.Proxy,
					RequestTimeout: p.DeepSeek.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"cerebras"},
			protocol:      "cerebras",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Cerebras.APIKey == "" && p.Cerebras.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "cerebras",
					Model:          "cerebras/llama-3.3-70b",
					APIKey:         p.Cerebras.APIKey,
					APIBase:        p.Cerebras.APIBase,
					Proxy:          p.Cerebras.Proxy,
					RequestTimeout: p.Cerebras.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"vivgrid"},
			protocol:      "vivgrid",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Vivgrid.APIKey == "" && p.Vivgrid.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "vivgrid",
					Model:          "vivgrid/auto",
					APIKey:         p.Vivgrid.APIKey,
					APIBase:        p.Vivgrid.APIBase,
					Proxy:          p.Vivgrid.Proxy,
					RequestTimeout: p.Vivgrid.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"volcengine", "doubao"},
			protocol:      "volcengine",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.VolcEngine.APIKey == "" && p.VolcEngine.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "volcengine",
					Model:          "volcengine/doubao-pro",
					APIKey:         p.VolcEngine.APIKey,
					APIBase:        p.VolcEngine.APIBase,
					Proxy:          p.VolcEngine.Proxy,
					RequestTimeout: p.VolcEngine.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"github_copilot", "copilot"},
			protocol:      "github-copilot",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" && p.GitHubCopilot.ConnectMode == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:   "github-copilot",
					Model:       "github-copilot/gpt-5.4",
					APIBase:     p.GitHubCopilot.APIBase,
					ConnectMode: p.GitHubCopilot.ConnectMode,
				}, true
			},
		},
		{
			providerNames: []string{"antigravity"},
			protocol:      "antigravity",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Antigravity.APIKey == "" && p.Antigravity.AuthMethod == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:  "antigravity",
					Model:      "antigravity/gemini-2.0-flash",
					APIKey:     p.Antigravity.APIKey,
					AuthMethod: p.Antigravity.AuthMethod,
				}, true
			},
		},
		{
			providerNames: []string{"qwen", "tongyi"},
			protocol:      "qwen",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Qwen.APIKey == "" && p.Qwen.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "qwen",
					Model:          "qwen/qwen-max",
					APIKey:         p.Qwen.APIKey,
					APIBase:        p.Qwen.APIBase,
					Proxy:          p.Qwen.Proxy,
					RequestTimeout: p.Qwen.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"mistral"},
			protocol:      "mistral",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Mistral.APIKey == "" && p.Mistral.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "mistral",
					Model:          "mistral/mistral-small-latest",
					APIKey:         p.Mistral.APIKey,
					APIBase:        p.Mistral.APIBase,
					Proxy:          p.Mistral.Proxy,
					RequestTimeout: p.Mistral.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"avian"},
			protocol:      "avian",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.Avian.APIKey == "" && p.Avian.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "avian",
					Model:          "avian/deepseek/deepseek-v3.2",
					APIKey:         p.Avian.APIKey,
					APIBase:        p.Avian.APIBase,
					Proxy:          p.Avian.Proxy,
					RequestTimeout: p.Avian.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"longcat"},
			protocol:      "longcat",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.LongCat.APIKey == "" && p.LongCat.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "longcat",
					Model:          "longcat/LongCat-Flash-Thinking",
					APIKey:         p.LongCat.APIKey,
					APIBase:        p.LongCat.APIBase,
					Proxy:          p.LongCat.Proxy,
					RequestTimeout: p.LongCat.RequestTimeout,
				}, true
			},
		},
		{
			providerNames: []string{"modelscope"},
			protocol:      "modelscope",
			buildConfig: func(p providersConfigV0) (modelConfigV0, bool) {
				if p.ModelScope.APIKey == "" && p.ModelScope.APIBase == "" {
					return modelConfigV0{}, false
				}
				return modelConfigV0{
					ModelName:      "modelscope",
					Model:          "modelscope/Qwen/Qwen3-235B-A22B-Instruct-2507",
					APIKey:         p.ModelScope.APIKey,
					APIBase:        p.ModelScope.APIBase,
					Proxy:          p.ModelScope.Proxy,
					RequestTimeout: p.ModelScope.RequestTimeout,
				}, true
			},
		},
	}

	// Process each provider migration
	for _, m := range migrations {
		mc, ok := m.buildConfig(p)
		if !ok {
			continue
		}

		// Check if this is the user's configured provider
		if slices.Contains(m.providerNames, userProvider) && userModel != "" {
			// Use the user's configured model instead of default
			mc.Model = buildModelWithProtocol(m.protocol, userModel)
		} else if userProvider == "" && userModel != "" && !legacyModelNameApplied {
			// Legacy config: no explicit provider field but model is specified
			// Use userModel as ModelName for the FIRST provider so GetModelConfig(model) can find it
			// This maintains backward compatibility with old configs that relied on implicit provider selection
			mc.ModelName = userModel
			mc.Model = buildModelWithProtocol(m.protocol, userModel)
			legacyModelNameApplied = true
		}

		result = append(result, mc)
	}

	return result
}

// loadConfigV0 loads a legacy config (no version field)
func loadConfigV0(data []byte) (migratable, error) {
	var v0 configV0
	if err := json.Unmarshal(data, &v0); err != nil {
		return nil, err
	}

	v0.migrateChannelConfigs()

	// Auto-migrate: if only legacy providers config exists, convert to model_list
	if len(v0.ModelList) == 0 && !v0.Providers.IsEmpty() {
		newModelList := v0ConvertProvidersToModelList(&v0)
		// Convert []ModelConfig to []modelConfigV0
		v0.ModelList = make([]modelConfigV0, len(newModelList))
		for i, m := range newModelList {
			v0.ModelList[i] = modelConfigV0{
				ModelName:      m.ModelName,
				Model:          m.Model,
				APIBase:        m.APIBase,
				Proxy:          m.Proxy,
				Fallbacks:      m.Fallbacks,
				AuthMethod:     m.AuthMethod,
				ConnectMode:    m.ConnectMode,
				Workspace:      m.Workspace,
				RPM:            m.RPM,
				MaxTokensField: m.MaxTokensField,
				RequestTimeout: m.RequestTimeout,
				ThinkingLevel:  m.ThinkingLevel,
				APIKey:         m.APIKey,
				APIKeys:        m.APIKeys,
			}
		}
	}

	return &v0, nil
}

// loadConfigV1 loads a version 1 config (current schema)
func loadConfig(data []byte) (*Config, error) {
	cfg := DefaultConfig()

	// Pre-scan the JSON to check how many model_list entries the user provided.
	// Go's JSON decoder reuses existing slice backing-array elements rather than
	// zero-initializing them, so fields absent from the user's JSON (e.g. api_base)
	// would silently inherit values from the DefaultConfig template at the same
	// index position. We only reset cfg.ModelList when the user actually provides
	// entries; when count is 0 we keep DefaultConfig's built-in list as fallback.
	var tmp Config
	if err := json.Unmarshal(data, &tmp); err != nil {
		return nil, err
	}
	if len(tmp.ModelList) > 0 {
		cfg.ModelList = nil
	}

	if err := json.Unmarshal(data, cfg); err != nil {
		return nil, err
	}
	return cfg, nil
}
