package providers

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

func TestNewHyprlandProvider(t *testing.T) {
	t.Run("custom path", func(t *testing.T) {
		p := NewHyprlandProvider("/custom/path")
		if p == nil {
			t.Fatal("NewHyprlandProvider returned nil")
		}
		if p.configPath != "/custom/path" {
			t.Errorf("configPath = %q, want %q", p.configPath, "/custom/path")
		}
	})

	t.Run("empty path defaults", func(t *testing.T) {
		p := NewHyprlandProvider("")
		if p == nil {
			t.Fatal("NewHyprlandProvider returned nil")
		}
		configDir, err := os.UserConfigDir()
		if err != nil {
			t.Fatalf("UserConfigDir failed: %v", err)
		}
		expected := filepath.Join(configDir, "hypr")
		if p.configPath != expected {
			t.Errorf("configPath = %q, want %q", p.configPath, expected)
		}
	})
}

func TestHyprlandProviderName(t *testing.T) {
	p := NewHyprlandProvider("")
	if p.Name() != "hyprland" {
		t.Errorf("Name() = %q, want %q", p.Name(), "hyprland")
	}
}

func TestHyprlandProviderGetCheatSheet(t *testing.T) {
	tmpDir := t.TempDir()
	configFile := filepath.Join(tmpDir, "hyprland.conf")

	content := `##! Window Management
bind = SUPER, Q, killactive
bind = SUPER, F, fullscreen, 0

###! Movement
bind = SUPER, left, movefocus, l
bind = SUPER, right, movefocus, r

##! Applications
bind = SUPER, T, exec, kitty # Terminal
bind = SUPER, 1, workspace, 1
`

	if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil {
		t.Fatalf("Failed to write test config: %v", err)
	}

	p := NewHyprlandProvider(tmpDir)
	sheet, err := p.GetCheatSheet()

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

	if sheet.Title != "Hyprland Keybinds" {
		t.Errorf("Title = %q, want %q", sheet.Title, "Hyprland Keybinds")
	}

	if sheet.Provider != "hyprland" {
		t.Errorf("Provider = %q, want %q", sheet.Provider, "hyprland")
	}

	if len(sheet.Binds) == 0 {
		t.Error("expected categorized bindings, got none")
	}

	if windowBinds, ok := sheet.Binds["Window"]; !ok || len(windowBinds) == 0 {
		t.Error("expected Window category with bindings")
	}

	if execBinds, ok := sheet.Binds["Execute"]; !ok || len(execBinds) == 0 {
		t.Error("expected Execute category with bindings")
	}

	if wsBinds, ok := sheet.Binds["Workspace"]; !ok || len(wsBinds) == 0 {
		t.Error("expected Workspace category with bindings")
	}
}

func TestHyprlandProviderGetCheatSheetError(t *testing.T) {
	p := NewHyprlandProvider("/nonexistent/path")
	_, err := p.GetCheatSheet()

	if err == nil {
		t.Error("expected error for nonexistent path, got nil")
	}
}

func TestFormatKey(t *testing.T) {
	tmpDir := t.TempDir()
	configFile := filepath.Join(tmpDir, "hyprland.conf")

	tests := []struct {
		name     string
		content  string
		expected string
		category string
	}{
		{
			name:     "single mod",
			content:  "bind = SUPER, Q, killactive",
			expected: "SUPER+Q",
			category: "Window",
		},
		{
			name:     "multiple mods",
			content:  "bind = SUPER+SHIFT, F, fullscreen, 0",
			expected: "SUPER+SHIFT+F",
			category: "Window",
		},
		{
			name:     "no mods",
			content:  "bind = , Print, exec, screenshot",
			expected: "Print",
			category: "Execute",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := os.WriteFile(configFile, []byte(tt.content), 0o644); err != nil {
				t.Fatalf("Failed to write test config: %v", err)
			}

			p := NewHyprlandProvider(tmpDir)
			sheet, err := p.GetCheatSheet()
			if err != nil {
				t.Fatalf("GetCheatSheet failed: %v", err)
			}

			categoryBinds, ok := sheet.Binds[tt.category]
			if !ok || len(categoryBinds) == 0 {
				t.Fatalf("expected binds in category %q", tt.category)
			}

			if categoryBinds[0].Key != tt.expected {
				t.Errorf("Key = %q, want %q", categoryBinds[0].Key, tt.expected)
			}
		})
	}
}

func TestDescriptionFallback(t *testing.T) {
	tmpDir := t.TempDir()
	configFile := filepath.Join(tmpDir, "hyprland.conf")

	tests := []struct {
		name     string
		content  string
		wantDesc string
	}{
		{
			name:     "autogenerated description for known dispatcher",
			content:  "bind = SUPER, Q, killactive",
			wantDesc: "Close window",
		},
		{
			name:     "custom comment overrides autogeneration",
			content:  "bind = SUPER, T, exec, kitty # Open terminal",
			wantDesc: "Open terminal",
		},
		{
			name:     "fallback for unknown dispatcher without params",
			content:  "bind = SUPER, W, unknowndispatcher",
			wantDesc: "unknowndispatcher",
		},
		{
			name:     "fallback for unknown dispatcher with params",
			content:  "bind = SUPER, X, customdispatcher, arg1",
			wantDesc: "customdispatcher arg1",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := os.WriteFile(configFile, []byte(tt.content), 0o644); err != nil {
				t.Fatalf("Failed to write test config: %v", err)
			}

			p := NewHyprlandProvider(tmpDir)
			sheet, err := p.GetCheatSheet()
			if err != nil {
				t.Fatalf("GetCheatSheet failed: %v", err)
			}

			found := false
			for _, binds := range sheet.Binds {
				for _, bind := range binds {
					if bind.Description == tt.wantDesc {
						found = true
						break
					}
				}
				if found {
					break
				}
			}

			if !found {
				t.Errorf("expected description %q not found in any bind", tt.wantDesc)
			}
		})
	}
}
