param(
    [ValidateSet("Verify", "DryRun", "Apply")]
    [string]$Mode = "Verify",
    [string]$VmrunPath = "C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe",
    [string]$Host110Vmx = "",
    [string]$Host111Vmx = "",
    [string]$Host188Vmx = "",
    [string]$Host120Vmx = "",
    [string]$Host121Vmx = "",
    [string]$Host112Vmx = "",
    [string[]]$RequiredVmAliases = @("111", "188", "120", "121", "112"),
    [string[]]$DiscoveryRoot = @(
        "D:\Documents\Virtual Machines",
        "D:\Downloads",
        "D:\VMs",
        "E:\VMs",
        "C:\VMs",
        "C:\Users\Public\Documents\Virtual Machines"
    )
)

$ErrorActionPreference = "Stop"

$TaskName = "AWOOOI-Start-VMware-VMs"
$ProgramDataDir = "C:\ProgramData\AWOOOI"
$StartScript = Join-Path $ProgramDataDir "Start-AWOOOI-VMs.ps1"
$VmOrder = $RequiredVmAliases
$SuppliedVmx = @{
    "110" = $Host110Vmx
    "111" = $Host111Vmx
    "188" = $Host188Vmx
    "120" = $Host120Vmx
    "121" = $Host121Vmx
    "112" = $Host112Vmx
}
$KnownVmxCandidates = @{
    "110" = @("D:\Documents\Virtual Machines\Ubuntu 64-bit (3)\Ubuntu 64-bit (3).vmx")
    "111" = @("D:\Documents\Virtual Machines\192.168.0.111_Ubuntu_64-bit\192.168.0.111_Ubuntu_64-bit.vmx")
    "188" = @("D:\Documents\Virtual Machines\Ollama_Ubuntu_64-bit\Ollama_Ubuntu_64-bit.vmx")
    "120" = @("D:\Documents\Virtual Machines\192.168.0.120_Ubuntu_64-bit\192.168.0.120_Ubuntu_64-bit.vmx")
    "121" = @("D:\Documents\Virtual Machines\192.168.0.121_Ubuntu_64-bit\192.168.0.121_Ubuntu_64-bit.vmx")
    "112" = @("D:\Downloads\kali-linux-2025.4-vmware-amd64\kali-linux-2025.4-vmware-amd64.vmwarevm\kali-linux-2025.4-vmware-amd64.vmx")
}

function Resolve-VmxPath {
    param([string]$HostAlias)

    $supplied = $SuppliedVmx[$HostAlias]
    if ($supplied -and (Test-Path -LiteralPath $supplied)) {
        return (Resolve-Path -LiteralPath $supplied).Path
    }

    foreach ($candidate in @($KnownVmxCandidates[$HostAlias])) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) {
            return (Resolve-Path -LiteralPath $candidate).Path
        }
    }

    foreach ($root in $DiscoveryRoot) {
        if (-not (Test-Path -LiteralPath $root)) {
            continue
        }
        $match = Get-ChildItem -LiteralPath $root -Recurse -Filter "*.vmx" -ErrorAction SilentlyContinue |
            Where-Object {
                $displayName = ""
                try {
                    $displayName = (Select-String -LiteralPath $_.FullName -Pattern "^displayName\s*=\s*" -ErrorAction SilentlyContinue |
                        Select-Object -First 1).Line
                } catch {
                    $displayName = ""
                }
                $_.FullName -match "(^|[\\_\-\s])$HostAlias([\\_\-\s]|$)" -or
                    $displayName -match "(^|[\\_\-\s])$HostAlias([\\_\-\s]|$)"
            } |
            Select-Object -First 1
        if ($match) {
            return $match.FullName
        }
    }
    return ""
}

function Get-VmMap {
    $map = [ordered]@{}
    foreach ($alias in $VmOrder) {
        $map[$alias] = Resolve-VmxPath -HostAlias $alias
    }
    return $map
}

function Write-StartupScript {
    param([hashtable]$VmMap)

    New-Item -ItemType Directory -Path $ProgramDataDir -Force | Out-Null
    $vmRows = foreach ($alias in $VmOrder) {
        "    @{ Alias = `"$alias`"; Path = `"$($VmMap[$alias])`" }"
    }
    $body = @"
`$ErrorActionPreference = "Continue"
`$VmrunPath = "$VmrunPath"
`$VMs = @(
$($vmRows -join ",`n")
)

