package config

import (
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// createTempConfig creates a temporary config file with content and returns the path
// If content is empty, only creates the directory (file not created)
func createTempConfig(t *testing.T, content string) string {
	t.Helper()

	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "mpv.conf")

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

	return configPath
}

// readFileLines reads file content and returns as array of lines
func readFileLines(t *testing.T, path string) []string {
	t.Helper()

	content, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("Failed to read file: %v", err)
	}

	return strings.Split(string(content), "\n")
}

// fileExists checks if a file exists
func fileExists(t *testing.T, path string) bool {
	t.Helper()

	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	t.Fatalf("Error checking file existence: %v", err)
	return false
}

// TestGetConfigValue tests the GetConfigValue function
func TestGetConfigValue(t *testing.T) {
	tests := []struct {
		name      string
		key       string
		setup     string
		want      []string
		setupFile bool
	}{
		{
			name:      "Existing key",
			key:       "hwdec",
			setup:     "hwdec=auto\n",
			want:      []string{"auto"},
			setupFile: true,
		},
		{
			name:      "Missing key",
			key:       "missing",
			setup:     "hwdec=auto\n",
			want:      []string{},
			setupFile: true,
		},
		{
			name:      "Array values",
			key:       "alang",
			setup:     "alang=en,fr,de\n",
			want:      []string{"en", "fr", "de"},
			setupFile: true,
		},
		{
			name:      "Inline comment",
			key:       "hwdec",
			setup:     "hwdec=auto  # Hardware decoder\n",
			want:      []string{"auto"},
			setupFile: true,
		},
		{
			name:      "Whitespace trimming",
			key:       "vo",
			setup:     "vo=  gpu  \n",
			want:      []string{"gpu"},
			setupFile: true,
		},
		{
			name:      "Empty array",
			key:       "alang",
			setup:     "alang=\n",
			want:      []string{},
			setupFile: true,
		},
		{
			name:      "No such file",
			key:       "hwdec",
			want:      []string{},
			setupFile: false,
		},
		{
			name:      "Comment only line before key",
			key:       "hwdec",
			setup:     "# This is a comment\nhwdec=auto\n",
			want:      []string{"auto"},
			setupFile: true,
		},
		{
			name:      "Mixed case key",
			key:       "HWDEC",
			setup:     "hwdec=auto\n",
			want:      []string{},
			setupFile: true,
		},
		{
			name:      "Array with empty values",
			key:       "alang",
			setup:     "alang=en,,fr,,de\n",
			want:      []string{"en", "fr", "de"},
			setupFile: true,
		},
		{
			name:      "Array with whitespace",
			key:       "alang",
			setup:     "alang= en , fr , de \n",
			want:      []string{"en", "fr", "de"},
			setupFile: true,
		},
		{
			name:      "Key with multiple lines (should return first match)",
			key:       "hwdec",
			setup:     "hwdec=auto\nhwdec=nvdec\n",
			want:      []string{"auto"},
			setupFile: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var originalHome string

			if tt.setupFile {
				// Set HOME to temp directory so GetConfigPath finds our file
				originalHome = os.Getenv("HOME")
				tmpDir := t.TempDir()
				defer os.Setenv("HOME", originalHome)
				os.Setenv("HOME", tmpDir)

				// Create the .config/mpv directory structure
				configDir := filepath.Join(tmpDir, ".config", "mpv")
				if err := os.MkdirAll(configDir, 0755); err != nil {
					t.Fatalf("Failed to create config dir: %v", err)
				}

				// Create the config file
				configPath := filepath.Join(configDir, "mpv.conf")
				if err := os.WriteFile(configPath, []byte(tt.setup), 0644); err != nil {
					t.Fatalf("Failed to create config file: %v", err)
				}

				// Skip Windows tests that require GetInstallPath()
				if runtime.GOOS == "windows" {
					t.Skip("Skipping Windows test (requires GetInstallPath setup)")
				}
			} else {
				// Set HOME to temp dir where no config file exists
				originalHome = os.Getenv("HOME")
				tmpDir := t.TempDir()
				defer os.Setenv("HOME", originalHome)
				os.Setenv("HOME", tmpDir)

				if runtime.GOOS == "windows" {
					t.Skip("Skipping Windows test (requires GetInstallPath setup)")
				}
			}

			got := GetConfigValue(tt.key)
			assert.Equal(t, tt.want, got, "GetConfigValue() = %v, want %v", got, tt.want)
		})
	}
}

