package mangowc

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

func TestAutogenerateComment(t *testing.T) {
	tests := []struct {
		command  string
		params   string
		expected string
	}{
		{"spawn", "kitty", "kitty"},
		{"spawn_shell", "firefox", "firefox"},
		{"killclient", "", "Close window"},
		{"quit", "", "Exit MangoWC"},
		{"reload_config", "", "Reload configuration"},
		{"focusstack", "next", "Focus next window"},
		{"focusstack", "prev", "Focus previous window"},
		{"focusdir", "left", "Focus left"},
		{"focusdir", "right", "Focus right"},
		{"focusdir", "up", "Focus up"},
		{"focusdir", "down", "Focus down"},
		{"exchange_client", "left", "Swap window left"},
		{"exchange_client", "right", "Swap window right"},
		{"togglefloating", "", "Float/unfloat window"},
		{"togglefullscreen", "", "Toggle fullscreen"},
		{"togglefakefullscreen", "", "Toggle fake fullscreen"},
		{"togglemaximizescreen", "", "Toggle maximize"},
		{"toggleglobal", "", "Toggle global"},
		{"toggleoverview", "", "Toggle overview"},
		{"toggleoverlay", "", "Toggle overlay"},
		{"minimized", "", "Minimize window"},
		{"restore_minimized", "", "Restore minimized"},
		{"toggle_scratchpad", "", "Toggle scratchpad"},
		{"setlayout", "tile", "Set layout tile"},
		{"switch_layout", "", "Switch layout"},
		{"view", "1,0", "View tag 1"},
		{"tag", "2,0", "Move to tag 2"},
		{"toggleview", "3,0", "Toggle tag 3"},
		{"viewtoleft", "", "View left tag"},
		{"viewtoright", "", "View right tag"},
		{"viewtoleft_have_client", "", "View left tag"},
		{"viewtoright_have_client", "", "View right tag"},
		{"tagtoleft", "", "Move to left tag"},
		{"tagtoright", "", "Move to right tag"},
		{"focusmon", "left", "Focus monitor left"},
		{"tagmon", "right", "Move to monitor right"},
		{"incgaps", "1", "Increase gaps"},
		{"incgaps", "-1", "Decrease gaps"},
		{"togglegaps", "", "Toggle gaps"},
		{"movewin", "+0,-50", "Move window by +0,-50"},
		{"resizewin", "+0,+50", "Resize window by +0,+50"},
		{"set_proportion", "1.0", "Set proportion 1.0"},
		{"switch_proportion_preset", "", "Switch proportion preset"},
		{"unknown", "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.command+"_"+tt.params, func(t *testing.T) {
			result := autogenerateComment(tt.command, tt.params)
			if result != tt.expected {
				t.Errorf("autogenerateComment(%q, %q) = %q, want %q",
					tt.command, tt.params, result, tt.expected)
			}
		})
	}
}

