//go:build darwin
// +build darwin

package worker

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// MacOSGPUInfo holds information about a macOS GPU
type MacOSGPUInfo struct {
	Name               string `json:"name"`
	ChipsetModel       string `json:"chipset_model"`
	Index              uint32 `json:"index"`
	VRAM_MB            uint32 `json:"vram_mb"`
	UtilizationPercent uint32 `json:"utilization_percent,omitempty"`
	PowerMW            uint32 `json:"power_mw,omitempty"`
	TemperatureC       uint32 `json:"temperature_c,omitempty"`
	IsIntegrated       bool   `json:"is_integrated"`
	IsAppleSilicon     bool   `json:"is_apple_silicon"`
}

// PowermetricsData holds GPU metrics from powermetrics
type PowermetricsData struct {
	GPUUtilization float64
	GPUPower       float64
	GPUTemperature float64
	HasData        bool
}

// IsMacOS returns true if running on macOS
func IsMacOS() bool {
	return runtime.GOOS == "darwin"
}

// IsAppleSilicon checks if running on Apple Silicon
func IsAppleSilicon() bool {
	if runtime.GOOS != "darwin" {
		return false
	}
	// Check machine hardware name
	out, err := exec.Command("uname", "-m").Output()
	if err != nil {
		return false
	}
	return strings.TrimSpace(string(out)) == "arm64"
}

// GetMacOSGPUCount returns the number of GPUs on macOS
func GetMacOSGPUCount() (int, error) {
	if runtime.GOOS != "darwin" {
		return 0, fmt.Errorf("not running on macOS")
	}

	// Use system_profiler to get GPU count
	cmd := exec.Command("system_profiler", "SPDisplaysDataType", "-json")
	out, err := cmd.Output()
	if err != nil {
		// Fall back to gfxutil if system_profiler fails
		return getGPUCountViaGfxutil()
	}

	// Parse JSON output
	var data map[string]interface{}
	if err := json.Unmarshal(out, &data); err != nil {
		return 0, err
	}

	// Extract display items
	if spData, ok := data["SPDisplaysDataType"].([]interface{}); ok {
		return len(spData), nil
	}

	return 0, nil
}

// getGPUCountViaGfxutil uses gfxutil to count GPUs (fallback)
func getGPUCountViaGfxutil() (int, error) {
	// gfxutil is available on macOS
	cmd := exec.Command("gfxutil", "-f", "display")
	out, err := cmd.Output()
	if err != nil {
		return 0, err
	}

	// Count display paths (one per GPU typically)
	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
	count := 0
	for _, line := range lines {
		if strings.Contains(line, "Display") {
			count++
		}
	}
	return count, nil
}

// GetMacOSGPUInfo returns detailed information about macOS GPUs
func GetMacOSGPUInfo() ([]MacOSGPUInfo, error) {
	if runtime.GOOS != "darwin" {
		return nil, fmt.Errorf("not running on macOS")
	}

	cmd := exec.Command("system_profiler", "SPDisplaysDataType", "-json")
	out, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var data map[string]interface{}
	if err := json.Unmarshal(out, &data); err != nil {
		return nil, err
	}

	spData, ok := data["SPDisplaysDataType"].([]interface{})
	if !ok {
		return []MacOSGPUInfo{}, nil
	}

	isAppleSilicon := IsAppleSilicon()
	var gpus []MacOSGPUInfo

	for i, item := range spData {
		if gpuData, ok := item.(map[string]interface{}); ok {
			info := MacOSGPUInfo{
				Index:          uint32(i),
				IsAppleSilicon: isAppleSilicon,
			}

			// Extract chipset model
			if model, ok := gpuData["sppci_model"].(string); ok {
				info.ChipsetModel = model
				info.Name = model
			}

			// Check for shared memory (integrated GPU)
			if _, ok := gpuData["sppci_vram_shared"]; ok {
				info.IsIntegrated = true
			}

			// Extract VRAM
			if vram, ok := gpuData["sppci_vram"].(string); ok {
				// Parse "16384 MB"
				parts := strings.Fields(vram)
				if len(parts) >= 1 {
					if mb, err := strconv.ParseUint(parts[0], 10, 32); err == nil {
						info.VRAM_MB = uint32(mb)
					}
				}
			}

			gpus = append(gpus, info)
		}
	}

	return gpus, nil
}