// TestSetConfigValue tests the SetConfigValue function
func TestSetConfigValue(t *testing.T) {
	tests := []struct {
		name             string
		key              string
		values           []string
		preserveComments bool
		initialContent   string
		wantErr          bool
	}{
		{
			name:             "New key",
			key:              "hwdec",
			values:           []string{"auto"},
			preserveComments: false,
			initialContent:   "# Config file\n",
			wantErr:          false,
		},
		{
			name:             "Replace existing key",
			key:              "vo",
			values:           []string{"gpu"},
			preserveComments: false,
			initialContent:   "vo=vdpau\n",
			wantErr:          false,
		},
		{
			name:             "Preserve comment",
			key:              "hwdec",
			values:           []string{"nvdec"},
			preserveComments: true,
			initialContent:   "hwdec=auto  # Hardware decoder\n",
			wantErr:          false,
		},
		{
			name:             "Don't preserve comment",
			key:              "hwdec",
			values:           []string{"nvdec"},
			preserveComments: false,
			initialContent:   "hwdec=auto  # Hardware decoder\n",
			wantErr:          false,
		},
		{
			name:             "Array values",
			key:              "alang",
			values:           []string{"en", "fr", "de"},
			preserveComments: false,
			initialContent:   "",
			wantErr:          false,
		},
		{
			name:             "Single value",
			key:              "scale",
			values:           []string{"lanczos"},
			preserveComments: false,
			initialContent:   "",
			wantErr:          false,
		},
		{
			name:             "Empty value",
			key:              "hwdec",
			values:           []string{},
			preserveComments: false,
			initialContent:   "",
			wantErr:          false,
		},
		{
			name:             "Multiple keys in file",
			key:              "hwdec",
			values:           []string{"nvdec"},
			preserveComments: false,
			initialContent:   "vo=gpu\nhwdec=auto\nalang=en\n",
			wantErr:          false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Skip Windows tests that require GetInstallPath()
			if runtime.GOOS == "windows" {
				t.Skip("Skipping Windows test (requires GetInstallPath setup)")
			}

			originalHome := os.Getenv("HOME")
			tmpDir := t.TempDir()
			defer os.Setenv("HOME", originalHome)
			os.Setenv("HOME", tmpDir)

			// Create the .config/mpv directory structure
			configDir := filepath.Join(tmpDir, ".config", "mpv")
			if err := os.MkdirAll(configDir, 0755); err != nil {
				t.Fatalf("Failed to create config dir: %v", err)
			}

			configPath := filepath.Join(configDir, "mpv.conf")

			// Create initial config file
			if err := os.WriteFile(configPath, []byte(tt.initialContent), 0644); err != nil {
				t.Fatalf("Failed to create initial config: %v", err)
			}

			// Set the config value
			err := SetConfigValue(tt.key, tt.values, tt.preserveComments)

			if tt.wantErr {
				assert.Error(t, err, "SetConfigValue() expected error")
				return
			}

			require.NoError(t, err, "SetConfigValue() failed")

			// Read file back and verify
			lines := readFileLines(t, configPath)
			found := false
			prefix := tt.key + "="

			for _, line := range lines {
				if strings.HasPrefix(strings.TrimSpace(line), prefix) {
					found = true

					// Check if comment preserved as expected
					if tt.preserveComments && len(tt.values) > 0 && strings.Contains(tt.initialContent, "#") {
						// Find the original line with comment
						for _, origLine := range strings.Split(tt.initialContent, "\n") {
							if strings.HasPrefix(strings.TrimSpace(origLine), prefix) && strings.Contains(origLine, "#") {
								assert.Contains(t, line, "#", "Comment should be preserved")
								break
							}
						}
					}

					// Check value
					valueStr := strings.TrimPrefix(strings.TrimSpace(line), prefix)
					if len(tt.values) == 0 {
						assert.Equal(t, "", valueStr, "Empty value should be set")
					} else if len(tt.values) == 1 {
						// Check value before any comment
						commentIndex := strings.Index(valueStr, "#")
						if commentIndex != -1 {
							valueStr = strings.TrimSpace(valueStr[:commentIndex])
						}
						assert.Equal(t, tt.values[0], valueStr, "Single value should match")
					} else {
						// Check array values
						commentIndex := strings.Index(valueStr, "#")
						if commentIndex != -1 {
							valueStr = strings.TrimSpace(valueStr[:commentIndex])
						}
						expected := strings.Join(tt.values, ",")
						assert.Equal(t, expected, valueStr, "Array values should match")
					}

					break
				}
			}

			assert.True(t, found, "Key %s not found in config file", tt.key)
		})
	}
}

