package config

import (
	"fmt"
	"strings"
)

const AgentKindGenericNode = "generic-node"

// NormalizeAgentKind returns the canonical agent kind for a node definition.
// An empty value defaults to generic-node; any other unsupported value is
// rejected so misconfigured kinds fail at config load time.
func NormalizeAgentKind(kind string) (string, error) {
	switch kind {
	case "":
		return AgentKindGenericNode, nil
	case AgentKindGenericNode:
		return kind, nil
	default:
		return "", fmt.Errorf("invalid agent_kind %q (allowed: %q)", kind, AgentKindGenericNode)
	}
}

// NormalizeProviderType returns the canonical driver name for provider type.
func NormalizeProviderType(t string) string {
	switch strings.ToLower(strings.TrimSpace(t)) {
	case "openai_compat", "openai_api", "vllm", "vllm-mlx", "lemonade", "sglang", "seulgivibe_claude", "seulgivibe_openai":
		return "openai_compat"
	case "ollama":
		return "ollama"
	case "cli":
		return "cli"
	default:
		return t
	}
}

// NormalizeAdapters promotes legacy single-instance Ollama/Vllm fields into the
// typed instance slices and validates that all instance names are unique.
func NormalizeAdapters(a *AdaptersConf) error {
	return normalizeAdapters(a)
}

func normalizeAdapters(a *AdaptersConf) error {
	if a.Ollama.Enabled {
		existing := findOllamaInstance(a.OllamaInstances, "ollama")
		if existing == nil {
			a.OllamaInstances = append([]OllamaInstanceConf{{
				Name:             "ollama",
				Enabled:          a.Ollama.Enabled,
				BaseURL:          a.Ollama.BaseURL,
				ContextSize:      a.Ollama.ContextSize,
				Capacity:         a.Ollama.Capacity,
				MaxQueue:         a.Ollama.MaxQueue,
				QueueTimeoutMS:   a.Ollama.QueueTimeoutMS,
				RequestTimeoutMS: a.Ollama.RequestTimeoutMS,
			}}, a.OllamaInstances...)
		} else if !sameOllamaInstance(*existing, a.Ollama) {
			return fmt.Errorf("ollama: legacy field conflicts with explicit instance %q: enabled/base_url/context_size/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "ollama")
		}
	}
	if a.Vllm.Enabled {
		existing := findVllmInstance(a.VllmInstances, "vllm")
		if existing == nil {
			a.VllmInstances = append([]VllmInstanceConf{{
				Name:             "vllm",
				Enabled:          a.Vllm.Enabled,
				Endpoint:         a.Vllm.Endpoint,
				Capacity:         a.Vllm.Capacity,
				MaxQueue:         a.Vllm.MaxQueue,
				QueueTimeoutMS:   a.Vllm.QueueTimeoutMS,
				RequestTimeoutMS: a.Vllm.RequestTimeoutMS,
			}}, a.VllmInstances...)
		} else if !sameVllmInstance(*existing, a.Vllm) {
			return fmt.Errorf("vllm: legacy field conflicts with explicit instance %q: enabled/endpoint/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "vllm")
		}
	}
	if a.OpenAICompat.Enabled {
		existing := findOpenAICompatInstance(a.OpenAICompatInstances, "openai_compat")
		if existing == nil {
			a.OpenAICompatInstances = append([]OpenAICompatInstanceConf{{
				Name:             "openai_compat",
				Enabled:          a.OpenAICompat.Enabled,
				Provider:         a.OpenAICompat.Provider,
				Endpoint:         a.OpenAICompat.Endpoint,
				Headers:          a.OpenAICompat.Headers,
				Capacity:         a.OpenAICompat.Capacity,
				MaxQueue:         a.OpenAICompat.MaxQueue,
				QueueTimeoutMS:   a.OpenAICompat.QueueTimeoutMS,
				RequestTimeoutMS: a.OpenAICompat.RequestTimeoutMS,
			}}, a.OpenAICompatInstances...)
		} else if !sameOpenAICompatInstance(*existing, a.OpenAICompat) {
			return fmt.Errorf("openai_compat: legacy field conflicts with explicit instance %q: enabled/provider/endpoint/headers/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "openai_compat")
		}
	}

	if err := checkUniqueNames("ollama_instances", func(i int) string { return a.OllamaInstances[i].Name }, len(a.OllamaInstances)); err != nil {
		return err
	}
	if err := checkUniqueNames("vllm_instances", func(i int) string { return a.VllmInstances[i].Name }, len(a.VllmInstances)); err != nil {
		return err
	}
	if err := checkUniqueNames("openai_compat_instances", func(i int) string { return a.OpenAICompatInstances[i].Name }, len(a.OpenAICompatInstances)); err != nil {
		return err
	}
	for i, inst := range a.OllamaInstances {
		field := fmt.Sprintf("ollama_instances[%d]", i)
		if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
			return err
		}
	}
	for i, inst := range a.VllmInstances {
		field := fmt.Sprintf("vllm_instances[%d]", i)
		if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
			return err
		}
	}
	for i, inst := range a.OpenAICompatInstances {
		field := fmt.Sprintf("openai_compat_instances[%d]", i)
		if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
			return err
		}
	}
	return nil
}

func sameOllamaInstance(inst OllamaInstanceConf, legacy OllamaConf) bool {
	return inst.Enabled == legacy.Enabled &&
		inst.BaseURL == legacy.BaseURL &&
		inst.ContextSize == legacy.ContextSize &&
		inst.Capacity == legacy.Capacity &&
		inst.MaxQueue == legacy.MaxQueue &&
		inst.QueueTimeoutMS == legacy.QueueTimeoutMS &&
		inst.RequestTimeoutMS == legacy.RequestTimeoutMS
}

func sameVllmInstance(inst VllmInstanceConf, legacy VllmConf) bool {
	return inst.Enabled == legacy.Enabled &&
		inst.Endpoint == legacy.Endpoint &&
		inst.Capacity == legacy.Capacity &&
		inst.MaxQueue == legacy.MaxQueue &&
		inst.QueueTimeoutMS == legacy.QueueTimeoutMS &&
		inst.RequestTimeoutMS == legacy.RequestTimeoutMS
}

func findOllamaInstance(instances []OllamaInstanceConf, name string) *OllamaInstanceConf {
	for i := range instances {
		if instances[i].Name == name {
			return &instances[i]
		}
	}
	return nil
}

func findVllmInstance(instances []VllmInstanceConf, name string) *VllmInstanceConf {
	for i := range instances {
		if instances[i].Name == name {
			return &instances[i]
		}
	}
	return nil
}

func sameOpenAICompatInstance(inst OpenAICompatInstanceConf, legacy OpenAICompatConf) bool {
	if inst.Enabled != legacy.Enabled ||
		inst.Provider != legacy.Provider ||
		inst.Endpoint != legacy.Endpoint ||
		inst.Capacity != legacy.Capacity ||
		inst.MaxQueue != legacy.MaxQueue ||
		inst.QueueTimeoutMS != legacy.QueueTimeoutMS ||
		inst.RequestTimeoutMS != legacy.RequestTimeoutMS {
		return false
	}
	if len(inst.Headers) != len(legacy.Headers) {
		return false
	}
	for k, v := range inst.Headers {
		if lv, ok := legacy.Headers[k]; !ok || lv != v {
			return false
		}
	}
	return true
}

func findOpenAICompatInstance(instances []OpenAICompatInstanceConf, name string) *OpenAICompatInstanceConf {
	for i := range instances {
		if instances[i].Name == name {
			return &instances[i]
		}
	}
	return nil
}