func TestGetKeybindAtLine(t *testing.T) {
	tests := []struct {
		name     string
		line     string
		expected *KeyBinding
	}{
		{
			name: "basic_keybind",
			line: "bind=ALT,q,killclient,",
			expected: &KeyBinding{
				Mods:    []string{"ALT"},
				Key:     "q",
				Command: "killclient",
				Params:  "",
				Comment: "Close window",
			},
		},
		{
			name: "keybind_with_params",
			line: "bind=ALT,Left,focusdir,left",
			expected: &KeyBinding{
				Mods:    []string{"ALT"},
				Key:     "Left",
				Command: "focusdir",
				Params:  "left",
				Comment: "Focus left",
			},
		},
		{
			name: "keybind_with_comment",
			line: "bind=Alt,t,spawn,kitty # Open terminal",
			expected: &KeyBinding{
				Mods:    []string{"Alt"},
				Key:     "t",
				Command: "spawn",
				Params:  "kitty",
				Comment: "Open terminal",
			},
		},
		{
			name:     "keybind_hidden",
			line:     "bind=SUPER,h,spawn,secret # [hidden]",
			expected: nil,
		},
		{
			name: "keybind_multiple_mods",
			line: "bind=SUPER+SHIFT,Up,exchange_client,up",
			expected: &KeyBinding{
				Mods:    []string{"SUPER", "SHIFT"},
				Key:     "Up",
				Command: "exchange_client",
				Params:  "up",
				Comment: "Swap window up",
			},
		},
		{
			name: "keybind_no_mods",
			line: "bind=NONE,Print,spawn,screenshot",
			expected: &KeyBinding{
				Mods:    []string{},
				Key:     "Print",
				Command: "spawn",
				Params:  "screenshot",
				Comment: "screenshot",
			},
		},
		{
			name: "keybind_multiple_params",
			line: "bind=Ctrl,1,view,1,0",
			expected: &KeyBinding{
				Mods:    []string{"Ctrl"},
				Key:     "1",
				Command: "view",
				Params:  "1,0",
				Comment: "View tag 1",
			},
		},
		{
			name: "bindl_flag",
			line: "bindl=SUPER+ALT,l,spawn,dms ipc call lock lock",
			expected: &KeyBinding{
				Mods:    []string{"SUPER", "ALT"},
				Key:     "l",
				Command: "spawn",
				Params:  "dms ipc call lock lock",
				Comment: "dms ipc call lock lock",
			},
		},
		{
			name: "keybind_with_spaces",
			line: "bind = SUPER, r, reload_config",
			expected: &KeyBinding{
				Mods:    []string{"SUPER"},
				Key:     "r",
				Command: "reload_config",
				Params:  "",
				Comment: "Reload configuration",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parser := NewParser()
			parser.contentLines = []string{tt.line}
			result := parser.getKeybindAtLine(0)

			if tt.expected == nil {
				if result != nil {
					t.Errorf("expected nil, got %+v", result)
				}
				return
			}

			if result == nil {
				t.Errorf("expected %+v, got nil", tt.expected)
				return
			}

			if result.Key != tt.expected.Key {
				t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key)
			}
			if result.Command != tt.expected.Command {
				t.Errorf("Command = %q, want %q", result.Command, tt.expected.Command)
			}
			if result.Params != tt.expected.Params {
				t.Errorf("Params = %q, want %q", result.Params, tt.expected.Params)
			}
			if result.Comment != tt.expected.Comment {
				t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment)
			}
			if len(result.Mods) != len(tt.expected.Mods) {
				t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods))
			} else {
				for i := range result.Mods {
					if result.Mods[i] != tt.expected.Mods[i] {
						t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i])
					}
				}
			}
		})
	}
}

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

	content := `# MangoWC Configuration
blur=0
border_radius=12

# Key Bindings
bind=SUPER,r,reload_config
bind=Alt,t,spawn,kitty # Terminal
bind=ALT,q,killclient,
bind=ALT,Left,focusdir,left

# Hidden binding
bind=SUPER,h,spawn,secret # [hidden]

# Multiple modifiers
bind=SUPER+SHIFT,Up,exchange_client,up

# Workspace bindings
bind=Ctrl,1,view,1,0
bind=Ctrl,2,view,2,0
`

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

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

	expectedCount := 7
	if len(keybinds) != expectedCount {
		t.Errorf("Expected %d keybinds, got %d", expectedCount, len(keybinds))
	}

	if len(keybinds) > 0 && keybinds[0].Command != "reload_config" {
		t.Errorf("First keybind command = %q, want %q", keybinds[0].Command, "reload_config")
	}

	foundHidden := false
	for _, kb := range keybinds {
		if kb.Command == "spawn" && kb.Params == "secret" {
			foundHidden = true
		}
	}
	if foundHidden {
		t.Error("Hidden keybind should not be included in results")
	}
}

func TestReadContentMultipleFiles(t *testing.T) {
	tmpDir := t.TempDir()

	file1 := filepath.Join(tmpDir, "a.conf")
	file2 := filepath.Join(tmpDir, "b.conf")

	content1 := "bind=ALT,q,killclient,\n"
	content2 := "bind=Alt,t,spawn,kitty\n"

	if err := os.WriteFile(file1, []byte(content1), 0644); err != nil {
		t.Fatalf("Failed to write file1: %v", err)
	}
	if err := os.WriteFile(file2, []byte(content2), 0644); err != nil {
		t.Fatalf("Failed to write file2: %v", err)
	}

	parser := NewParser()
	if err := parser.ReadContent(tmpDir); err != nil {
		t.Fatalf("ReadContent failed: %v", err)
	}

	keybinds := parser.ParseKeys()
	if len(keybinds) != 2 {
		t.Errorf("Expected 2 keybinds from multiple files, got %d", len(keybinds))
	}
}

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

	content := "bind=ALT,q,killclient,\n"

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

	parser := NewParser()
	if err := parser.ReadContent(configFile); err != nil {
		t.Fatalf("ReadContent failed: %v", err)
	}

	keybinds := parser.ParseKeys()
	if len(keybinds) != 1 {
		t.Errorf("Expected 1 keybind, got %d", len(keybinds))
	}
}

