//go:build windows

package client

import (
	"encoding/json"
	"os"
	"os/user"
	"strings"
)

const sysCheckWindowsScript = `
$ErrorActionPreference = 'SilentlyContinue'
$o = [ordered]@{}

# Identity
try {
  $cs = Get-CimInstance Win32_ComputerSystem
  $o.manufacturer = $cs.Manufacturer
  $o.model = $cs.Model
  $o.domain = $cs.Domain
  $o.computer_name = $env:COMPUTERNAME
  $o.total_ram_gb = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
} catch {}

try {
  $bios = Get-CimInstance Win32_BIOS
  $o.serial = $bios.SerialNumber
  $o.bios_version = $bios.SMBIOSBIOSVersion
} catch {}

try {
  $o.cpus = @(Get-CimInstance Win32_Processor | ForEach-Object {
    [ordered]@{
      name = $_.Name
      cores = [int]$_.NumberOfCores
      logical = [int]$_.NumberOfLogicalProcessors
      max_mhz = [int]$_.MaxClockSpeed
      current_mhz = [int]$_.CurrentClockSpeed
    }
  })
} catch {}

try {
  $o.gpus = @(Get-CimInstance Win32_VideoController | ForEach-Object {
    [ordered]@{
      name = $_.Name
      driver = $_.DriverVersion
      vram_mb = if ($_.AdapterRAM -and $_.AdapterRAM -gt 0) { [int]($_.AdapterRAM / 1MB) } else { 0 }
    }
  })
} catch {}

try {
  $o.disks = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
    $freePct = if ($_.Size -gt 0) { [int](($_.FreeSpace / $_.Size) * 100) } else { 0 }
    [ordered]@{
      mount = $_.DeviceID
      label = $_.VolumeName
      fs_type = $_.FileSystem
      total_gb = [math]::Round($_.Size / 1GB, 2)
      free_gb = [math]::Round($_.FreeSpace / 1GB, 2)
      free_pct = $freePct
    }
  })
} catch {}

try {
  $os = Get-CimInstance Win32_OperatingSystem
  $o.uptime_hours = [math]::Round(((Get-Date) - $os.LastBootUpTime).TotalHours, 1)
  $o.timezone = (Get-TimeZone).Id
} catch {}

try {
  $gw = Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue |
    Sort-Object RouteMetric | Select-Object -First 1
  if ($gw) { $o.default_gateway = $gw.NextHop }
} catch {}

try {
  $routes = Get-NetRoute -AddressFamily IPv4 -ErrorAction SilentlyContinue |
    Select-Object -First 24 DestinationPrefix, NextHop, InterfaceAlias, RouteMetric |
    Format-Table -AutoSize | Out-String -Width 200
  $o.routes_summary = $routes.Trim()
} catch {}

$o | ConvertTo-Json -Depth 5 -Compress
`

func collectSysCheckPlatform(r *FullSysCheckReport) {
	out, err := silentCombinedOutput(
		"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command",
		sysCheckWindowsScript,
	)
	if err != nil {
		r.ProbeErrors = append(r.ProbeErrors, "windows hardware probe: "+err.Error())
	} else {
		parseWindowsSysCheckJSON(r, string(out))
	}

	if r.Identity == nil {
		r.Identity = &SysCheckIdentity{}
	}
	if u, err := user.Current(); err == nil {
		r.Identity.Username = u.Username
	}
	r.Identity.MACAddress = primaryMACAddress()
	if r.Environment == nil {
		r.Environment = &SysCheckEnvironment{}
	}
	r.Environment.TempDir = os.Getenv("TEMP")
	r.Environment.HomeDir = os.Getenv("USERPROFILE")
}

