package platform

import (
	"os/exec"
	"runtime"
	"strings"
	"sync"

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

type OSType string

const (
	Windows OSType = "windows"
	Darwin  OSType = "darwin"
	Linux   OSType = "linux"
)

type Platform struct {
	OSType       OSType
	Arch         string
	Distro       string
	DistroFamily string
	Version      string
	CPUInfo      CPUInfo
	GPUInfo      *GPUInfo
}

type CPUInfo struct {
	Vendor            string
	VendorDisplay     string
	ArchitectureLevel string
	Model             string
	Features          []string
	SupportsAVX2      bool
	SupportsAVX512    bool
	SupportsNEON      bool
}

func Detect() *Platform {
	p := &Platform{
		OSType: getOSType(),
		Arch:   runtime.GOARCH,
	}

	if p.OSType == Linux {
		p.Distro, p.DistroFamily = detectLinuxDistro()
	}

	// Run CPU and GPU detection concurrently
	var cpuInfo CPUInfo
	var gpuInfo *GPUInfo
	var wg sync.WaitGroup
	wg.Add(2)

	// Detect CPU in goroutine
	go func() {
		defer wg.Done()
		cpuInfo = detectCPU()
	}()

	// Detect GPU in goroutine
	go func() {
		defer wg.Done()
		gpuInfo = detectGPU()
	}()

	// Wait for both detections to complete
	wg.Wait()
	p.CPUInfo = cpuInfo
	p.GPUInfo = gpuInfo

	if p.OSType == Darwin {
		p.Version = detectMacOSVersion()
	}

	return p
}

func getOSType() OSType {
	switch runtime.GOOS {
	case "windows":
		return Windows
	case "darwin":
		return Darwin
	default:
		return Linux
	}
}

func (p *Platform) getOSTypeDisplay() string {
	switch p.OSType {
	case Darwin:
		return "macOS"
	default:
		return string(p.OSType)
	}
}

func (p *Platform) IsWindows() bool {
	return p.OSType == Windows
}

func (p *Platform) IsDarwin() bool {
	return p.OSType == Darwin
}

func (p *Platform) IsLinux() bool {
	return p.OSType == Linux
}

// GetCPULevel returns the CPU architecture level for binary selection
// Returns "x86-64-v3" for AVX2 support, "x86-64" for baseline, or "arm64" for ARM
func (p *Platform) GetCPULevel() string {
	return p.CPUInfo.ArchitectureLevel
}

// GetDetectedCPULevel returns the detected CPU architecture level
// This creates a new Platform detection to get the CPU level
func GetDetectedCPULevel() string {
	p := Detect()
	return p.CPUInfo.ArchitectureLevel
}

// Cached platform for GetCPULevel function
var detectedPlatform *Platform
var detectOnce sync.Once

// GetCPULevel returns the CPU architecture level (cached)
func GetCPULevel() string {
	detectOnce.Do(func() {
		detectedPlatform = Detect()
	})
	return detectedPlatform.CPUInfo.ArchitectureLevel
}

func (p *Platform) IsImmutableDistro() bool {
	if p.OSType != Linux {
		return false
	}

	immutableDistros := []string{
		"fedora-atomic",
		"fedora-silverblue",
		"fedora-kinoite",
		"endless",
		"vanilla",
		"bluefin",
		"bazzite",
		"universal-blue",
		"aurora",
		"cosmic",
	}

	for _, distro := range immutableDistros {
		if strings.Contains(strings.ToLower(p.Distro), distro) {
			return true
		}
	}

	return false
}

func detectMacOSVersion() string {
	if runtime.GOOS != "darwin" {
		return ""
	}

	output, err := exec.Command("sw_vers", "-productVersion").Output()
	if err != nil {
		log.Debug("Failed to get macOS version: " + err.Error())
		return ""
	}
	return strings.TrimSpace(string(output))
}

func getMacOSVersionName(version string) string {
	macosNames := map[string]string{
		"10.0":  "Cheetah",
		"10.1":  "Puma",
		"10.2":  "Jaguar",
		"10.3":  "Panther",
		"10.4":  "Tiger",
		"10.5":  "Leopard",
		"10.6":  "Snow Leopard",
		"10.7":  "Lion",
		"10.8":  "Mountain Lion",
		"10.9":  "Mavericks",
		"10.10": "Yosemite",
		"10.11": "El Capitan",
		"10.12": "Sierra",
		"10.13": "High Sierra",
		"10.14": "Mojave",
		"10.15": "Catalina",
		"11":    "Big Sur",
		"12":    "Monterey",
		"13":    "Ventura",
		"14":    "Sonoma",
		"15":    "Sequoia",
		"26":    "Tahoe",
	}

	if name, ok := macosNames[version]; ok {
		return name
	}

	return version
}

func (p *Platform) String() string {
	var sb strings.Builder
	sb.WriteString(p.getOSTypeDisplay())

	if p.OSType == Darwin {
		sb.WriteString(" ")
		sb.WriteString(p.Version)
		if versionName := getMacOSVersionName(p.Version); versionName != "" && versionName != p.Version {
			sb.WriteString(" - ")
			sb.WriteString(versionName)
		}
		if p.CPUInfo.Model != "" {
			sb.WriteString(" (")
			sb.WriteString(p.CPUInfo.Model)
			sb.WriteString(")")
		}
	} else {
		sb.WriteString(" ")
		sb.WriteString(p.Arch)
	}

	if p.OSType == Linux && p.Distro != "" {
		sb.WriteString(" (")
		sb.WriteString(p.Distro)
		if p.DistroFamily != "" {
			sb.WriteString("/")
			sb.WriteString(p.DistroFamily)
		}
		sb.WriteString(")")
	}

	return sb.String()
}