// TestSetConfigValue_EmptyPreserveComment tests SetConfigValue with empty value and preserveComments=true
func TestSetConfigValue_EmptyPreserveComment(t *testing.T) {
	// Skip Windows tests that require GetInstallPath()
	if runtime.GOOS == "windows" {
		t.Skip("Skipping Windows test (requires GetInstallPath setup)")
	}

	originalHome := os.Getenv("HOME")
	tmpDir := t.TempDir()
	defer os.Setenv("HOME", originalHome)
	os.Setenv("HOME", tmpDir)

	// Create the .config/mpv directory structure
	configDir := filepath.Join(tmpDir, ".config", "mpv")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		t.Fatalf("Failed to create config dir: %v", err)
	}

	configPath := filepath.Join(configDir, "mpv.conf")

	// Create initial config file
	initialContent := "hwdec=auto  # Hardware decoder\n"
	if err := os.WriteFile(configPath, []byte(initialContent), 0644); err != nil {
		t.Fatalf("Failed to create initial config: %v", err)
	}

	// Set to empty value with preserveComments=true
	err := SetConfigValue("hwdec", []string{}, true)
	require.NoError(t, err, "SetConfigValue() failed")

	// Read file back
	lines := readFileLines(t, configPath)
	found := false
	for _, line := range lines {
		if strings.HasPrefix(strings.TrimSpace(line), "hwdec=") {
			found = true
			// When preserveComments is true but value is empty, it should replace without comment
			// because the logic checks: if preserveComments && newValue != ""
			assert.Equal(t, "hwdec=", strings.TrimSpace(line), "Empty value should replace line")
			break
		}
	}
	assert.True(t, found, "Key not found in config file")
}

// TestSetConfigValue_CommentAfterEquals tests SetConfigValue with comment after equals
func TestSetConfigValue_CommentAfterEquals(t *testing.T) {
	// Skip Windows tests that require GetInstallPath()
	if runtime.GOOS == "windows" {
		t.Skip("Skipping Windows test (requires GetInstallPath setup)")
	}

	originalHome := os.Getenv("HOME")
	tmpDir := t.TempDir()
	defer os.Setenv("HOME", originalHome)
	os.Setenv("HOME", tmpDir)

	// Create the .config/mpv directory structure
	configDir := filepath.Join(tmpDir, ".config", "mpv")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		t.Fatalf("Failed to create config dir: %v", err)
	}

	configPath := filepath.Join(configDir, "mpv.conf")

	// Create config with comment after equals
	initialContent := "hwdec= # comment\n"
	if err := os.WriteFile(configPath, []byte(initialContent), 0644); err != nil {
		t.Fatalf("Failed to create config file: %v", err)
	}

	// Set new value with preserveComments=true
	err := SetConfigValue("hwdec", []string{"nvdec"}, true)
	require.NoError(t, err, "SetConfigValue() failed")

	// Read file back and verify value is updated (comment may or may not be preserved)
	got := GetConfigValue("hwdec")
	assert.Equal(t, []string{"nvdec"}, got, "Value should be updated")
}