func parseWindowsSysCheckJSON(r *FullSysCheckReport, raw string) {
	raw = strings.TrimSpace(raw)
	if idx := strings.LastIndex(raw, "{"); idx > 0 {
		raw = raw[idx:]
	}
	var m map[string]interface{}
	if json.Unmarshal([]byte(raw), &m) != nil {
		return
	}

	hw := &SysCheckHardware{}
	if v, ok := m["manufacturer"].(string); ok {
		hw.Manufacturer = v
	}
	if v, ok := m["model"].(string); ok {
		hw.Model = v
	}
	if v, ok := m["serial"].(string); ok {
		hw.Serial = v
	}
	if v, ok := m["bios_version"].(string); ok {
		hw.BIOSVersion = v
	}
	if v, ok := m["total_ram_gb"].(float64); ok {
		hw.MemoryGB = v
	}
	if v, ok := m["uptime_hours"].(float64); ok {
		hw.UptimeHours = v
	}
	hw.CPUs = parseCPUList(m["cpus"])
	hw.GPUs = parseGPUList(m["gpus"])
	hw.Disks = parseDiskList(m["disks"])
	r.Hardware = hw

	if r.Identity == nil {
		r.Identity = &SysCheckIdentity{}
	}
	if v, ok := m["domain"].(string); ok {
		r.Identity.Domain = v
	}
	if v, ok := m["computer_name"].(string); ok {
		r.Identity.ComputerName = v
	}

	if r.Network == nil {
		r.Network = &SysCheckNetwork{}
	}
	if v, ok := m["default_gateway"].(string); ok {
		r.Network.DefaultGateway = v
	}
	if v, ok := m["routes_summary"].(string); ok {
		r.Network.RoutesSummary = v
	}

	if r.Environment == nil {
		r.Environment = &SysCheckEnvironment{}
	}
	if v, ok := m["timezone"].(string); ok {
		r.Environment.Timezone = v
	}
}

func parseCPUList(v interface{}) []SysCheckCPU {
	arr, ok := v.([]interface{})
	if !ok {
		return nil
	}
	var out []SysCheckCPU
	for _, item := range arr {
		obj, ok := item.(map[string]interface{})
		if !ok {
			continue
		}
		c := SysCheckCPU{Name: mapStr(obj, "name")}
		if n, ok := obj["cores"].(float64); ok {
			c.Cores = int(n)
		}
		if n, ok := obj["logical"].(float64); ok {
			c.Logical = int(n)
		}
		if n, ok := obj["max_mhz"].(float64); ok {
			c.MaxMHz = int(n)
		}
		if n, ok := obj["current_mhz"].(float64); ok {
			c.CurrentMHz = int(n)
		}
		out = append(out, c)
	}
	return out
}

func parseGPUList(v interface{}) []SysCheckGPU {
	arr, ok := v.([]interface{})
	if !ok {
		return nil
	}
	var out []SysCheckGPU
	for _, item := range arr {
		obj, ok := item.(map[string]interface{})
		if !ok {
			continue
		}
		g := SysCheckGPU{
			Name:   mapStr(obj, "name"),
			Driver: mapStr(obj, "driver"),
		}
		if n, ok := obj["vram_mb"].(float64); ok {
			g.VRAM_MB = int(n)
		}
		out = append(out, g)
	}
	return out
}

func parseDiskList(v interface{}) []SysCheckDisk {
	arr, ok := v.([]interface{})
	if !ok {
		return nil
	}
	var out []SysCheckDisk
	for _, item := range arr {
		obj, ok := item.(map[string]interface{})
		if !ok {
			continue
		}
		d := SysCheckDisk{
			Mount:  mapStr(obj, "mount"),
			Label:  mapStr(obj, "label"),
			FSType: mapStr(obj, "fs_type"),
		}
		if n, ok := obj["total_gb"].(float64); ok {
			d.TotalGB = n
		}
		if n, ok := obj["free_gb"].(float64); ok {
			d.FreeGB = n
		}
		if n, ok := obj["free_pct"].(float64); ok {
			d.FreePct = int(n)
		}
		out = append(out, d)
	}
	return out
}

func mapStr(m map[string]interface{}, key string) string {
	if v, ok := m[key].(string); ok {
		return v
	}
	return ""
}
