//go:build windows

// Package preflight runs quick environment checks before deployment starts.
// Each check returns a human-readable result; failures are warnings, not blockers.
package preflight

import (
	"fmt"
	"os/exec"
	"strings"
	"syscall"
	"unsafe"
)

// Result is one pre-flight check outcome.
type Result struct {
	Name   string
	OK     bool
	Detail string
}

// RunAll executes all pre-flight checks and returns results.
func RunAll() []Result {
	return []Result{
		checkAdmin(),
		checkWinget(),
		checkNetwork(),
		checkDisk(),
	}
}

func checkAdmin() Result {
	r := Result{Name: "Spusteno jako Administrator"}
	// If we got here via #Requires -RunAsAdministrator or manifested exe,
	// we're admin. Double-check via net session.
	cmd := exec.Command("net", "session")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	if err := cmd.Run(); err != nil {
		r.OK = false
		r.Detail = "Neni spusteno jako Administrator"
		return r
	}
	r.OK = true
	r.Detail = "OK"
	return r
}

func checkWinget() Result {
	r := Result{Name: "Winget dostupny"}
	cmd := exec.Command("winget", "--version")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	out, err := cmd.CombinedOutput()
	if err != nil {
		r.OK = false
		r.Detail = "winget nenalezen - software se nenainstaluje"
		return r
	}
	ver := strings.TrimSpace(string(out))
	r.OK = true
	r.Detail = ver
	return r
}

func checkNetwork() Result {
	r := Result{Name: "Pripojeni k internetu"}
	// Try to resolve a well-known hostname
	cmd := exec.Command("powershell.exe", "-NonInteractive", "-Command",
		"[System.Net.Dns]::GetHostEntry('www.google.com').AddressList[0].IPAddressToString")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	out, err := cmd.CombinedOutput()
	if err != nil || strings.TrimSpace(string(out)) == "" {
		r.OK = false
		r.Detail = "DNS resolve selhal - Atera a winget nebudou fungovat"
		return r
	}
	r.OK = true
	r.Detail = "OK"
	return r
}

func checkDisk() Result {
	r := Result{Name: "Volne misto na C:"}

	kernel32 := syscall.NewLazyDLL("kernel32.dll")
	getDiskFreeSpaceEx := kernel32.NewProc("GetDiskFreeSpaceExW")

	var freeBytesAvailable, totalBytes, totalFreeBytes uint64
	drive, _ := syscall.UTF16PtrFromString("C:\\")

	ret, _, _ := getDiskFreeSpaceEx.Call(
		uintptr(unsafe.Pointer(drive)),
		uintptr(unsafe.Pointer(&freeBytesAvailable)),
		uintptr(unsafe.Pointer(&totalBytes)),
		uintptr(unsafe.Pointer(&totalFreeBytes)),
	)
	if ret == 0 {
		r.OK = false
		r.Detail = "Nelze zjistit volne misto"
		return r
	}

	freeGB := float64(freeBytesAvailable) / (1024 * 1024 * 1024)
	if freeGB < 5 {
		r.OK = false
		r.Detail = fmt.Sprintf("%.1f GB - malo mista (min 5 GB)", freeGB)
		return r
	}
	r.OK = true
	r.Detail = fmt.Sprintf("%.1f GB volnych", freeGB)
	return r
}