// TestSetConfigValue_ErrorOnDirectoryCreate tests SetConfigValue when directory creation fails
func TestSetConfigValue_ErrorOnDirectoryCreate(t *testing.T) {
	// Skip Windows tests that require GetInstallPath()
	if runtime.GOOS == "windows" {
		t.Skip("Skipping Windows test (requires GetInstallPath setup)")
	}

	originalHome := os.Getenv("HOME")
	tmpDir := t.TempDir()
	defer os.Setenv("HOME", originalHome)
	os.Setenv("HOME", tmpDir)

	// Create a file instead of a directory to make mkdir fail
	configDir := filepath.Join(tmpDir, ".config")
	if err := os.WriteFile(configDir, []byte("test"), 0644); err != nil {
		t.Fatalf("Failed to create test file: %v", err)
	}

	// SetConfigValue should fail when it can't create the directory
	err := SetConfigValue("hwdec", []string{"auto"}, false)
	assert.Error(t, err, "SetConfigValue() should error when directory creation fails")
	assert.Contains(t, err.Error(), "failed to create config directory", "Error message should mention directory creation")
}

// TestGetConfigPath tests the GetConfigPath function
func TestGetConfigPath(t *testing.T) {
	tests := []struct {
		name      string
		contains  string
		setupHome bool
		homeValue string
	}{
		{
			name:      "Unix-like path",
			contains:  ".config/mpv/mpv.conf",
			setupHome: true,
			homeValue: "/home/testuser",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Skip on Windows (different logic)
			if runtime.GOOS == "windows" {
				t.Skip("Skipping Unix-specific path test on Windows")
			}

			// Save original HOME
			originalHome := os.Getenv("HOME")
			defer os.Setenv("HOME", originalHome)

			// Set test values
			if tt.setupHome {
				os.Setenv("HOME", tt.homeValue)
			}

			got := GetConfigPath()

			// Should contain expected path
			assert.Contains(t, got, tt.contains, "GetConfigPath() should contain %s", tt.contains)

			// If home was set, should also contain home value
			if tt.setupHome {
				assert.Contains(t, got, tt.homeValue, "GetConfigPath() should contain home dir")
			}
		})
	}
}

// TestGetConfigPath_Windows tests GetConfigPath for Windows
func TestGetConfigPath_Windows(t *testing.T) {
	// Only run on Windows or when testing Windows behavior
	if runtime.GOOS != "windows" {
		t.Skip("Skipping Windows-specific test on non-Windows platform")
	}

	// Save original HOME
	originalHome := os.Getenv("HOME")
	defer os.Setenv("HOME", originalHome)

	// Create temp config with install path
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)

	// Set up config with install path
	configDir := filepath.Join(tmpDir, ".config", "mpv-manager")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		t.Fatalf("Failed to create config dir: %v", err)
	}

	configFile := filepath.Join(configDir, "mpv-manager.json")
	configContent := `{
  "install_path": "/custom/mpv",
  "auto_update": false,
  "installed_apps": []
}`
	if err := os.WriteFile(configFile, []byte(configContent), 0644); err != nil {
		t.Fatalf("Failed to create config file: %v", err)
	}

	// Reset global config to force reload
	globalConfig = nil

	got := GetConfigPath()

	// Should contain portable_config path from install path
	assert.Contains(t, got, "portable_config", "GetConfigPath() on Windows should contain portable_config")
	assert.Contains(t, got, "mpv.conf", "GetConfigPath() should contain mpv.conf")
}

