// Package keyring provides secure password storage for sudo authentication
package keyring

import (
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestDetectStatus(t *testing.T) {
	status := DetectStatus()

	// Platform should always be set
	if status.Platform == "" {
		t.Error("Platform should not be empty")
	}

	// Platform should match runtime
	if status.Platform != runtime.GOOS {
		t.Errorf("Platform = %q, want %q", status.Platform, runtime.GOOS)
	}

	// Non-Linux should have daemon running
	if runtime.GOOS != "linux" {
		if !status.DaemonRunning {
			t.Error("Non-Linux platforms should have daemonRunning = true")
		}
		if status.Distro != "n/a" {
			t.Errorf("Non-Linux distro = %q, want 'n/a'", status.Distro)
		}
		if status.DesktopEnv != "n/a" {
			t.Errorf("Non-Linux desktopEnv = %q, want 'n/a'", status.DesktopEnv)
		}
	}
}

func TestCheckDependencies(t *testing.T) {
	err := CheckDependencies()

	// Non-Linux should always pass
	if runtime.GOOS != "linux" {
		if err != nil {
			t.Errorf("Non-Linux CheckDependencies() should return nil, got: %v", err)
		}
	}
	// Linux check is environment-dependent, so we just ensure it doesn't panic
}

func TestNeedsInstallPrompt(t *testing.T) {
	needs := NeedsInstallPrompt()

	// Non-Linux should never need prompt
	if runtime.GOOS != "linux" {
		if needs {
			t.Error("Non-Linux platforms should not need install prompt")
		}
	}
	// Linux check is environment-dependent, so we just ensure it doesn't panic
}

func TestCanUseKeyring(t *testing.T) {
	canUse := CanUseKeyring()

	// Non-Linux should always be able to use keyring
	if runtime.GOOS != "linux" {
		if !canUse {
			t.Error("Non-Linux platforms should be able to use keyring")
		}
	}
}

func TestOpen(t *testing.T) {
	if runtime.GOOS == "linux" && testing.Short() {
		t.Skip("Skipping on Linux in short mode - may require keyring interaction")
	}

	// Use a timeout to prevent hanging
	done := make(chan struct{})
	var kr *Keyring
	var err error

	go func() {
		kr, err = Open()
		close(done)
	}()

	select {
	case <-done:
		// Good, completed
	case <-time.After(5 * time.Second):
		t.Fatal("Open() timed out - likely waiting for keyring interaction")
	}

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

	if kr == nil {
		t.Fatal("Open() returned nil keyring")
	}

	// Backend should be set
	if kr.Backend() == BackendNone {
		t.Error("Backend should not be BackendNone after successful Open()")
	}
}

func TestStoreAndGetPassword(t *testing.T) {
	if runtime.GOOS == "linux" {
		t.Skip("Skipping on Linux - may require keyring interaction")
	}

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

	// Clean up any existing password
	_ = kr.DeletePassword()

	// Initially should have no password
	if kr.HasPassword() {
		t.Error("HasPassword() should be false initially")
	}

	// Store password
	testPassword := "test-sudo-password-123"
	err = kr.StorePassword(testPassword)
	if err != nil {
		t.Fatalf("StorePassword() failed: %v", err)
	}

	// Should have password now
	if !kr.HasPassword() {
		t.Error("HasPassword() should be true after StorePassword()")
	}

	// Retrieve password
	retrieved, err := kr.GetPassword()
	if err != nil {
		t.Fatalf("GetPassword() failed: %v", err)
	}

	if retrieved != testPassword {
		t.Errorf("GetPassword() = %q, want %q", retrieved, testPassword)
	}

	// Delete password
	err = kr.DeletePassword()
	if err != nil {
		t.Fatalf("DeletePassword() failed: %v", err)
	}

	// Should not have password after delete
	if kr.HasPassword() {
		t.Error("HasPassword() should be false after DeletePassword()")
	}

	// GetPassword should fail
	_, err = kr.GetPassword()
	if err == nil {
		t.Error("GetPassword() should fail after DeletePassword()")
	}
}

func TestStoreEmptyPassword(t *testing.T) {
	kr, err := Open()
	if err != nil {
		t.Fatalf("Open() failed: %v", err)
	}

	// Empty password should fail
	err = kr.StorePassword("")
	if err == nil {
		t.Error("StorePassword('') should fail")
	}
}

func TestGetSetupInstructions(t *testing.T) {
	instructions := GetSetupInstructions()

	// Non-Linux should have empty instructions
	if runtime.GOOS != "linux" {
		if instructions != "" {
			t.Errorf("Non-Linux GetSetupInstructions() = %q, want empty", instructions)
		}
	}
}

func TestValidateSetup(t *testing.T) {
	if runtime.GOOS == "linux" {
		// On Linux, this test may require keyring interaction which hangs in CI
		// Skip if no keyring daemon is available
		status := DetectStatus()
		if !status.DaemonRunning && len(status.AvailableBackends) == 0 {
			t.Skip("Skipping on Linux without keyring daemon - may require interaction")
		}
	}

	// Use a timeout to prevent hanging
	done := make(chan struct{})
	var valid bool
	var msg string

	go func() {
		valid, msg = ValidateSetup()
		close(done)
	}()

	select {
	case <-done:
		// Good, completed
	case <-time.After(5 * time.Second):
		t.Skip("ValidateSetup() timed out - likely waiting for keyring interaction")
	}

	// Non-Linux should always be valid
	if runtime.GOOS != "linux" {
		if !valid {
			t.Errorf("Non-Linux ValidateSetup() should be valid, got: %s", msg)
		}
	}

	// Message should not be empty
	if msg == "" {
		t.Error("ValidateSetup() message should not be empty")
	}
}

func TestNormalizeDesktopEnv(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"GNOME", "GNOME"},
		{"ubuntu:GNOME", "GNOME"},
		{"KDE", "KDE"},
		{"plasma", "KDE"},
		{"XFCE", "XFCE"},
		{"xfce4", "XFCE"},
		{"i3", "i3"},
		{"sway", "Sway"},
		{"i3wm", "i3"},
		{"Hyprland", "Hyprland"},
		{"hyprland", "Hyprland"},
		{"COSMIC", "COSMIC"},
		{"cosmic", "COSMIC"},
		{"Cinnamon", "Cinnamon"},
		{"Pantheon", "Pantheon"},
		{"river", "River"},
		{"qtile", "qtile"},
		{"unknown-de", "unknown-de"},
	}

	for _, tt := range tests {
		result := normalizeDesktopEnv(tt.input)
		if result != tt.expected {
			t.Errorf("normalizeDesktopEnv(%q) = %q, want %q", tt.input, result, tt.expected)
		}
	}
}

