package platform

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"gitgud.io/mike/mpv-manager/pkg/log"
)

type GPUInfo struct {
	Models          []string
	Brand           string
	SupportsVA      bool
	SupportsNV      bool
	SupportsVT      bool
	SupportedCodecs []string
}

type Codec struct {
	Name    string
	Profile string
}

type CodecSupport struct {
	API    string
	Codecs []Codec
}

type HardwareDecoder struct {
	Vendor    string
	API       string
	Supported []string
}

type GPUModelEntry struct {
	ModelNames   []string
	SeriesNames  []string
	Codename     string
	AltCodenames []string
	Architecture string
	Codecs       []string
}

var codecProfiles = map[string][]string{
	"av2": {"AV2Profile0"},
	"vvc": {"VVCMain", "VVCMain10", "VVCMain12"},
	"av1": {"AV1Profile0", "AV1Profile1"},
	"vp9": {"VP9Profile0", "VP9Profile2", "VP9Profile1", "VP9Profile3"},
	"hevc": {"HEVCMain", "HEVCMain10", "HEVCMain12", "HEVCMain422", "HEVCMain444",
		"HEVCMain444_10", "HEVCMain444_12", "HEVCSccMain", "HEVCSccMain10"},
	"vp8":    {"VP8Version0_3"},
	"prores": {"ProRes422", "ProRes422HQ", "ProRes422LT", "ProRes422Proxy", "ProRes4444"},
	"vc1":    {"VC1Simple", "VC1Main", "VC1Advanced"},
	"avc": {"H264ConstrainedBaseline", "H264Baseline", "H264Main", "H264High",
		"H264High10", "H264High422", "H264High444", "H264MultiviewHigh",
		"H264StereoHigh", "H264ConstrainedHigh"},
	"mpeg2": {"MPEG2Simple", "MPEG2Main", "MPEG2High"},
}

var codecNameMapping = map[string]string{
	"H.264":  "avc",
	"HEVC":   "hevc",
	"VP9":    "vp9",
	"AV1":    "av1",
	"VP8":    "vp8",
	"VC-1":   "vc1",
	"MPEG-2": "mpeg2",
	"VVC":    "vvc",
}

func convertCodecsToMPVFormat(csvCodecs string) []string {
	parts := strings.Split(csvCodecs, ",")
	var codecs []string
	for _, part := range parts {
		codecName := strings.TrimSpace(part)
		if mapped, ok := codecNameMapping[codecName]; ok {
			codecs = append(codecs, mapped)
		}
	}
	return codecs
}

func parseGlxInfo(output string) []string {
	var models []string
	lines := strings.Split(output, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "Device:") ||
			strings.HasPrefix(line, "OpenGL renderer string:") {

			var gpuName string
			if strings.HasPrefix(line, "Device:") {
				gpuName = strings.TrimSpace(strings.TrimPrefix(line, "Device:"))
			} else {
				gpuName = strings.TrimSpace(strings.TrimPrefix(line, "OpenGL renderer string:"))
			}

			if idx := strings.Index(gpuName, "("); idx > 0 {
				gpuName = strings.TrimSpace(gpuName[:idx])
			}

			if gpuName != "" && !contains(models, gpuName) {
				models = append(models, gpuName)
			}
		}
	}

	return models
}

func parseVulkanInfo(output string) []string {
	var models []string
	lines := strings.Split(output, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)

		if strings.Contains(line, "deviceName") && strings.Contains(line, "=") {
			parts := strings.SplitN(line, "=", 2)
			if len(parts) == 2 {
				gpuName := strings.TrimSpace(parts[1])

				if idx := strings.Index(gpuName, "("); idx > 0 {
					gpuName = strings.TrimSpace(gpuName[:idx])
				}

				if gpuName != "" && !contains(models, gpuName) {
					models = append(models, gpuName)
				}
			}
		}
	}

	return models
}

func lookupGPUCodecs(gpuName string, gpuData []GPUModelEntry) ([]string, bool) {
	gpuNameLower := strings.ToLower(strings.TrimSpace(gpuName))

	for _, entry := range gpuData {
		for _, modelName := range entry.ModelNames {
			if strings.Contains(gpuNameLower, strings.ToLower(modelName)) {
				return entry.Codecs, true
			}
		}
	}

	for _, entry := range gpuData {
		for _, seriesName := range entry.SeriesNames {
			if strings.Contains(gpuNameLower, strings.ToLower(seriesName)) {
				return entry.Codecs, true
			}
		}
	}

	for _, entry := range gpuData {
		if strings.Contains(gpuNameLower, strings.ToLower(entry.Codename)) {
			return entry.Codecs, true
		}
	}

	for _, entry := range gpuData {
		for _, altCodename := range entry.AltCodenames {
			if strings.Contains(gpuNameLower, strings.ToLower(altCodename)) {
				return entry.Codecs, true
			}
		}
	}

	return []string{"unknown"}, false
}