func TestReadContentErrors(t *testing.T) {
	tests := []struct {
		name string
		path string
	}{
		{
			name: "nonexistent_directory",
			path: "/nonexistent/path/that/does/not/exist",
		},
		{
			name: "empty_directory",
			path: t.TempDir(),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := ParseKeys(tt.path)
			if err == nil {
				t.Error("Expected error, got nil")
			}
		})
	}
}

func TestReadContentWithTildeExpansion(t *testing.T) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		t.Skip("Cannot get home directory")
	}

	tmpSubdir := filepath.Join(homeDir, ".config", "test-mango-"+t.Name())
	if err := os.MkdirAll(tmpSubdir, 0755); err != nil {
		t.Skip("Cannot create test directory in home")
	}
	defer os.RemoveAll(tmpSubdir)

	configFile := filepath.Join(tmpSubdir, "config.conf")
	if err := os.WriteFile(configFile, []byte("bind=ALT,q,killclient,\n"), 0644); err != nil {
		t.Fatalf("Failed to write test config: %v", err)
	}

	relPath, err := filepath.Rel(homeDir, tmpSubdir)
	if err != nil {
		t.Skip("Cannot create relative path")
	}

	parser := NewParser()
	tildePathMatch := "~/" + relPath
	err = parser.ReadContent(tildePathMatch)

	if err != nil {
		t.Errorf("ReadContent with tilde path failed: %v", err)
	}
}

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

	content := `
# This is a comment
bind=ALT,q,killclient,

# Another comment

bind=Alt,t,spawn,kitty
`

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

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

	if len(keybinds) != 2 {
		t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(keybinds))
	}
}

func TestInvalidBindLines(t *testing.T) {
	tests := []struct {
		name string
		line string
	}{
		{
			name: "missing_parts",
			line: "bind=SUPER,q",
		},
		{
			name: "not_bind",
			line: "blur=0",
		},
		{
			name: "empty_line",
			line: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parser := NewParser()
			parser.contentLines = []string{tt.line}
			result := parser.getKeybindAtLine(0)

			if result != nil {
				t.Errorf("expected nil for invalid line, got %+v", result)
			}
		})
	}
}

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

	content := `# Application Launchers
bind=Alt,t,spawn,kitty
bind=Alt,space,spawn,dms ipc call spotlight toggle
bind=Alt,v,spawn,dms ipc call clipboard toggle

# exit
bind=ALT+SHIFT,e,quit
bind=ALT,q,killclient,

# switch window focus
bind=SUPER,Tab,focusstack,next
bind=ALT,Left,focusdir,left
bind=ALT,Right,focusdir,right

# tag switch
bind=SUPER,Left,viewtoleft,0
bind=CTRL,Left,viewtoleft_have_client,0
bind=SUPER,Right,viewtoright,0

bind=Ctrl,1,view,1,0
bind=Ctrl,2,view,2,0
bind=Ctrl,3,view,3,0
`

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

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

	if len(keybinds) < 14 {
		t.Errorf("Expected at least 14 keybinds, got %d", len(keybinds))
	}

	foundSpawn := false
	foundQuit := false
	foundView := false

	for _, kb := range keybinds {
		if kb.Command == "spawn" && kb.Params == "kitty" {
			foundSpawn = true
		}
		if kb.Command == "quit" {
			foundQuit = true
		}
		if kb.Command == "view" && kb.Params == "1,0" {
			foundView = true
		}
	}

	if !foundSpawn {
		t.Error("Did not find spawn kitty keybind")
	}
	if !foundQuit {
		t.Error("Did not find quit keybind")
	}
	if !foundView {
		t.Error("Did not find view workspace 1 keybind")
	}
}