// TestParseConfig tests the ParseConfig function
func TestParseConfig(t *testing.T) {
	tests := []struct {
		name    string
		content string
		want    map[string][]string
		wantErr bool
	}{
		{
			name:    "Simple config",
			content: "hwdec=auto\nvo=gpu\n",
			want: map[string][]string{
				"hwdec": {"auto"},
				"vo":    {"gpu"},
			},
			wantErr: false,
		},
		{
			name:    "With comments",
			content: "# Hardware decoder\nhwdec=auto  # Comment\n",
			want: map[string][]string{
				"hwdec": {"auto"},
			},
			wantErr: false,
		},
		{
			name:    "Array values",
			content: "alang=en,fr,de\nslang=en,es,pt\n",
			want: map[string][]string{
				"alang": {"en", "fr", "de"},
				"slang": {"en", "es", "pt"},
			},
			wantErr: false,
		},
		{
			name:    "Empty file",
			content: "",
			want:    map[string][]string{},
			wantErr: false,
		},
		{
			name:    "Whitespace lines",
			content: "hwdec=auto\n\n\nvo=gpu\n",
			want: map[string][]string{
				"hwdec": {"auto"},
				"vo":    {"gpu"},
			},
			wantErr: false,
		},
		{
			name:    "Multiple comments",
			content: "# Comment 1\n# Comment 2\nhwdec=auto\n# Comment 3\n",
			want: map[string][]string{
				"hwdec": {"auto"},
			},
			wantErr: false,
		},
		{
			name:    "Empty values",
			content: "hwdec=\nvo=\n",
			want: map[string][]string{
				"hwdec": {},
				"vo":    {},
			},
			wantErr: false,
		},
		{
			name:    "Array with whitespace",
			content: "alang= en , fr , de \n",
			want: map[string][]string{
				"alang": {"en", "fr", "de"},
			},
			wantErr: false,
		},
		{
			name:    "Array with empty values",
			content: "alang=en,,fr,,de\n",
			want: map[string][]string{
				"alang": {"en", "fr", "de"},
			},
			wantErr: false,
		},
		{
			name:    "Skip invalid lines",
			content: "invalid_line\n# comment\nhwdec=auto\nno_equals_here\n",
			want: map[string][]string{
				"hwdec": {"auto"},
			},
			wantErr: false,
		},
		{
			name:    "Complex config",
			content: "# MPV Configuration\nvo=gpu\nhwdec=auto\nalang=en,fr,de\nscale=lanczos\n",
			want: map[string][]string{
				"vo":    {"gpu"},
				"hwdec": {"auto"},
				"alang": {"en", "fr", "de"},
				"scale": {"lanczos"},
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			configPath := createTempConfig(t, tt.content)

			got, err := ParseConfig(configPath)

			if tt.wantErr {
				assert.Error(t, err, "ParseConfig() expected error")
				return
			}

			require.NoError(t, err, "ParseConfig() failed")
			assert.Equal(t, tt.want, got, "ParseConfig() = %v, want %v", got, tt.want)
		})
	}
}

// TestParseConfig_NonExistentFile tests ParseConfig with non-existent file
func TestParseConfig_NonExistentFile(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "nonexistent.conf")

	got, err := ParseConfig(configPath)

	require.NoError(t, err, "ParseConfig() should not error on non-existent file")
	assert.NotNil(t, got, "ParseConfig() should return empty map for non-existent file")
	assert.Empty(t, got, "ParseConfig() should return empty map")
}