func lookupGPUCodecsWithFallback(gpuName string, brand string) ([]string, bool) {
	switch strings.ToLower(brand) {
	case "amd":
		if codecs, matched := lookupGPUCodecs(gpuName, amdGPUData); matched {
			return codecs, true
		}
	case "nvidia":
		if codecs, matched := lookupGPUCodecs(gpuName, nvidiaGPUData); matched {
			return codecs, true
		}
	case "intel":
		if codecs, matched := lookupGPUCodecs(gpuName, intelGPUData); matched {
			return codecs, true
		}
	}

	allData := [][]GPUModelEntry{amdGPUData, nvidiaGPUData, intelGPUData}
	for _, data := range allData {
		if codecs, matched := lookupGPUCodecs(gpuName, data); matched {
			return codecs, true
		}
	}

	// Log when no codec database match is found for this GPU
	log.Debug(fmt.Sprintf("[GPU] No codec database match found for GPU: %q (brand: %s), using fallback", gpuName, brand))
	return []string{"unknown"}, false
}

var amdGPUData = []GPUModelEntry{
	{
		ModelNames:   []string{"Radeon RX 480", "Radeon RX 470", "Radeon RX 590", "Radeon RX 580", "Radeon RX 570"},
		SeriesNames:  []string{"RX 480", "RX 470", "RX 590", "RX 580", "RX 570"},
		Codename:     "Polaris 10",
		AltCodenames: []string{"Polaris 20", "Polaris 30"},
		Architecture: "GCN 4.0",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 460", "Radeon RX 560", "Radeon RX 550", "Radeon RX 540"},
		SeriesNames:  []string{"RX 460", "RX 560", "RX 550", "RX 540"},
		Codename:     "Polaris 11",
		AltCodenames: []string{"Polaris 12", "Polaris 21"},
		Architecture: "GCN 4.0",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX Vega 64", "Radeon RX Vega 56"},
		SeriesNames:  []string{"RX Vega 64", "RX Vega 56", "Vega 64", "Vega 56"},
		Codename:     "Vega 10",
		Architecture: "GCN 5.0",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		// Intel Kaby Lake-G processors with integrated AMD Radeon Vega M GPUs
		// These hybrid chips combine Intel CPU with AMD Vega graphics on same package
		// Examples: i5-8705G, i7-8705G, i7-8809G
		ModelNames:   []string{"Radeon RX Vega M GL", "Radeon RX Vega M MX", "Vega M GL", "Vega M MX"},
		SeriesNames:  []string{"RX Vega M GL", "RX Vega M MX", "Vega M GL", "Vega M MX", "Vega M"},
		Codename:     "Vega M",
		AltCodenames: []string{"Kaby Lake-G"},
		Architecture: "GCN 5.0",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon VII"},
		SeriesNames:  []string{"Radeon VII"},
		Codename:     "Vega 20",
		Architecture: "GCN 5.0",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 5700 XT", "Radeon RX 5700", "Radeon RX 5600 XT"},
		SeriesNames:  []string{"RX 5700 XT", "RX 5700", "RX 5600 XT", "RX 5700", "RX 5600"},
		Codename:     "Navi 10",
		Architecture: "RDNA 1",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 5500 XT", "Radeon RX 5500", "Radeon RX 5300"},
		SeriesNames:  []string{"RX 5500 XT", "RX 5500", "RX 5300", "RX 5500", "RX 5300"},
		Codename:     "Navi 14",
		Architecture: "RDNA 1",
		Codecs:       []string{"avc", "hevc", "vp9", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 6950 XT", "Radeon RX 6900 XT", "Radeon RX 6800 XT"},
		SeriesNames:  []string{"RX 6950 XT", "RX 6900 XT", "RX 6800 XT", "RX 6900", "RX 6800"},
		Codename:     "Navi 21",
		Architecture: "RDNA 2",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 6750 XT", "Radeon RX 6700 XT", "Radeon RX 6700"},
		SeriesNames:  []string{"RX 6750 XT", "RX 6700 XT", "RX 6700", "RX 6750", "RX 6700"},
		Codename:     "Navi 22",
		Architecture: "RDNA 2",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 6650 XT", "Radeon RX 6600 XT", "Radeon RX 6600"},
		SeriesNames:  []string{"RX 6650 XT", "RX 6600 XT", "RX 6600", "RX 6650", "RX 6600"},
		Codename:     "Navi 23",
		Architecture: "RDNA 2",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Radeon RX 6500 XT", "Radeon RX 6400"},
		SeriesNames:  []string{"RX 6500 XT", "RX 6400", "RX 6500", "RX 6400"},
		Codename:     "Navi 24",
		Architecture: "RDNA 2",
		Codecs:       []string{"avc", "hevc", "vp9"},
	},
	{
		ModelNames:   []string{"Radeon RX 7900 XTX", "Radeon RX 7900 XT", "Radeon RX 7900 GRE", "Radeon RX 7900M"},
		SeriesNames:  []string{"RX 7900 XTX", "RX 7900 XT", "RX 7900 GRE", "RX 7900M", "RX 7900"},
		Codename:     "Navi 31",
		Architecture: "RDNA 3",
		Codecs:       []string{"avc", "hevc", "vp9", "av1"},
	},
	{
		ModelNames:   []string{"Radeon RX 7800 XT", "Radeon RX 7700 XT"},
		SeriesNames:  []string{"RX 7800 XT", "RX 7700 XT", "RX 7800", "RX 7700"},
		Codename:     "Navi 32",
		Architecture: "RDNA 3",
		Codecs:       []string{"avc", "hevc", "vp9", "av1"},
	},
	{
		ModelNames:   []string{"Radeon RX 7600 XT", "Radeon RX 7600"},
		SeriesNames:  []string{"RX 7600 XT", "RX 7600", "RX 7600"},
		Codename:     "Navi 33",
		Architecture: "RDNA 3",
		Codecs:       []string{"avc", "hevc", "vp9", "av1"},
	},
	{
		ModelNames:   []string{"Radeon RX 9070 XT", "Radeon RX 9070"},
		SeriesNames:  []string{"RX 9070 XT", "RX 9070", "RX 9070"},
		Codename:     "Navi 48",
		Architecture: "RDNA 4",
		Codecs:       []string{"avc", "hevc", "vp9", "av1"},
	},
	{
		ModelNames:   []string{"Radeon RX 9060 XT", "Radeon RX 9060"},
		SeriesNames:  []string{"RX 9060 XT", "RX 9060", "RX 9060"},
		Codename:     "Navi 44",
		Architecture: "RDNA 4",
		Codecs:       []string{"avc", "hevc", "vp9", "av1"},
	},
}

var intelGPUData = []GPUModelEntry{
	{
		ModelNames:   []string{"HD Graphics 2000", "HD Graphics 3000"},
		SeriesNames:  []string{"HD Graphics 2000", "HD Graphics 3000"},
		Codename:     "Sandy Bridge",
		Architecture: "Gen 6",
		Codecs:       []string{"avc", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"HD Graphics 2500", "HD Graphics 4000"},
		SeriesNames:  []string{"HD Graphics 2500", "HD Graphics 4000"},
		Codename:     "Ivy Bridge",
		Architecture: "Gen 7",
		Codecs:       []string{"avc", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"HD Graphics 4200", "HD Graphics 4400", "HD Graphics 4600", "HD Graphics 5000", "HD Graphics 5100", "HD Graphics 5200"},
		SeriesNames:  []string{"HD Graphics 4200", "HD Graphics 4400", "HD Graphics 4600", "HD Graphics 5000", "HD Graphics 5100", "HD Graphics 5200"},
		Codename:     "Haswell",
		Architecture: "Gen 7.5",
		Codecs:       []string{"avc", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"HD Graphics 5300", "HD Graphics 5500", "HD Graphics 6000", "HD Graphics 6100", "HD Graphics 6200", "HD Graphics 6300"},
		SeriesNames:  []string{"HD Graphics 5300", "HD Graphics 5500", "HD Graphics 6000", "HD Graphics 6100", "HD Graphics 6200", "HD Graphics 6300"},
		Codename:     "Broadwell",
		Architecture: "Gen 8",
		Codecs:       []string{"avc", "vp8", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"HD Graphics 510", "HD Graphics 515", "HD Graphics 520", "HD Graphics 530", "HD Graphics 540", "HD Graphics 550", "HD Graphics P530", "HD Graphics P580"},
		SeriesNames:  []string{"HD Graphics 510", "HD Graphics 515", "HD Graphics 520", "HD Graphics 530", "HD Graphics 540", "HD Graphics 550"},
		Codename:     "Skylake",
		Architecture: "Gen 9",
		Codecs:       []string{"avc", "hevc", "vp8", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"UHD Graphics 610", "UHD Graphics 615", "UHD Graphics 620", "UHD Graphics 630", "UHD Graphics 640", "UHD Graphics 650", "UHD Graphics 655"},
		SeriesNames:  []string{"UHD Graphics 610", "UHD Graphics 615", "UHD Graphics 620", "UHD Graphics 630", "UHD Graphics 640", "UHD Graphics 650", "UHD Graphics 655", "UHD 610", "UHD 615", "UHD 620", "UHD 630"},
		Codename:     "Kaby Lake",
		AltCodenames: []string{"Coffee Lake", "Comet Lake"},
		Architecture: "Gen 9.5",
		Codecs:       []string{"avc", "hevc", "vp9", "vp8", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Iris Plus Graphics 645", "Iris Plus Graphics 655", "Iris Plus Graphics G4", "Iris Plus Graphics G7"},
		SeriesNames:  []string{"Iris Plus Graphics 645", "Iris Plus Graphics 655", "Iris Plus Graphics G4", "Iris Plus Graphics G7", "Iris Plus"},
		Codename:     "Ice Lake",
		Architecture: "Gen 11",
		Codecs:       []string{"avc", "hevc", "vp9", "vp8", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"UHD Graphics 700", "UHD 700", "Iris Xe Graphics", "Iris Xe"},
		SeriesNames:  []string{"UHD Graphics 700", "UHD 700", "UHD 770", "UHD 750", "UHD 730", "UHD 710", "Iris Xe Graphics", "Iris Xe", "Iris Xe"},
		Codename:     "Tiger Lake",
		AltCodenames: []string{"Alder Lake", "Raptor Lake"},
		Architecture: "Gen 12.1 (Xe-LP)",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vc1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Arc A770", "Arc A750", "Arc A550", "Arc A380", "Arc A350", "Arc Graphics", "Arc"},
		SeriesNames:  []string{"Arc A770", "Arc A750", "Arc A550", "Arc A380", "Arc A350", "Arc Graphics", "Arc", "Arc A"},
		Codename:     "Alchemist",
		AltCodenames: []string{"Meteor Lake"},
		Architecture: "Xe-HPG / Xe-LPG",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "mpeg2"},
	},
	{
		ModelNames:   []string{"Core Ultra 7 155H", "Core Ultra 7 165H", "Core Ultra 9 185H", "Core Ultra", "Lunar Lake"},
		SeriesNames:  []string{"Core Ultra 7", "Core Ultra 9", "Core Ultra", "Lunar Lake"},
		Codename:     "Lunar Lake",
		Architecture: "Xe2-LPG",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vvc", "mpeg2"},
	},
	{
		ModelNames:   []string{"Arc B580", "Arc B570", "Arc B-Series", "Battlemage"},
		SeriesNames:  []string{"Arc B580", "Arc B570", "Arc B-Series", "Battlemage", "Arc B"},
		Codename:     "Battlemage",
		Architecture: "Xe2-HPG",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "mpeg2"},
	},
}