Start-Service -Name "VMAuthdService" -ErrorAction SilentlyContinue
Start-Service -Name "VMnetDHCP" -ErrorAction SilentlyContinue
Start-Sleep -Seconds 30
`$running = (& `$VmrunPath list 2>`$null) -join [Environment]::NewLine

foreach (`$vm in `$VMs) {
    if (-not (Test-Path -LiteralPath `$vm.Path)) {
        Write-Output "VMX_MISSING alias=`$(`$vm.Alias) path=`$(`$vm.Path)"
        continue
    }
    if (`$running -like "*`$(`$vm.Path)*") {
        Write-Output "VM_ALREADY_RUNNING alias=`$(`$vm.Alias) path=`$(`$vm.Path)"
        continue
    }
    & `$VmrunPath -T ws start `$vm.Path nogui
    Write-Output "VM_START_REQUESTED alias=`$(`$vm.Alias) path=`$(`$vm.Path) exit=`$LASTEXITCODE"
    Start-Sleep -Seconds 20
}
"@
    Set-Content -LiteralPath $StartScript -Value $body -Encoding UTF8
}

function Apply-WindowsUpdatePolicy {
    $auPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
    New-Item -Path $auPath -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "NoAutoRebootWithLoggedOnUsers" -Value 1 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "AlwaysAutoRebootAtScheduledTime" -Value 0 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "AUOptions" -Value 3 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "ScheduledInstallDay" -Value 0 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "ScheduledInstallTime" -Value 3 -PropertyType DWord -Force | Out-Null
    New-ItemProperty -Path $auPath -Name "AUPowerManagement" -Value 0 -PropertyType DWord -Force | Out-Null
}

function Apply-ScheduledTask {
    $action = New-ScheduledTaskAction `
        -Execute "powershell.exe" `
        -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$StartScript`""
    $trigger = New-ScheduledTaskTrigger -AtStartup
    $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
    $settings = New-ScheduledTaskSettingsSet `
        -AllowStartIfOnBatteries `
        -DontStopIfGoingOnBatteries `
        -MultipleInstances IgnoreNew `
        -RestartCount 3 `
        -RestartInterval (New-TimeSpan -Minutes 2) `
        -StartWhenAvailable
    Register-ScheduledTask `
        -TaskName $TaskName `
        -Action $action `
        -Trigger $trigger `
        -Principal $principal `
        -Settings $settings `
        -Force | Out-Null
}

function Get-VmrunList {
    if (-not (Test-Path -LiteralPath $VmrunPath)) {
        return @()
    }
    try {
        return @(& $VmrunPath list 2>$null | Select-Object -Skip 1)
    } catch {
        return @()
    }
}

function Get-VmwareServiceReadback {
    $serviceNames = @("VMAuthdService", "VMnetDHCP")
    foreach ($name in $serviceNames) {
        $service = Get-Service -Name $name -ErrorAction SilentlyContinue
        $startupType = "unknown"
        try {
            $cim = Get-CimInstance -ClassName Win32_Service -Filter "Name='$name'" -ErrorAction SilentlyContinue
            if ($cim) {
                $startupType = $cim.StartMode
            }
        } catch {
            $startupType = "unknown"
        }
        $present = [int][bool]$service
        $status = if ($service) { "$($service.Status)" } else { "missing" }
        $ok = [int]($service -and $service.Status -eq "Running" -and $startupType -in @("Auto", "Automatic"))
        Write-Output "VMWARE_SERVICE name=$name present=$present status=$status startup_type=$startupType ok=$ok"
    }
}

function Get-AutostartTaskReadback {
    $task = $null
    try {
        $task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
    } catch {
        $task = $null
    }
    $present = [int][bool]$task
    $enabled = 0
    $state = "missing"
    $triggerCount = 0
    $actionCount = 0
    if ($task) {
        $enabled = [int]($task.State -ne "Disabled")
        $state = "$($task.State)"
        $triggerCount = @($task.Triggers).Count
        $actionCount = @($task.Actions).Count
    }
    $ok = [int]($task -and $enabled -eq 1 -and $triggerCount -gt 0 -and $actionCount -gt 0)
    Write-Output "VMWARE_AUTOSTART_TASK name=$TaskName present=$present enabled=$enabled state=$state trigger_count=$triggerCount action_count=$actionCount ok=$ok"
}

function Get-WindowsUpdatePolicyReadback {
    $auPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
    $expected = [ordered]@{
        "NoAutoRebootWithLoggedOnUsers" = 1
        "AlwaysAutoRebootAtScheduledTime" = 0
        "AUOptions" = 3
        "ScheduledInstallDay" = 0
        "ScheduledInstallTime" = 3
        "AUPowerManagement" = 0
    }
    $props = $null
    if (Test-Path -LiteralPath $auPath) {
        $props = Get-ItemProperty -LiteralPath $auPath -ErrorAction SilentlyContinue
    }
    foreach ($name in $expected.Keys) {
        $value = "missing"
        if ($props -and $null -ne $props.$name) {
            $value = "$($props.$name)"
        }
        $ok = [int]($value -eq "$($expected[$name])")
        Write-Output "WINDOWS_UPDATE_POLICY name=$name value=$value expected=$($expected[$name]) ok=$ok"
    }
}

function Get-VmPowerReadback {
    param([hashtable]$VmMap)

    $running = Get-VmrunList
    foreach ($alias in $VmOrder) {
        $path = "$($VmMap[$alias])"
        $present = [int][bool]$path
        $runningMatch = 0
        if ($path) {
            $runningMatch = [int]($running -contains $path)
        }
        Write-Output "VM_POWER alias=$alias vmx_present=$present running=$runningMatch"
    }
}

$vmMap = Get-VmMap
$missing = @($VmOrder | Where-Object { -not $vmMap[$_] })
$vmrunPresent = Test-Path -LiteralPath $VmrunPath

Write-Output "AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1"
Write-Output "MODE=$Mode"
Write-Output "VMRUN_PRESENT=$([int]$vmrunPresent)"
if ($SuppliedVmx["111"] -or $VmOrder -contains "111") {
    Write-Output "HOST111_VMWARE_TARGET=explicit_or_required"
} else {
    Write-Output "HOST111_VMWARE_TARGET=not_required_by_default_missing_no_vmx_found_on_99"
}
foreach ($alias in $VmOrder) {
    Write-Output "VMX alias=$alias path=$($vmMap[$alias]) present=$([int][bool]$vmMap[$alias])"
}
Get-VmwareServiceReadback
Get-AutostartTaskReadback
Get-WindowsUpdatePolicyReadback
Get-VmPowerReadback -VmMap $vmMap

$serviceReady = @("VMAuthdService", "VMnetDHCP") | ForEach-Object {
    $svc = Get-Service -Name $_ -ErrorAction SilentlyContinue
    $svc -and $svc.Status -eq "Running"
}
$task = $null
try {
    $task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
} catch {
    $task = $null
}
$policyValues = @{}
try {
    $policy = Get-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -ErrorAction SilentlyContinue
    if ($policy) {
        $policyValues["NoAutoRebootWithLoggedOnUsers"] = $policy.NoAutoRebootWithLoggedOnUsers
        $policyValues["AlwaysAutoRebootAtScheduledTime"] = $policy.AlwaysAutoRebootAtScheduledTime
        $policyValues["AUOptions"] = $policy.AUOptions
        $policyValues["ScheduledInstallDay"] = $policy.ScheduledInstallDay
        $policyValues["ScheduledInstallTime"] = $policy.ScheduledInstallTime
        $policyValues["AUPowerManagement"] = $policy.AUPowerManagement
    }
} catch {
    $policyValues = @{}
}
$windowsUpdateReady = (
    "$($policyValues["NoAutoRebootWithLoggedOnUsers"])" -eq "1" -and
    "$($policyValues["AlwaysAutoRebootAtScheduledTime"])" -eq "0" -and
    "$($policyValues["AUOptions"])" -eq "3" -and
    "$($policyValues["ScheduledInstallDay"])" -eq "0" -and
    "$($policyValues["ScheduledInstallTime"])" -eq "3" -and
    "$($policyValues["AUPowerManagement"])" -eq "0"
)
$runningVmList = Get-VmrunList
$vmPowerReady = $true
foreach ($alias in $VmOrder) {
    $path = "$($vmMap[$alias])"
    if (-not $path -or -not ($runningVmList -contains $path)) {
        $vmPowerReady = $false
    }
}
$configReady = (
    $vmrunPresent -and
    $missing.Count -eq 0 -and
    ($serviceReady -notcontains $false) -and
    $task -and
    $task.State -ne "Disabled" -and
    @($task.Triggers).Count -gt 0 -and
    @($task.Actions).Count -gt 0 -and
    $windowsUpdateReady
)
Write-Output "VMWARE_AUTOSTART_CONFIG_READY=$([int]$configReady)"
Write-Output "VMWARE_AUTOSTART_POWER_READY=$([int]$vmPowerReady)"
Write-Output "WINDOWS_UPDATE_NO_AUTO_REBOOT_READY=$([int]$windowsUpdateReady)"
Write-Output "VMWARE_AUTOSTART_VERIFY_READY=$([int]($configReady -and $vmPowerReady))"

if (-not $vmrunPresent) {
    Write-Error "vmrun.exe not found at $VmrunPath"
}

if ($missing.Count -gt 0) {
    Write-Output "MISSING_VMX_ALIASES=$($missing -join ',')"
    if ($Mode -eq "Apply") {
        throw "Apply requires explicit or discoverable VMX paths for every required VM."
    }
}

if ($Mode -eq "Verify") {
    exit 0
}

if ($Mode -eq "DryRun") {
    Write-Output "DRY_RUN would_write_start_script=$StartScript"
    Write-Output "DRY_RUN would_register_task=$TaskName"
    Write-Output "DRY_RUN would_apply_windows_update_no_auto_reboot_policy=1"
    exit 0
}

Write-StartupScript -VmMap $vmMap
Set-Service -Name "VMAuthdService" -StartupType Automatic -ErrorAction SilentlyContinue
Set-Service -Name "VMnetDHCP" -StartupType Automatic -ErrorAction SilentlyContinue
Apply-ScheduledTask
Apply-WindowsUpdatePolicy

Write-Output "APPLIED scheduled_task=$TaskName start_script=$StartScript windows_update_no_auto_reboot_policy=1"