// TestWriteConfig tests the WriteConfig function
func TestWriteConfig(t *testing.T) {
	tests := []struct {
		name    string
		config  map[string][]string
		wantErr bool
	}{
		{
			name: "Simple write",
			config: map[string][]string{
				"hwdec": {"auto"},
				"vo":    {"gpu"},
			},
			wantErr: false,
		},
		{
			name: "Array values",
			config: map[string][]string{
				"alang": {"en", "fr", "de"},
			},
			wantErr: false,
		},
		{
			name:    "Empty config",
			config:  map[string][]string{},
			wantErr: false,
		},
		{
			name: "Empty values",
			config: map[string][]string{
				"hwdec": {},
				"vo":    {},
			},
			wantErr: false,
		},
		{
			name: "Mixed single and array values",
			config: map[string][]string{
				"hwdec": {"auto"},
				"alang": {"en", "fr"},
				"vo":    {"gpu"},
				"scale": {"lanczos"},
				"slang": {"en"},
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tmpDir := t.TempDir()
			configPath := filepath.Join(tmpDir, "test-mpv.conf")

			err := WriteConfig(configPath, tt.config)

			if tt.wantErr {
				assert.Error(t, err, "WriteConfig() expected error")
				return
			}

			require.NoError(t, err, "WriteConfig() failed")

			// Verify file exists
			assert.True(t, fileExists(t, configPath), "WriteConfig() should create file")

			// Read file back and verify content
			content, err := os.ReadFile(configPath)
			require.NoError(t, err, "Failed to read written config file")

			lines := strings.Split(string(content), "\n")

			// Parse and verify
			parsed := make(map[string][]string)
			for _, line := range lines {
				line = strings.TrimSpace(line)
				if line == "" {
					continue
				}

				equalIndex := strings.Index(line, "=")
				if equalIndex == -1 {
					continue
				}

				key := strings.TrimSpace(line[:equalIndex])
				value := strings.TrimSpace(line[equalIndex+1:])

				if value == "" {
					parsed[key] = []string{}
				} else {
					parsed[key] = strings.Split(value, ",")
					for i := range parsed[key] {
						parsed[key][i] = strings.TrimSpace(parsed[key][i])
					}
				}
			}

			assert.Equal(t, tt.config, parsed, "Written config should match original config")
		})
	}
}

// TestWriteConfig_Atomically tests atomic write behavior
func TestWriteConfig_Atomically(t *testing.T) {
	tests := []struct {
		name    string
		config  map[string][]string
		wantErr bool
	}{
		{
			name: "Write succeeds",
			config: map[string][]string{
				"hwdec": {"auto"},
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tmpDir := t.TempDir()
			configPath := filepath.Join(tmpDir, "atomic-test.conf")

			err := WriteConfig(configPath, tt.config)

			if tt.wantErr {
				assert.Error(t, err, "WriteConfig() expected error")
				return
			}

			require.NoError(t, err, "WriteConfig() failed")

			// Verify final file exists
			assert.True(t, fileExists(t, configPath), "Final config file should exist")

			// Verify temp file was cleaned up
			tempPath := configPath + ".tmp"
			assert.False(t, fileExists(t, tempPath), "Temp file should be cleaned up")

			// Verify content is correct
			content, err := os.ReadFile(configPath)
			require.NoError(t, err, "Failed to read final config file")

			// Should contain to expected key=value
			expectedLine := "hwdec=auto"
			assert.Contains(t, string(content), expectedLine, "Final file should contain expected content")
		})
	}
}

// TestWriteConfig_Overwrite tests overwriting existing config
func TestWriteConfig_Overwrite(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "overwrite-test.conf")

	// Write initial config
	initialConfig := map[string][]string{
		"hwdec": {"auto"},
		"vo":    {"vdpau"},
	}
	err := WriteConfig(configPath, initialConfig)
	require.NoError(t, err, "WriteConfig() failed for initial config")

	// Overwrite with new config
	newConfig := map[string][]string{
		"hwdec": {"nvdec"},
		"vo":    {"gpu"},
		"alang": {"en", "fr"},
	}
	err = WriteConfig(configPath, newConfig)
	require.NoError(t, err, "WriteConfig() failed for new config")

	// Verify new content
	parsed, err := ParseConfig(configPath)
	require.NoError(t, err, "Failed to parse config after overwrite")

	assert.Equal(t, newConfig, parsed, "Config should be overwritten correctly")
}