var nvidiaGPUData = []GPUModelEntry{
	{
		ModelNames:   []string{"GeForce GTX 600", "GeForce GTX 700", "GeForce Titan", "GTX 600", "GTX 700"},
		SeriesNames:  []string{"GeForce GTX 600", "GeForce GTX 700", "GeForce Titan", "GTX 600", "GTX 700", "Titan"},
		Codename:     "GK104",
		AltCodenames: []string{"GK106", "GK107", "GK110", "GK208"},
		Architecture: "Kepler",
		Codecs:       []string{"avc", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce GTX 750", "GeForce GTX 750 Ti"},
		SeriesNames:  []string{"GeForce GTX 750", "GeForce GTX 750 Ti", "GTX 750", "GTX 750 Ti"},
		Codename:     "GM107",
		AltCodenames: []string{"GM108"},
		Architecture: "Maxwell (1st Gen)",
		Codecs:       []string{"avc", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce GTX 970", "GeForce GTX 980", "GeForce Titan X"},
		SeriesNames:  []string{"GeForce GTX 970", "GeForce GTX 980", "GeForce Titan X", "GTX 970", "GTX 980", "Titan X"},
		Codename:     "GM200",
		AltCodenames: []string{"GM204"},
		Architecture: "Maxwell (2nd Gen)",
		Codecs:       []string{"avc", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce GTX 950", "GeForce GTX 960"},
		SeriesNames:  []string{"GeForce GTX 950", "GeForce GTX 960", "GTX 950", "GTX 960"},
		Codename:     "GM206",
		Architecture: "Maxwell (2nd Gen)",
		Codecs:       []string{"avc", "hevc", "vp9", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce GTX 1000", "GeForce GT 1030", "GTX 1000"},
		SeriesNames:  []string{"GeForce GTX 1000", "GeForce GT 1030", "GTX 1000", "GTX 1080", "GTX 1070", "GTX 1060", "GTX 1050"},
		Codename:     "GP102",
		AltCodenames: []string{"GP104", "GP106", "GP107", "GP108"},
		Architecture: "Pascal",
		Codecs:       []string{"avc", "hevc", "vp9", "vp8", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"NVIDIA Titan V"},
		SeriesNames:  []string{"NVIDIA Titan V", "Titan V"},
		Codename:     "GV100",
		Architecture: "Volta",
		Codecs:       []string{"avc", "hevc", "vp9", "vp8", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce RTX 20", "GeForce GTX 16", "RTX 20", "GTX 16"},
		SeriesNames:  []string{"GeForce RTX 20", "GeForce GTX 16", "RTX 20", "GTX 16", "RTX 2080", "RTX 2070", "RTX 2060", "GTX 1660", "GTX 1650"},
		Codename:     "TU102",
		AltCodenames: []string{"TU104", "TU106", "TU116", "TU117"},
		Architecture: "Turing",
		Codecs:       []string{"avc", "hevc", "vp9", "vp8", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce RTX 30", "RTX 30"},
		SeriesNames:  []string{"GeForce RTX 30", "RTX 30", "RTX 3090", "RTX 3080", "RTX 3070", "RTX 3060"},
		Codename:     "GA102",
		AltCodenames: []string{"GA104", "GA106", "GA107"},
		Architecture: "Ampere",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vp8", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce RTX 40", "RTX 40"},
		SeriesNames:  []string{"GeForce RTX 40", "RTX 40", "RTX 4090", "RTX 4080", "RTX 4070", "RTX 4060"},
		Codename:     "AD102",
		AltCodenames: []string{"AD103", "AD104", "AD106", "AD107"},
		Architecture: "Ada Lovelace",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vp8", "mpeg2", "vc1"},
	},
	{
		ModelNames:   []string{"GeForce RTX 50", "RTX 50"},
		SeriesNames:  []string{"GeForce RTX 50", "RTX 50", "RTX 5090", "RTX 5080", "RTX 5070"},
		Codename:     "GB202",
		AltCodenames: []string{"GB203", "GB205"},
		Architecture: "Blackwell",
		Codecs:       []string{"avc", "hevc", "vp9", "av1", "vp8", "mpeg2", "vc1"},
	},
}

func detectGPU() *GPUInfo {
	info := &GPUInfo{
		Models: []string{},
	}

	if runtime.GOOS == "windows" {
		info = detectGPUWindows()
	} else if runtime.GOOS == "linux" {
		info = detectGPULinux()
	} else if runtime.GOOS == "darwin" {
		info = detectGPUDarwin()
	}

	codecs, err := detectCodecSupport(info)
	if err == nil && len(codecs) > 0 {
		info.SupportedCodecs = codecs
	}

	return info
}

func detectGPULinux() *GPUInfo {
	info := &GPUInfo{
		Models: []string{},
	}

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	if output, err := exec.CommandContext(ctx, "glxinfo", "-B").Output(); err == nil {
		if models := parseGlxInfo(string(output)); len(models) > 0 {
			info.Models = append(info.Models, models...)
		}
	} else {
		log.Debug("glxinfo command failed: " + err.Error())
	}

	if len(info.Models) == 0 {
		if output, err := exec.CommandContext(ctx, "vulkaninfo", "--summary").Output(); err == nil {
			if models := parseVulkanInfo(string(output)); len(models) > 0 {
				info.Models = append(info.Models, models...)
			}
		} else {
			log.Debug("vulkaninfo command failed: " + err.Error())
		}
	}

	if len(info.Models) == 0 {
		if output, err := exec.Command("lspci").Output(); err == nil {
			for _, line := range strings.Split(string(output), "\n") {
				if strings.Contains(strings.ToLower(line), "vga") ||
					strings.Contains(strings.ToLower(line), "display") {
					if idx := strings.Index(line, ": "); idx > 0 {
						model := strings.TrimSpace(line[idx+2:])
						if model != "" && !contains(info.Models, model) {
							info.Models = append(info.Models, model)
						}
					}
				}
			}
		} else {
			log.Debug("lspci command failed: " + err.Error())
		}
	}

	if len(info.Models) == 0 {
		drmDir := "/sys/class/drm"
		if entries, err := os.ReadDir(drmDir); err == nil {
			for _, entry := range entries {
				if strings.HasPrefix(entry.Name(), "card") {
					devicePath := filepath.Join(drmDir, entry.Name(), "device")
					ueventPath := filepath.Join(devicePath, "uevent")
					if fileExists(ueventPath) {
						if data, err := os.ReadFile(ueventPath); err == nil {
							for _, line := range strings.Split(string(data), "\n") {
								if strings.HasPrefix(line, "PCI_ID=") {
									pciID := strings.TrimPrefix(line, "PCI_ID=")
									model := getGPUModelFromPCIID(pciID)
									if model != "" && !contains(info.Models, model) {
										info.Models = append(info.Models, model)
									}
								}
							}
						}
					}
				}
			}
		}
	}

	if len(info.Models) == 1 {
		var filteredModels []string
		for _, model := range info.Models {
			modelLower := strings.ToLower(model)
			if !strings.Contains(modelLower, "ryzen") &&
				!strings.Contains(modelLower, "core") &&
				!strings.Contains(modelLower, "processor") &&
				!strings.Contains(modelLower, "rapahel") &&
				!strings.Contains(modelLower, "mendocino") {
				filteredModels = append(filteredModels, model)
			}
		}
		if len(filteredModels) > 0 {
			info.Models = filteredModels
		}
	}

	info.Brand = determineGPUBrand(info.Models)
	info.SupportsVA = info.Brand == "amd" || info.Brand == "intel"
	info.SupportsNV = info.Brand == "nvidia"

	return info
}

func detectGPUDarwin() *GPUInfo {
	info := &GPUInfo{
		Models:     []string{},
		Brand:      "apple",
		SupportsVT: true,
	}

	if runtime.GOARCH == "arm64" {
		if output, err := exec.Command("sysctl", "-n", "machdep.cpu.brand_string").Output(); err == nil {
			cpuModel := strings.TrimSpace(string(output))
			info.Models = append(info.Models, cpuModel)
			return info
		}
	}

	if output, err := exec.Command("system_profiler", "SPDisplaysDataType", "-json").Output(); err == nil {
		var data struct {
			SPDisplaysDataType []struct {
				Model string `json:"_name"`
			} `json:"SPDisplaysDataType"`
		}
		if err := json.Unmarshal(output, &data); err == nil {
			for _, display := range data.SPDisplaysDataType {
				if display.Model != "" {
					info.Models = append(info.Models, display.Model)
					info.Brand = determineGPUBrand(info.Models)
				}
			}
		}
	}

	return info
}

func detectGPUWindows() *GPUInfo {
	info := &GPUInfo{
		Models: []string{},
	}

	cmd := exec.Command("wmic", "path", "win32_VideoController", "get", "name")
	if output, err := cmd.Output(); err == nil {
		lines := strings.Split(string(output), "\n")
		for i, line := range lines {
			if i == 0 || strings.TrimSpace(line) == "" {
				continue
			}
			model := strings.TrimSpace(line)
			if model != "" && !contains(info.Models, model) {
				info.Models = append(info.Models, model)
			}
		}
	}

	if len(info.Models) == 0 {
		cmd = exec.Command("powershell", "-Command",
			"Get-WmiObject Win32_VideoController | Select-Object Name")
		if output, err := cmd.Output(); err == nil {
			lines := strings.Split(string(output), "\n")
			for _, line := range lines {
				if strings.TrimSpace(line) != "" &&
					!strings.Contains(line, "Name") &&
					!strings.Contains(line, "---") {
					model := strings.TrimSpace(line)
					if model != "" && !contains(info.Models, model) {
						info.Models = append(info.Models, model)
					}
				}
			}
		}
	}

	info.Brand = determineGPUBrand(info.Models)
	info.SupportsVA = info.Brand == "amd" || info.Brand == "intel"
	info.SupportsNV = info.Brand == "nvidia"

	return info
}

func determineGPUBrand(models []string) string {
	if len(models) == 0 {
		return ""
	}

	brands := make(map[string]int)
	hasArc := false

	for _, model := range models {
		lowerModel := strings.ToLower(model)
		if strings.Contains(lowerModel, "nvidia") || strings.Contains(lowerModel, "geforce") ||
			strings.Contains(lowerModel, "quadro") || strings.Contains(lowerModel, "tesla") {
			brands["nvidia"]++
		} else if strings.Contains(lowerModel, "amd") || strings.Contains(lowerModel, "radeon") ||
			strings.Contains(lowerModel, "advanced micro devices") {
			brands["amd"]++
		} else if strings.Contains(lowerModel, "intel") {
			if strings.Contains(lowerModel, "arc") {
				hasArc = true
			} else {
				brands["intel"]++
			}
		} else if strings.Contains(lowerModel, "apple") {
			brands["apple"]++
		}
	}

	if brands["amd"] > 0 && brands["intel"] > 0 {
		if hasArc {
			return "intel"
		}
		return "amd"
	}

	if brands["amd"] > 0 && brands["nvidia"] > 0 {
		return "nvidia"
	}

	if brands["intel"] > 0 && brands["nvidia"] > 0 {
		return "nvidia"
	}

	for brand, count := range brands {
		if count > 0 {
			return brand
		}
	}

	return ""
}

func GetHWAOptions(ostype OSType, brand string) []string {
	var options []string

	if brand == "apple" && ostype == Darwin {
		options = []string{"videotoolbox", "videotoolbox-copy"}
	} else if ostype == Linux {
		if brand == "nvidia" {
			// Include ,auto variants for Nvidia on Linux
			options = []string{
				"nvdec", "nvdec,auto",
				"nvdec-copy", "nvdec-copy,auto",
				"vulkan", "vulkan,auto",
				"vulkan-copy", "vulkan-copy,auto",
			}
		} else if brand == "amd" || brand == "intel" {
			options = []string{
				"vaapi", "vaapi,auto",
				"vaapi-copy", "vaapi-copy,auto",
				"vulkan", "vulkan,auto",
				"vulkan-copy", "vulkan-copy,auto",
				"drm", "drm,auto",
				"drm-copy", "drm-copy,auto",
			}
		} else {
			options = []string{
				"vaapi", "vaapi,auto",
				"nvdec", "nvdec,auto",
				"vulkan", "vulkan,auto",
				"vaapi-copy", "vaapi-copy,auto",
				"nvdec-copy", "nvdec-copy,auto",
				"vulkan-copy", "vulkan-copy,auto",
			}
		}
	} else if ostype == Windows {
		// Include ,auto variants for Windows
		options = []string{
			"d3d11va", "d3d11va,auto",
			"d3d11va-copy", "d3d11va-copy,auto",
			"vulkan", "vulkan,auto",
			"vulkan-copy", "vulkan-copy,auto",
		}
		if brand == "nvidia" {
			options = append([]string{
				"auto-safe",
				"nvdec", "nvdec,auto",
				"nvdec-copy", "nvdec-copy,auto",
			}, options...)
		}
	}

	return options
}

// GetRecommendedHWADecoder returns the recommended hardware decoder based on OS and GPU vendor.
// The returned value may include an ",auto" suffix which is MPV's fallback mechanism -
// if the primary decoder fails, it falls back to auto.
//
// Platform-specific recommendations:
//   - Windows + Nvidia: nvdec-copy,auto
//   - Windows + AMD: vulkan,auto
//   - Windows + Intel: vulkan,auto
//   - Linux + Nvidia: nvdec,auto
//   - Linux + AMD: auto
//   - Linux + Intel: auto
//   - macOS (All GPUs): auto
//   - Unknown brands: auto
func GetRecommendedHWADecoder(ostype OSType, brand string) string {
	switch ostype {
	case Windows:
		switch strings.ToLower(brand) {
		case "nvidia":
			return "nvdec-copy,auto"
		case "amd", "intel":
			return "vulkan,auto"
		default:
			return "auto"
		}
	case Linux:
		switch strings.ToLower(brand) {
		case "nvidia":
			return "nvdec,auto"
		case "amd", "intel":
			return "auto"
		default:
			return "auto"
		}
	case Darwin:
		// macOS VideoToolbox works well with auto
		return "auto"
	default:
		return "auto"
	}
}

// GetHwaDescription returns a human-readable description for a hardware acceleration method.
// This function consolidates descriptions used in both TUI and Web UI, providing consistent
// documentation across all interfaces. The matching is case-insensitive, and unknown methods
// are returned as-is for forward compatibility.
// Methods with ",auto" suffix get the base description with " (fallback to auto)" appended.
func GetHwaDescription(method string) string {
	methodLower := strings.ToLower(method)

	// Check for ,auto suffix and handle specially
	if strings.HasSuffix(methodLower, ",auto") {
		baseMethod := strings.TrimSuffix(methodLower, ",auto")
		baseDesc := getBaseHwaDescription(baseMethod)
		return baseDesc + " (fallback to auto)"
	}

	return getBaseHwaDescription(methodLower)
}

// getBaseHwaDescription returns the description for a base hwdec method (without ,auto suffix)
func getBaseHwaDescription(method string) string {
	switch method {
	// Automatic selection methods
	case "auto":
		return "Let MPV choose best method"
	case "auto-safe":
		return "Safe automatic selection (NVIDIA only)"

	// NVIDIA CUDA decoding
	case "nvdec":
		return "NVIDIA Video Decode"
	case "nvdec-copy":
		return "NVIDIA Video Decode with system RAM copy"

	// Video Acceleration API (Linux AMD/Intel)
	case "vaapi":
		return "Video Acceleration API (Linux/AMD/Intel)"
	case "vaapi-copy":
		return "Video Acceleration API with system RAM copy"

	// Vulkan video decoding (cross-platform)
	case "vulkan":
		return "Vulkan video decoding"
	case "vulkan-copy":
		return "Vulkan with system RAM copy"

	// Direct3D 11 Video Acceleration (Windows)
	case "d3d11va":
		return "Direct3D 11 Video Acceleration (Windows)"
	case "d3d11va-copy":
		return "Direct3D 11 Video Acceleration with system RAM copy"

	// Apple VideoToolbox (macOS)
	case "videotoolbox":
		return "Apple VideoToolbox (macOS)"
	case "videotoolbox-copy":
		return "Apple VideoToolbox with system RAM copy"

	// Direct Rendering Manager (Linux AMD/Intel)
	case "drm":
		return "Direct Rendering Manager (Linux/AMD/Intel)"
	case "drm-copy":
		return "Direct Rendering Manager with system RAM copy"

	// Disable hardware decoding
	case "no":
		return "Disable hardware acceleration (CPU only)"

	// Unknown method - return as-is for forward compatibility
	default:
		return method
	}
}

func detectCodecSupport(info *GPUInfo) ([]string, error) {
	switch runtime.GOOS {
	case "linux":
		return parseCodecSupportLinux(info)
	case "windows":
		return parseCodecSupportWindows(info)
	case "darwin":
		return detectCodecSupportDarwin()
	default:
		return nil, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
	}
}

func parseCodecSupportLinux(info *GPUInfo) ([]string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	if output, err := exec.CommandContext(ctx, "vainfo").Output(); err == nil {
		if detected, err := parseVainfo(string(output)); err == nil && len(detected) > 0 {
			return detected, nil
		}
	}

	if output, err := exec.CommandContext(ctx, "vdpauinfo").Output(); err == nil {
		if detected, err := parseVDPAUInfo(string(output)); err == nil && len(detected) > 0 {
			return detected, nil
		}
	}

	if len(info.Models) > 0 {
		brand := info.Brand
		for _, model := range info.Models {
			if codecs, matched := lookupGPUCodecsWithFallback(model, brand); matched {
				return codecs, nil
			}
		}
	}

	// Log when all detection methods fail and we fall back to unknown
	log.Debug("[GPU] HWA codec detection failed on Linux (no vainfo, vdpauinfo, or GPU match), falling back to 'unknown'")
	return []string{"unknown"}, nil
}

func parseVainfo(output string) ([]string, error) {
	var detected []string
	lines := strings.Split(output, "\n")

	for _, line := range lines {
		if !strings.Contains(line, "VAEntrypointVLD") {
			continue
		}

		for codec, profiles := range codecProfiles {
			for _, profile := range profiles {
				if strings.Contains(line, profile) {
					if !contains(detected, codec) {
						detected = append(detected, codec)
					}
					break
				}
			}
		}
	}

	if len(detected) == 0 {
		return nil, fmt.Errorf("no codecs found in VA-API output")
	}

	return detected, nil
}

func parseVDPAUInfo(output string) ([]string, error) {
	var detected []string
	lines := strings.Split(output, "\n")

	vdpauProfileMap := map[string]string{
		"MPEG1": "mpeg2",
		"MPEG2": "mpeg2",
		"H264":  "avc",
		"VC1":   "vc1",
		"VP8":   "vp8",
		"VP9":   "vp9",
		"HEVC":  "hevc",
		"AV1":   "av1",
	}

	for _, line := range lines {
		for vdpauName, codec := range vdpauProfileMap {
			if strings.Contains(line, vdpauName) {
				if !contains(detected, codec) {
					detected = append(detected, codec)
				}
			}
		}
	}

	if len(detected) == 0 {
		return nil, fmt.Errorf("no codecs found in VDPAU output")
	}

	return detected, nil
}

func parseCodecSupportWindows(info *GPUInfo) ([]string, error) {
	_, err := exec.Command("powershell", "-Command", "Get-CimInstance Win32_VideoController | Select-Object Name").Output()
	if err != nil {
		return nil, err
	}

	brand := info.Brand
	for _, model := range info.Models {
		if codecs, matched := lookupGPUCodecsWithFallback(model, brand); matched {
			return codecs, nil
		}
	}

	// Log when no GPU model matched and we fall back to unknown
	log.Debug("[GPU] HWA codec detection failed on Windows (no GPU database match), falling back to 'unknown'")
	return []string{"unknown"}, nil
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

func getGPUModelFromPCIID(pciID string) string {
	return ""
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
