package platform

import (
	"runtime"
	"testing"

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

// TestIsWindows tests Windows platform detection
func TestIsWindows(t *testing.T) {
	tests := []struct {
		name   string
		osType OSType
		want   bool
	}{
		{
			name:   "Windows platform",
			osType: Windows,
			want:   true,
		},
		{
			name:   "Darwin platform",
			osType: Darwin,
			want:   false,
		},
		{
			name:   "Linux platform",
			osType: Linux,
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := &Platform{OSType: tt.osType}
			assert.Equal(t, tt.want, p.IsWindows())
		})
	}
}

// TestIsDarwin tests macOS platform detection
func TestIsDarwin(t *testing.T) {
	tests := []struct {
		name   string
		osType OSType
		want   bool
	}{
		{
			name:   "Darwin platform",
			osType: Darwin,
			want:   true,
		},
		{
			name:   "Windows platform",
			osType: Windows,
			want:   false,
		},
		{
			name:   "Linux platform",
			osType: Linux,
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := &Platform{OSType: tt.osType}
			assert.Equal(t, tt.want, p.IsDarwin())
		})
	}
}

// TestIsLinux tests Linux platform detection
func TestIsLinux(t *testing.T) {
	tests := []struct {
		name   string
		osType OSType
		want   bool
	}{
		{
			name:   "Linux platform",
			osType: Linux,
			want:   true,
		},
		{
			name:   "Windows platform",
			osType: Windows,
			want:   false,
		},
		{
			name:   "Darwin platform",
			osType: Darwin,
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := &Platform{OSType: tt.osType}
			assert.Equal(t, tt.want, p.IsLinux())
		})
	}
}

// TestIsImmutableDistro tests immutable distro detection
func TestIsImmutableDistro(t *testing.T) {
	tests := []struct {
		name          string
		osType        OSType
		distro        string
		wantImmutable bool
	}{
		{
			name:          "Fedora Atomic",
			osType:        Linux,
			distro:        "fedora-atomic",
			wantImmutable: true,
		},
		{
			name:          "Fedora Silverblue",
			osType:        Linux,
			distro:        "fedora-silverblue",
			wantImmutable: true,
		},
		{
			name:          "Fedora Kinoite",
			osType:        Linux,
			distro:        "fedora-kinoite",
			wantImmutable: true,
		},
		{
			name:          "Endless OS",
			osType:        Linux,
			distro:        "endless",
			wantImmutable: true,
		},
		{
			name:          "Vanilla OS",
			osType:        Linux,
			distro:        "Vanilla",
			wantImmutable: true,
		},
		{
			name:          "Bluefin",
			osType:        Linux,
			distro:        "bluefin",
			wantImmutable: true,
		},
		{
			name:          "Bazzite",
			osType:        Linux,
			distro:        "bazzite",
			wantImmutable: true,
		},
		{
			name:          "Universal Blue",
			osType:        Linux,
			distro:        "universal-blue",
			wantImmutable: true,
		},
		{
			name:          "Aurora",
			osType:        Linux,
			distro:        "aurora",
			wantImmutable: true,
		},
		{
			name:          "Cosmic",
			osType:        Linux,
			distro:        "cosmic",
			wantImmutable: true,
		},
		{
			name:          "Ubuntu",
			osType:        Linux,
			distro:        "Ubuntu",
			wantImmutable: false,
		},
		{
			name:          "Fedora",
			osType:        Linux,
			distro:        "Fedora",
			wantImmutable: false,
		},
		{
			name:          "Arch Linux",
			osType:        Linux,
			distro:        "Arch Linux",
			wantImmutable: false,
		},
		{
			name:          "Windows",
			osType:        Windows,
			distro:        "",
			wantImmutable: false,
		},
		{
			name:          "macOS",
			osType:        Darwin,
			distro:        "",
			wantImmutable: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := &Platform{OSType: tt.osType, Distro: tt.distro}
			assert.Equal(t, tt.wantImmutable, p.IsImmutableDistro())
		})
	}
}