// TestWriteConfig_MultipleValues tests writing config with multiple values per key
func TestWriteConfig_MultipleValues(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "multi-test.conf")

	config := map[string][]string{
		"alang": {"en", "fr", "de", "es", "it", "pt", "ru", "ja", "ko"},
	}

	err := WriteConfig(configPath, config)
	require.NoError(t, err, "WriteConfig() failed")

	// Read back and verify
	content, err := os.ReadFile(configPath)
	require.NoError(t, err, "Failed to read config")

	expected := "alang=en,fr,de,es,it,pt,ru,ja,ko"
	assert.Contains(t, string(content), expected, "Multiple values should be comma-separated")

	// Parse and verify
	parsed, err := ParseConfig(configPath)
	require.NoError(t, err, "Failed to parse config")

	assert.Equal(t, config, parsed, "Parsed config should match original")
}

// TestGetConfigValue_ReadError tests GetConfigValue when file read fails
func TestGetConfigValue_ReadError(t *testing.T) {
	// Skip Windows tests that require GetInstallPath()
	if runtime.GOOS == "windows" {
		t.Skip("Skipping Windows test (requires GetInstallPath setup)")
	}

	originalHome := os.Getenv("HOME")
	tmpDir := t.TempDir()
	defer os.Setenv("HOME", originalHome)
	os.Setenv("HOME", tmpDir)

	// Create the .config/mpv directory structure
	configDir := filepath.Join(tmpDir, ".config", "mpv")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		t.Fatalf("Failed to create config dir: %v", err)
	}

	configPath := filepath.Join(configDir, "mpv.conf")

	// Create a directory instead of a file to cause read error
	if err := os.Mkdir(configPath, 0755); err != nil {
		t.Fatalf("Failed to create directory: %v", err)
	}

	// Should return empty array on error
	got := GetConfigValue("hwdec")
	assert.Equal(t, []string{}, got, "GetConfigValue() should return empty array on error")
}

// TestParseConfig_ReadError tests ParseConfig when file read fails
func TestParseConfig_ReadError(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "test-dir")

	// Create a directory instead of a file
	if err := os.Mkdir(configPath, 0755); err != nil {
		t.Fatalf("Failed to create directory: %v", err)
	}

	_, err := ParseConfig(configPath)
	assert.Error(t, err, "ParseConfig() should error when reading directory")
}

// TestWriteConfig_ReadOnlyPath tests WriteConfig when path is read-only
func TestWriteConfig_ReadOnlyPath(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "readonly-parent", "test.conf")

	// Create a read-only parent directory
	readonlyDir := filepath.Join(tmpDir, "readonly-parent")
	if err := os.Mkdir(readonlyDir, 0444); err != nil {
		t.Fatalf("Failed to create read-only directory: %v", err)
	}

	config := map[string][]string{
		"hwdec": {"auto"},
	}

	// On some systems, we can't enforce read-only on directories
	// We'll try to write and see if it fails
	err := WriteConfig(configPath, config)
	if err != nil {
		assert.Error(t, err, "WriteConfig() should error when parent is read-only")
	}

	// Clean up
	os.Chmod(readonlyDir, 0755)
}

// TestSetConfigValue_EmbeddedAsset tests SetConfigValue when creating from embedded asset
func TestSetConfigValue_EmbeddedAsset(t *testing.T) {
	// Skip Windows tests that require GetInstallPath()
	if runtime.GOOS == "windows" {
		t.Skip("Skipping Windows test (requires GetInstallPath setup)")
	}

	originalHome := os.Getenv("HOME")
	tmpDir := t.TempDir()
	defer os.Setenv("HOME", originalHome)
	os.Setenv("HOME", tmpDir)

	// Create the .config/mpv directory structure
	configDir := filepath.Join(tmpDir, ".config", "mpv")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		t.Fatalf("Failed to create config dir: %v", err)
	}

	// Don't create mpv.conf - it should be created from embedded asset
	err := SetConfigValue("hwdec", []string{"auto"}, false)
	require.NoError(t, err, "SetConfigValue() failed")

	// Verify config file was created
	configPath := filepath.Join(configDir, "mpv.conf")
	assert.True(t, fileExists(t, configPath), "Config file should be created from embedded asset")

	// Verify value was set
	got := GetConfigValue("hwdec")
	assert.Equal(t, []string{"auto"}, got, "Value should be set correctly")
}
