package mcp

import (
	"os"
	"path/filepath"
	"testing"
)

func TestDefaultRegistry(t *testing.T) {
	reg := DefaultRegistry()
	if reg.SchemaVersion != "v1" {
		t.Errorf("Expected v1, got %s", reg.SchemaVersion)
	}
	if len(reg.Servers) == 0 {
		t.Error("Default registry should have servers")
	}

	names := map[string]bool{}
	for _, s := range reg.Servers {
		if names[s.Name] {
			t.Errorf("Duplicate server name: %s", s.Name)
		}
		names[s.Name] = true
		if s.Command == "" {
			t.Errorf("Server %s missing command", s.Name)
		}
	}
}

func TestSaveAndLoadRegistry(t *testing.T) {
	tmpDir := t.TempDir()
	registryPath := filepath.Join(tmpDir, "mcp-registry.yaml")
	SetRegistryPath(registryPath)

	reg := DefaultRegistry()
	if err := SaveRegistry(reg); err != nil {
		t.Fatalf("SaveRegistry failed: %v", err)
	}

	if _, err := os.Stat(registryPath); os.IsNotExist(err) {
		t.Error("Registry file should exist")
	}

	loaded, err := LoadRegistry()
	if err != nil {
		t.Fatalf("LoadRegistry failed: %v", err)
	}
	if len(loaded.Servers) != len(reg.Servers) {
		t.Errorf("Expected %d servers, got %d", len(reg.Servers), len(loaded.Servers))
	}
}

func TestAddAndRemoveFromRegistry(t *testing.T) {
	tmpDir := t.TempDir()
	SetRegistryPath(filepath.Join(tmpDir, "mcp-registry.yaml"))
	SaveRegistry(DefaultRegistry())

	newServer := RegistryServer{
		Name:        "test-server",
		Description: "Test server",
		Category:    "test",
		Command:     "npx",
		Args:        []string{"-y", "test-pkg"},
		InstallType: "npm",
	}

	if err := AddToRegistry(newServer); err != nil {
		t.Fatalf("AddToRegistry failed: %v", err)
	}

	reg, _ := LoadRegistry()
	found := false
	for _, s := range reg.Servers {
		if s.Name == "test-server" {
			found = true
			break
		}
	}
	if !found {
		t.Error("test-server should be in registry")
	}

	if err := RemoveFromRegistry("test-server"); err != nil {
		t.Fatalf("RemoveFromRegistry failed: %v", err)
	}

	reg, _ = LoadRegistry()
	for _, s := range reg.Servers {
		if s.Name == "test-server" {
			t.Error("test-server should have been removed")
		}
	}
}

func TestResolveEnv(t *testing.T) {
	env := map[string]string{
		"API_KEY": "",
		"HOST":    "localhost",
	}

	os.Setenv("API_KEY", "from-env")
	defer os.Unsetenv("API_KEY")

	resolved := ResolveEnv(env, nil)
	if resolved["API_KEY"] != "from-env" {
		t.Errorf("Expected from-env, got %s", resolved["API_KEY"])
	}
	if resolved["HOST"] != "localhost" {
		t.Errorf("Expected localhost, got %s", resolved["HOST"])
	}
}

func TestValidateConfig(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "test-config.json")
	os.WriteFile(configPath, []byte(`{"mcps":{}}`), 0644)

	if err := ValidateConfig(configPath); err != nil {
		t.Errorf("Valid config should pass: %v", err)
	}

	badPath := filepath.Join(tmpDir, "nonexistent.json")
	if err := ValidateConfig(badPath); err == nil {
		t.Error("Nonexistent config should fail")
	}
}

func TestEditorConfigs(t *testing.T) {
	configs := EditorConfigs("/tmp")
	if len(configs) < 3 {
		t.Errorf("Expected at least 3 editor configs, got %d", len(configs))
	}

	names := map[string]bool{}
	for _, c := range configs {
		if names[c.Name] {
			t.Errorf("Duplicate editor: %s", c.Name)
		}
		names[c.Name] = true
		if c.ConfigPath == "" {
			t.Errorf("Editor %s missing config path", c.Name)
		}
		if c.ConfigKey == "" {
			t.Errorf("Editor %s missing config key", c.Name)
		}
	}
}

func TestDiscoverNpmServers(t *testing.T) {
	servers, err := DiscoverNpmServers()
	if err != nil {
		t.Fatalf("DiscoverNpmServers failed: %v", err)
	}
	if len(servers) == 0 {
		t.Error("Should discover some npm servers")
	}
}

func TestReceiptRoundTrip(t *testing.T) {
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)
	defer os.Unsetenv("HOME")

	SetRegistryPath(filepath.Join(tmpDir, "reg.yaml"))

	if err := SaveReceipt("test-server", "1.2.3"); err != nil {
		t.Fatalf("SaveReceipt failed: %v", err)
	}

	version := GetInstalledVersion("test-server")
	if version != "1.2.3" {
		t.Errorf("Expected 1.2.3, got %s", version)
	}
}

func TestInitRegistry(t *testing.T) {
	tmpDir := t.TempDir()
	SetRegistryPath(filepath.Join(tmpDir, "init-reg.yaml"))

	if err := InitRegistry(); err != nil {
		t.Fatalf("InitRegistry failed: %v", err)
	}

	if _, err := os.Stat(filepath.Join(tmpDir, "init-reg.yaml")); os.IsNotExist(err) {
		t.Error("Registry file should be created")
	}

	if err := InitRegistry(); err != nil {
		t.Fatalf("Second InitRegistry should not fail: %v", err)
	}
}

func TestDetectInstalledEditors(t *testing.T) {
	tmpDir := t.TempDir()
	os.MkdirAll(filepath.Join(tmpDir, ".config", "crush"), 0755)
	os.WriteFile(filepath.Join(tmpDir, ".config", "crush", "crush.json"), []byte(`{}`), 0644)
	os.MkdirAll(filepath.Join(tmpDir, ".cursor"), 0755)

	editors := DetectInstalledEditors(tmpDir)
	if len(editors) < 2 {
		t.Errorf("Expected at least 2 editors, got %d", len(editors))
	}

	found := map[string]bool{}
	for _, e := range editors {
		found[e] = true
	}
	if !found["crush"] {
		t.Error("Should detect crush")
	}
	if !found["cursor"] {
		t.Error("Should detect cursor")
	}
}

func TestCheckServerStatus(t *testing.T) {
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)
	defer os.Unsetenv("HOME")

	SetRegistryPath(filepath.Join(tmpDir, "reg.yaml"))
	SaveRegistry(DefaultRegistry())

	status := CheckServerStatus("nonexistent")
	if status.Error == "" {
		t.Error("Should have error for nonexistent server")
	}
}