func TestDetectDistro(t *testing.T) {
	if runtime.GOOS != "linux" {
		// Non-Linux should return "n/a"
		distro := detectDistro()
		if distro != "n/a" {
			t.Errorf("Non-Linux detectDistro() = %q, want 'n/a'", distro)
		}
		return
	}

	// On Linux, just ensure it doesn't panic and returns something
	distro := detectDistro()
	if distro == "" {
		t.Error("detectDistro() should not return empty string")
	}
}

func TestDetectDesktopEnvironment(t *testing.T) {
	if runtime.GOOS != "linux" {
		// Non-Linux should return "n/a"
		de := detectDesktopEnvironment()
		if de != "n/a" {
			t.Errorf("Non-Linux detectDesktopEnvironment() = %q, want 'n/a'", de)
		}
		return
	}

	// On Linux, just ensure it doesn't panic and returns something
	de := detectDesktopEnvironment()
	if de == "" {
		t.Error("detectDesktopEnvironment() should not return empty string")
	}
}

func TestGenerateInstallHint(t *testing.T) {
	tests := []struct {
		distro string
		de     string
		want   string
	}{
		{"debian", "GNOME", "apt"},
		{"fedora", "GNOME", "dnf"},
		{"arch", "KDE", "pacman"},
		{"suse", "GNOME", "zypper"},
		{"unknown", "KDE", "kwallet"},
		{"unknown", "GNOME", "gnome-keyring"},
		{"unknown", "Cinnamon", "gnome-keyring"},
		{"unknown", "XFCE", "gnome-keyring"},
		{"unknown", "COSMIC", "gnome-keyring"},
		{"unknown", "unknown", "keyring"},
		// Tiling WMs need special setup hint
		{"arch", "i3", "gnome-keyring-daemon"},
		{"arch", "Hyprland", "gnome-keyring-daemon"},
		{"arch", "Sway", "gnome-keyring-daemon"},
		{"arch", "bspwm", "gnome-keyring-daemon"},
	}

	for _, tt := range tests {
		result := generateInstallHint(tt.distro, tt.de)
		if !strings.Contains(result, tt.want) {
			t.Errorf("generateInstallHint(%q, %q) = %q, should contain %q",
				tt.distro, tt.de, result, tt.want)
		}
	}
}

func TestBackendTypeConstants(t *testing.T) {
	// Ensure backend type constants are defined correctly
	tests := []struct {
		backend BackendType
		want    string
	}{
		{BackendSecretService, "secret-service"},
		{BackendKWallet, "kwallet"},
		{BackendPass, "pass"},
		{BackendFile, "file"},
		{BackendNone, "none"},
	}

	for _, tt := range tests {
		if string(tt.backend) != tt.want {
			t.Errorf("BackendType constant = %q, want %q", tt.backend, tt.want)
		}
	}
}

func TestStatusJSONTags(t *testing.T) {
	// Verify Status struct has proper JSON tags
	status := Status{
		AvailableBackends: []BackendType{BackendFile},
		ActiveBackend:     BackendFile,
		DaemonRunning:     true,
		InstallHint:       "test",
		Distro:            "test",
		DesktopEnv:        "test",
		HasPassword:       false,
		Platform:          "test",
	}

	// Just verify fields are accessible
	if status.ActiveBackend != BackendFile {
		t.Error("Status struct fields not accessible")
	}
}