// TestGetMacOSVersionName tests macOS version name mapping
func TestGetMacOSVersionName(t *testing.T) {
	tests := []struct {
		name     string
		version  string
		wantName string
	}{
		{
			name:     "Cheetah",
			version:  "10.0",
			wantName: "Cheetah",
		},
		{
			name:     "Tiger",
			version:  "10.4",
			wantName: "Tiger",
		},
		{
			name:     "Snow Leopard",
			version:  "10.6",
			wantName: "Snow Leopard",
		},
		{
			name:     "Lion",
			version:  "10.7",
			wantName: "Lion",
		},
		{
			name:     "Mojave",
			version:  "10.14",
			wantName: "Mojave",
		},
		{
			name:     "Catalina",
			version:  "10.15",
			wantName: "Catalina",
		},
		{
			name:     "Big Sur",
			version:  "11",
			wantName: "Big Sur",
		},
		{
			name:     "Monterey",
			version:  "12",
			wantName: "Monterey",
		},
		{
			name:     "Ventura",
			version:  "13",
			wantName: "Ventura",
		},
		{
			name:     "Sonoma",
			version:  "14",
			wantName: "Sonoma",
		},
		{
			name:     "Sequoia",
			version:  "15",
			wantName: "Sequoia",
		},
		{
			name:     "Unknown version",
			version:  "99",
			wantName: "99",
		},
		{
			name:     "Empty version",
			version:  "",
			wantName: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := getMacOSVersionName(tt.version)
			assert.Equal(t, tt.wantName, got)
		})
	}
}

// TestPlatformString tests platform string representation
func TestPlatformString(t *testing.T) {
	tests := []struct {
		name     string
		platform Platform
		want     string
	}{
		{
			name: "Windows platform",
			platform: Platform{
				OSType: Windows,
				Arch:   "amd64",
			},
			want: "windows amd64",
		},
		{
			name: "Linux platform",
			platform: Platform{
				OSType:       Linux,
				Arch:         "x86_64",
				Distro:       "Ubuntu",
				DistroFamily: "debian",
			},
			want: "linux x86_64 (Ubuntu/debian)",
		},
		{
			name: "Linux platform without family",
			platform: Platform{
				OSType: Linux,
				Arch:   "amd64",
				Distro: "Arch",
			},
			want: "linux amd64 (Arch)",
		},
		{
			name: "macOS with version and name",
			platform: Platform{
				OSType:  Darwin,
				Arch:    "arm64",
				Version: "14",
				CPUInfo: CPUInfo{Model: "Apple M1"},
			},
			want: "macOS 14 - Sonoma (Apple M1)",
		},
		{
			name: "macOS with version only",
			platform: Platform{
				OSType:  Darwin,
				Arch:    "x86_64",
				Version: "13",
				CPUInfo: CPUInfo{Model: "Intel Core i7"},
			},
			want: "macOS 13 - Ventura (Intel Core i7)",
		},
		{
			name: "macOS without CPU info",
			platform: Platform{
				OSType:  Darwin,
				Arch:    "arm64",
				Version: "15",
				CPUInfo: CPUInfo{},
			},
			want: "macOS 15 - Sequoia",
		},
		{
			name: "Linux with empty distro",
			platform: Platform{
				OSType: Linux,
				Arch:   "amd64",
			},
			want: "linux amd64",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := tt.platform.String()
			assert.Equal(t, tt.want, got)
		})
	}
}

// TestSupportsX86_64_v3 tests x86-64-v3 support detection
func TestSupportsX86_64_v3(t *testing.T) {
	tests := []struct {
		name     string
		platform Platform
		want     bool
	}{
		{
			name: "amd64 with AVX2 support",
			platform: Platform{
				Arch: "amd64",
				CPUInfo: CPUInfo{
					SupportsAVX2: true,
				},
			},
			want: true,
		},
		{
			name: "x86_64 with AVX2 support",
			platform: Platform{
				Arch: "x86_64",
				CPUInfo: CPUInfo{
					SupportsAVX2: true,
				},
			},
			want: true,
		},
		{
			name: "amd64 without AVX2 support",
			platform: Platform{
				Arch: "amd64",
				CPUInfo: CPUInfo{
					SupportsAVX2: false,
				},
			},
			want: false,
		},
		{
			name: "x86_64 without AVX2 support",
			platform: Platform{
				Arch: "x86_64",
				CPUInfo: CPUInfo{
					SupportsAVX2: false,
				},
			},
			want: false,
		},
		{
			name: "arm64 architecture",
			platform: Platform{
				Arch: "arm64",
				CPUInfo: CPUInfo{
					SupportsAVX2: false,
				},
			},
			want: false,
		},
		{
			name: "386 architecture",
			platform: Platform{
				Arch: "386",
				CPUInfo: CPUInfo{
					SupportsAVX2: false,
				},
			},
			want: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := tt.platform.SupportsX86_64_v3()
			assert.Equal(t, tt.want, got)
		})
	}
}

// TestDetect detects current platform
func TestDetect(t *testing.T) {
	p := Detect()

	assert.NotNil(t, p)
	assert.NotEmpty(t, p.OSType)
	assert.NotEmpty(t, p.Arch)

	if runtime.GOOS == "linux" {
		assert.Equal(t, Linux, p.OSType)
	} else if runtime.GOOS == "darwin" {
		assert.Equal(t, Darwin, p.OSType)
	} else if runtime.GOOS == "windows" {
		assert.Equal(t, Windows, p.OSType)
	}

	assert.Equal(t, runtime.GOARCH, p.Arch)
}

