package web

import (
	"testing"
)

// TestValidateConfigValue tests config value validation
func TestValidateConfigValue(t *testing.T) {
	tests := []struct {
		name      string
		key       string
		value     string
		wantError bool
		errorMsg  string
	}{
		// Valid hwdec values
		{"Valid HWA - auto", "hwdec", "auto", false, ""},
		{"Valid HWA - auto-safe", "hwdec", "auto-safe", false, ""},
		{"Valid HWA - nvdec", "hwdec", "nvdec", false, ""},
		{"Valid HWA - nvdec-copy", "hwdec", "nvdec-copy", false, ""},
		{"Valid HWA - vaapi", "hwdec", "vaapi", false, ""},
		{"Valid HWA - vaapi-copy", "hwdec", "vaapi-copy", false, ""},
		{"Valid HWA - vulkan", "hwdec", "vulkan", false, ""},
		{"Valid HWA - vulkan-copy", "hwdec", "vulkan-copy", false, ""},
		{"Valid HWA - d3d11va", "hwdec", "d3d11va", false, ""},
		{"Valid HWA - d3d11va-copy", "hwdec", "d3d11va-copy", false, ""},
		{"Valid HWA - videotoolbox", "hwdec", "videotoolbox", false, ""},
		{"Valid HWA - videotoolbox-copy", "hwdec", "videotoolbox-copy", false, ""},
		{"Valid HWA - drm", "hwdec", "drm", false, ""},
		{"Valid HWA - drm-copy", "hwdec", "drm-copy", false, ""},
		{"Valid HWA - no", "hwdec", "no", false, ""},

		// Invalid hwdec values
		{"Invalid HWA - invalid method", "hwdec", "invalid", true, "invalid value 'invalid' for key 'hwdec'"},
		{"Invalid HWA - typo", "hwdec", "vdau", true, "invalid value 'vdau' for key 'hwdec'"},

		// Valid vo values
		{"Valid VO - gpu", "vo", "gpu", false, ""},
		{"Valid VO - vdpau", "vo", "vdpau", false, ""},
		{"Valid VO - xv", "vo", "xv", false, ""},

		// Invalid vo values
		{"Invalid VO - special chars", "vo", "gpu$invalid", true, "invalid value"},

		// Valid scale values
		{"Valid scale - bilinear", "scale", "bilinear", false, ""},
		{"Valid scale - lanczos", "scale", "lanczos", false, ""},

		// Invalid scale values
		{"Invalid scale - special chars", "scale", "scale$invalid", true, "invalid value"},

		// Valid dither values
		{"Valid dither - no", "dither-depth-convert", "no", false, ""},
		{"Valid dither - ordered", "dither-depth-convert", "ordered", false, ""},
		{"Valid dither - error-diffusion", "dither-depth-convert", "error-diffusion", false, ""},
		{"Valid dither - floyd-steinberg", "dither-depth-convert", "floyd-steinberg", false, ""},

		// Invalid dither values
		{"Invalid dither - invalid", "dither-depth-convert", "invalid", true, "invalid value"},

		// Invalid keys
		{"Invalid key", "invalid_key", "value", true, "invalid config key: invalid_key"},
		{"Invalid key - hwdec typo", "hwde", "auto", true, "invalid config key: hwde"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateConfigValue(tt.key, tt.value)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateConfigValue() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateConfigValue() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestValidateMethodID tests method ID validation
func TestValidateMethodID(t *testing.T) {
	tests := []struct {
		name      string
		methodID  string
		wantError bool
		errorMsg  string
	}{
		// Valid method IDs
		{"Valid method - mpv-binary", "mpv-binary", false, ""},
		{"Valid method - mpv-binary-v3", "mpv-binary-v3", false, ""},
		{"Valid method - mpc-qt", "mpc-qt", false, ""},
		{"Valid method - mpv-app", "mpv-app", false, ""},
		{"Valid method - mpv-brew", "mpv-brew", false, ""},
		{"Valid method - iina", "iina", false, ""},
		{"Valid method - mpv-flatpak", "mpv-flatpak", false, ""},
		{"Valid method - celluloid-flatpak", "celluloid-flatpak", false, ""},
		{"Valid method - mpv-package", "mpv-package", false, ""},
		{"Valid method - celluloid-package", "celluloid-package", false, ""},

		// Invalid method IDs
		{"Invalid method - empty", "", true, "invalid method ID"},
		{"Invalid method - typo", "mpv-inary", true, "invalid method ID"},
		{"Invalid method - case sensitive", "MPV-BINARY", true, "invalid method ID"},
		{"Invalid method - special chars", "mpv-binary$", true, "invalid method ID"},
		{"Invalid method - random", "random-method", true, "invalid method ID"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateMethodID(tt.methodID)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateMethodID() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateMethodID() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestValidateAppName tests app name validation
func TestValidateAppName(t *testing.T) {
	tests := []struct {
		name      string
		appName   string
		wantError bool
		errorMsg  string
	}{
		// Valid app names
		{"Valid app - MPV", "MPV", false, ""},
		{"Valid app - IINA", "IINA", false, ""},
		{"Valid app - MPC-QT", "MPC-QT", false, ""},
		{"Valid app - Celluloid", "Celluloid", false, ""},
		{"Valid app - with spaces", "My Player", false, ""},
		{"Valid app - with periods", "MPV.Player", false, ""},

		// Invalid app names
		{"Invalid app - empty", "", true, "app name cannot be empty"},
		{"Invalid app - special chars", "MPV$", true, "must contain only letters"},
		{"Invalid app - slashes", "MPV/Test", true, "must contain only letters"},
		{"Invalid app - too long", "MPVPlayerWithAVeryLongNameThatExceedsTheMaximumAllowedCharacterLimitOfOneHundredCharactersAndShouldBeRejected", true, "app name too long"},
		{"Invalid app - unicode", "MPV播放器", true, "must contain only letters"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateAppName(tt.appName)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateAppName() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateAppName() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestValidateAppType tests app type validation
func TestValidateAppType(t *testing.T) {
	tests := []struct {
		name      string
		appType   string
		wantError bool
		errorMsg  string
	}{
		// Valid app types
		{"Valid type - app", "app", false, ""},
		{"Valid type - frontend", "frontend", false, ""},

		// Legacy types (normalized automatically)
		{"Legacy type - flatpak", "flatpak", false, ""},
		{"Legacy type - package-manager", "package-manager", false, ""},
		{"Legacy type - executable", "executable", false, ""},
		{"Legacy type - portable", "portable", false, ""},

		// Old app names (pre-Feb 2026, normalized automatically)
		{"Legacy app name - MPV", "MPV", false, ""},
		{"Legacy app name - IINA", "IINA", false, ""},
		{"Legacy app name - MPC-QT", "MPC-QT", false, ""},
		{"Legacy app name - Celluloid", "Celluloid", false, ""},

		// Invalid app types
		{"Invalid type - empty", "", true, "invalid app type"},
		{"Invalid type - player", "player", true, "must be 'app' or 'frontend'"},
		{"Invalid type - application", "application", true, "must be 'app' or 'frontend'"},
		{"Invalid type - random", "random", true, "must be 'app' or 'frontend'"},
		{"Invalid type - case sensitive", "App", true, "must be 'app' or 'frontend'"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateAppType(tt.appType)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateAppType() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateAppType() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestNormalizeAppType tests app type normalization
func TestNormalizeAppType(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		// Standard types - unchanged
		{"Standard - app", "app", "app"},
		{"Standard - frontend", "frontend", "frontend"},

		// Legacy install types - normalize to "app"
		{"Legacy - flatpak", "flatpak", "app"},
		{"Legacy - package-manager", "package-manager", "app"},
		{"Legacy - executable", "executable", "app"},
		{"Legacy - portable", "portable", "app"},

		// Old app names (pre-Feb 2026)
		{"Old app name - MPV", "MPV", "app"},
		{"Old app name - IINA", "IINA", "app"},
		{"Old app name - MPC-QT", "MPC-QT", "app"},
		{"Old app name - Celluloid", "Celluloid", "frontend"},

		// Unknown types - returned as-is
		{"Unknown - random", "random", "random"},
		{"Unknown - empty", "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := NormalizeAppType(tt.input)
			if result != tt.expected {
				t.Errorf("NormalizeAppType(%q) = %q, expected %q", tt.input, result, tt.expected)
			}
		})
	}
}

// TestValidateLanguageCode tests language code validation
func TestValidateLanguageCode(t *testing.T) {
	tests := []struct {
		name      string
		code      string
		wantError bool
		errorMsg  string
	}{
		// Valid language codes
		{"Valid code - en", "en", false, ""},
		{"Valid code - zh", "zh", false, ""},
		{"Valid code - es", "es", false, ""},
		{"Valid code - en-US", "en-US", false, ""},
		{"Valid code - zh-CN", "zh-CN", false, ""},
		{"Valid code - en-GB", "en-GB", false, ""},
		{"Valid code - empty", "", false, ""},

		// Invalid language codes
		{"Invalid code - single letter", "e", true, "invalid language code"},
		{"Invalid code - uppercase", "EN", true, "invalid language code"},
		{"Invalid code - three letter", "eng", true, "invalid language code"},
		{"Invalid code - lowercase country", "en-us", true, "invalid language code"},
		{"Invalid code - special chars", "en_US", true, "invalid language code"},
		{"Invalid code - number", "e1", true, "invalid language code"},
		{"Invalid code - random", "english", true, "invalid language code"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateLanguageCode(tt.code)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateLanguageCode() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateLanguageCode() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestValidateHWAValue tests hardware acceleration method validation
func TestValidateHWAValue(t *testing.T) {
	tests := []struct {
		name      string
		method    string
		wantError bool
		errorMsg  string
	}{
		// Valid HWA methods
		{"Valid HWA - auto", "auto", false, ""},
		{"Valid HWA - auto-safe", "auto-safe", false, ""},
		{"Valid HWA - nvdec", "nvdec", false, ""},
		{"Valid HWA - nvdec-copy", "nvdec-copy", false, ""},
		{"Valid HWA - vaapi", "vaapi", false, ""},
		{"Valid HWA - vaapi-copy", "vaapi-copy", false, ""},
		{"Valid HWA - vulkan", "vulkan", false, ""},
		{"Valid HWA - vulkan-copy", "vulkan-copy", false, ""},
		{"Valid HWA - d3d11va", "d3d11va", false, ""},
		{"Valid HWA - d3d11va-copy", "d3d11va-copy", false, ""},
		{"Valid HWA - videotoolbox", "videotoolbox", false, ""},
		{"Valid HWA - videotoolbox-copy", "videotoolbox-copy", false, ""},
		{"Valid HWA - drm", "drm", false, ""},
		{"Valid HWA - drm-copy", "drm-copy", false, ""},
		{"Valid HWA - no", "no", false, ""},
		{"Valid HWA - empty", "", false, ""},

		// Invalid HWA methods
		{"Invalid HWA - typo", "vdau", true, "invalid hardware acceleration method"},
		{"Invalid HWA - case sensitive", "Auto", true, "invalid hardware acceleration method"},
		{"Invalid HWA - partial", "nvde", true, "invalid hardware acceleration method"},
		{"Invalid HWA - random", "random", true, "invalid hardware acceleration method"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateHWAValue(tt.method)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateHWAValue() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateHWAValue() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// TestValidateInstallMethod tests install method validation with platform compatibility
func TestValidateInstallMethod(t *testing.T) {
	tests := []struct {
		name      string
		methodID  string
		isWindows bool
		isDarwin  bool
		isLinux   bool
		wantError bool
		errorMsg  string
	}{
		// Windows-only methods
		{"MPC-QT on Windows", "mpc-qt", true, false, false, false, ""},
		{"MPC-QT on macOS", "mpc-qt", false, true, false, true, "only available on Windows"},
		{"MPC-QT on Linux", "mpc-qt", false, false, true, true, "only available on Windows"},

		// macOS-only methods
		{"MPV App on macOS", "mpv-app", false, true, false, false, ""},
		{"MPV App on Windows", "mpv-app", true, false, false, true, "only available on macOS"},
		{"MPV App on Linux", "mpv-app", false, false, true, true, "only available on macOS"},
		{"IINA on macOS", "iina", false, true, false, false, ""},
		{"IINA on Windows", "iina", true, false, false, true, "only available on macOS"},
		{"IINA on Linux", "iina", false, false, true, true, "only available on macOS"},

		// Platform-specific methods
		{"MPV Binary on Windows", "mpv-binary", true, false, false, false, ""},
		{"MPV Binary on Linux", "mpv-binary", false, false, true, false, ""},
		{"MPV Binary V3 on Windows", "mpv-binary-v3", true, false, false, false, ""},
		{"MPV Binary V3 on Linux", "mpv-binary-v3", false, false, true, false, ""},
		{"MPV Binary on macOS", "mpv-binary", false, true, false, true, "not available on macOS"},
		{"MPV Binary V3 on macOS", "mpv-binary-v3", false, true, false, true, "not available on macOS"},

		// Package manager methods (not available on Windows)
		{"MPV Brew on macOS", "mpv-brew", false, true, false, false, ""},
		{"MPV Brew on Windows", "mpv-brew", true, false, false, true, "not available on Windows"},
		{"MPV Flatpak on Linux", "mpv-flatpak", false, false, true, false, ""},
		{"MPV Flatpak on Windows", "mpv-flatpak", true, false, false, true, "not available on Windows"},
		{"MPV Package on Linux", "mpv-package", false, false, true, false, ""},
		{"MPV Package on Windows", "mpv-package", true, false, false, true, "not available on Windows"},
		{"Celluloid Flatpak on Linux", "celluloid-flatpak", false, false, true, false, ""},
		{"Celluloid Flatpak on Windows", "celluloid-flatpak", true, false, false, true, "not available on Windows"},
		{"Celluloid Package on Linux", "celluloid-package", false, false, true, false, ""},
		{"Celluloid Package on Windows", "celluloid-package", true, false, false, true, "not available on Windows"},

		// Invalid method ID
		{"Invalid method", "invalid-method", true, false, false, true, "invalid method ID"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateInstallMethod(tt.methodID, tt.isWindows, tt.isDarwin, tt.isLinux)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateInstallMethod() error = %v, wantError %v", err, tt.wantError)
				return
			}
			if tt.wantError && err != nil {
				if !contains(err.Error(), tt.errorMsg) {
					t.Errorf("ValidateInstallMethod() error = %v, expected substring %v", err.Error(), tt.errorMsg)
				}
			}
		})
	}
}

// Helper function to check if string contains substring
func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && findSubstring(s, substr))
}

func findSubstring(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}