// GetPowermetricsData tries to get real-time GPU metrics from powermetrics
// Requires sudo access. Returns empty data if not available.
func GetPowermetricsData() (*PowermetricsData, error) {
	// powermetrics requires sudo, so this may fail
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	cmd := exec.CommandContext(ctx, "powermetrics", "--samplers", "gpu_power", "-n", "1", "-i", "100")
	out, err := cmd.Output()
	if err != nil {
		// powermetrics not available or no permission
		if ctx.Err() != context.DeadlineExceeded {
			fmt.Fprintln(os.Stderr, "Warning: powermetrics requires sudo for GPU metrics")
		}
		return &PowermetricsData{HasData: false}, nil
	}

	data := &PowermetricsData{HasData: false}

	// Parse powermetrics output
	// Example: "GPU Power: 5000 mW" or "GPU utilization: 45%"
	scanner := bufio.NewScanner(strings.NewReader(string(out)))
	for scanner.Scan() {
		line := scanner.Text()

		// Parse GPU utilization
		if strings.Contains(line, "GPU utilization") || strings.Contains(line, "GPU active") {
			re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*%`)
			if matches := re.FindStringSubmatch(line); len(matches) > 1 {
				if util, err := strconv.ParseFloat(matches[1], 64); err == nil {
					data.GPUUtilization = util
					data.HasData = true
				}
			}
		}

		// Parse GPU power
		if strings.Contains(line, "GPU Power") || strings.Contains(line, "GPU power") {
			re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*mW`)
			if matches := re.FindStringSubmatch(line); len(matches) > 1 {
				if power, err := strconv.ParseFloat(matches[1], 64); err == nil {
					data.GPUPower = power
					data.HasData = true
				}
			}
		}

		// Parse GPU temperature (if available)
		if strings.Contains(line, "GPU Temperature") || strings.Contains(line, "GPU temp") {
			re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*C`)
			if matches := re.FindStringSubmatch(line); len(matches) > 1 {
				if temp, err := strconv.ParseFloat(matches[1], 64); err == nil {
					data.GPUTemperature = temp
					data.HasData = true
				}
			}
		}
	}

	return data, nil
}

// FormatMacOSGPUStatus formats GPU status for display
func FormatMacOSGPUStatus() (string, error) {
	gpus, err := GetMacOSGPUInfo()
	if err != nil {
		return "", err
	}

	// Try to get real-time metrics from powermetrics
	powermetrics, _ := GetPowermetricsData()

	if len(gpus) == 0 {
		return "GPU info unavailable\n\nRun on a system with NVIDIA GPU or macOS", nil
	}

	var b strings.Builder

	if IsAppleSilicon() {
		b.WriteString("GPU Status (macOS - Apple Silicon)\n")
	} else {
		b.WriteString("GPU Status (macOS)\n")
	}
	b.WriteString(strings.Repeat("═", 50) + "\n\n")

	for _, gpu := range gpus {
		fmt.Fprintf(&b, "🎮 GPU %d: %s\n", gpu.Index, gpu.Name)
		if gpu.IsAppleSilicon {
			b.WriteString("   Type: Apple Silicon (Unified Memory)\n")
		} else if gpu.IsIntegrated {
			b.WriteString("   Type: Integrated (Shared Memory)\n")
		} else {
			fmt.Fprintf(&b, "   VRAM: %d MB\n", gpu.VRAM_MB)
		}

		// Display powermetrics data if available
		if powermetrics != nil && powermetrics.HasData {
			if powermetrics.GPUUtilization > 0 {
				b.WriteString(fmt.Sprintf("   Utilization: %.1f%%\n", powermetrics.GPUUtilization))
			}
			if powermetrics.GPUPower > 0 {
				b.WriteString(fmt.Sprintf("   Power: %.1f W\n", powermetrics.GPUPower/1000))
			}
			if powermetrics.GPUTemperature > 0 {
				b.WriteString(fmt.Sprintf("   Temperature: %.0f°C\n", powermetrics.GPUTemperature))
			}
		}
		b.WriteString("\n")
	}

	if powermetrics == nil || !powermetrics.HasData {
		b.WriteString("💡 Note: Run with sudo for real-time GPU metrics via powermetrics\n")
	}
	return b.String(), nil
}