// TestCheckAVX2 tests AVX2 feature detection
func TestCheckAVX2(t *testing.T) {
	tests := []struct {
		name     string
		features []string
		want     bool
	}{
		{
			name:     "AVX2 present",
			features: []string{"sse", "sse2", "avx", "avx2"},
			want:     true,
		},
		{
			name:     "AVX2 uppercase",
			features: []string{"AVX2", "SSE4_2"},
			want:     true,
		},
		{
			name:     "No AVX2",
			features: []string{"sse", "sse2", "avx"},
			want:     false,
		},
		{
			name:     "Empty features",
			features: []string{},
			want:     false,
		},
		{
			name:     "Mixed case AVX2",
			features: []string{"AvX2"},
			want:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := checkAVX2(tt.features)
			assert.Equal(t, tt.want, got)
		})
	}
}

// TestGetRecommendedHWADecoder tests platform-specific hardware decoder recommendations
func TestGetRecommendedHWADecoder(t *testing.T) {
	tests := []struct {
		name      string
		osType    OSType
		gpuBrand  string
		wantValue string
	}{
		// Windows
		{
			name:      "Windows + Nvidia",
			osType:    Windows,
			gpuBrand:  "nvidia",
			wantValue: "nvdec-copy,auto",
		},
		{
			name:      "Windows + AMD",
			osType:    Windows,
			gpuBrand:  "amd",
			wantValue: "vulkan,auto",
		},
		{
			name:      "Windows + Intel",
			osType:    Windows,
			gpuBrand:  "intel",
			wantValue: "vulkan,auto",
		},
		{
			name:      "Windows + Unknown",
			osType:    Windows,
			gpuBrand:  "unknown",
			wantValue: "auto",
		},
		// Linux
		{
			name:      "Linux + Nvidia",
			osType:    Linux,
			gpuBrand:  "nvidia",
			wantValue: "nvdec,auto",
		},
		{
			name:      "Linux + AMD",
			osType:    Linux,
			gpuBrand:  "amd",
			wantValue: "auto",
		},
		{
			name:      "Linux + Intel",
			osType:    Linux,
			gpuBrand:  "intel",
			wantValue: "auto",
		},
		{
			name:      "Linux + Unknown",
			osType:    Linux,
			gpuBrand:  "unknown",
			wantValue: "auto",
		},
		// macOS
		{
			name:      "macOS + Apple",
			osType:    Darwin,
			gpuBrand:  "apple",
			wantValue: "auto",
		},
		{
			name:      "macOS + Intel",
			osType:    Darwin,
			gpuBrand:  "intel",
			wantValue: "auto",
		},
		{
			name:      "macOS + Unknown",
			osType:    Darwin,
			gpuBrand:  "unknown",
			wantValue: "auto",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := GetRecommendedHWADecoder(tt.osType, tt.gpuBrand)
			assert.Equal(t, tt.wantValue, got)
		})
	}
}

// TestGetHwaDescription tests hardware acceleration method descriptions
func TestGetHwaDescription(t *testing.T) {
	tests := []struct {
		name         string
		method       string
		wantContains string
	}{
		{
			name:         "auto",
			method:       "auto",
			wantContains: "choose best method",
		},
		{
			name:         "nvdec",
			method:       "nvdec",
			wantContains: "NVIDIA",
		},
		{
			name:         "nvdec-copy",
			method:       "nvdec-copy",
			wantContains: "system RAM copy",
		},
		{
			name:         "nvdec,auto",
			method:       "nvdec,auto",
			wantContains: "fallback to auto",
		},
		{
			name:         "nvdec-copy,auto",
			method:       "nvdec-copy,auto",
			wantContains: "fallback to auto",
		},
		{
			name:         "vulkan,auto",
			method:       "vulkan,auto",
			wantContains: "fallback to auto",
		},
		{
			name:         "vaapi",
			method:       "vaapi",
			wantContains: "Video Acceleration API",
		},
		{
			name:         "d3d11va",
			method:       "d3d11va",
			wantContains: "Direct3D 11",
		},
		{
			name:         "videotoolbox",
			method:       "videotoolbox",
			wantContains: "VideoToolbox",
		},
		{
			name:         "no",
			method:       "no",
			wantContains: "CPU only",
		},
		{
			name:         "unknown method",
			method:       "unknown-method",
			wantContains: "unknown-method",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := GetHwaDescription(tt.method)
			assert.Contains(t, got, tt.wantContains)
		})
	}
}
