src/public/Infrastructure/Resolve-AitherInfraPrereqs.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Resolve and auto-install infrastructure prerequisites for the AitherOS ISO pipeline.

.DESCRIPTION
    Acts as a dependency resolver for the AitherOS ISO build and deploy pipeline.
    Checks for all required tools and, if missing, invokes the appropriate installer
    script from the 01-infrastructure automation directory.

    Called automatically by:
      - New-AitherWindowsISO (the orchestrator cmdlet)
      - 3105_Build-WindowsISO.ps1 (the ISO builder)

    Can also be used standalone for validation.

    Required components by pipeline phase:
      ISO Build: Windows ADK (oscdimg.exe), DISM, Administrator
      Tofu Apply: OpenTofu >= 1.6.0, Taliesin Hyper-V provider
      VM Hosting: Hyper-V role enabled
      Remote Mgmt: WinRM / PSRemoting

.PARAMETER Scope
    Which prerequisites to resolve:
      - All: Everything needed for the full pipeline
      - ISO: Only ISO building prerequisites (ADK, DISM)
      - Deploy: Only deployment prerequisites (OpenTofu, Hyper-V)
      - Validate: Check-only, do not install anything

.PARAMETER AutoInstall
    Automatically install missing prerequisites without prompting.
    Default: $true (since this is designed to be called by automation).

.PARAMETER ScriptsDir
    Path to the 01-infrastructure automation scripts directory.
    Auto-detected from module root if not specified.

.PARAMETER PassThru
    Return a result object instead of throwing on failure.

.EXAMPLE
    Resolve-AitherInfraPrereqs
    Checks and auto-installs all prerequisites for the full pipeline.

.EXAMPLE
    Resolve-AitherInfraPrereqs -Scope ISO -PassThru
    Checks ISO prerequisites only and returns a status object.

.EXAMPLE
    Resolve-AitherInfraPrereqs -Scope Validate
    Validation-only mode — reports status without installing anything.

.OUTPUTS
    PSCustomObject (if -PassThru) with properties: AllSatisfied, Results, RebootRequired

.NOTES
    Exported AitherZero module function — available after Import-Module AitherZero.
#>

function Resolve-AitherInfraPrereqs {
    [CmdletBinding()]
    param(
        [ValidateSet('All', 'ISO', 'Deploy', 'Validate')]
        [string]$Scope = 'All',

        [bool]$AutoInstall = $true,

        [string]$ScriptsDir,

        [switch]$PassThru
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    # ─────────────────────────────────────────────
    # Locate installer scripts directory
    # ─────────────────────────────────────────────
    if (-not $ScriptsDir) {
        # Try module root
        $moduleRoot = if (Get-Command Get-AitherModuleRoot -ErrorAction SilentlyContinue) {
            Get-AitherModuleRoot
        }
        else {
            # Walk up from this file's location
            $candidate = $PSScriptRoot
            while ($candidate -and -not (Test-Path (Join-Path $candidate 'AitherZero\AitherZero.psd1'))) {
                $candidate = Split-Path $candidate -Parent
            }
            $candidate
        }
        $ScriptsDir = Join-Path $moduleRoot 'AitherZero\library\automation-scripts\01-infrastructure'
    }

    $isValidateOnly = $Scope -eq 'Validate'
    if ($isValidateOnly) { $AutoInstall = $false }

    $results = [ordered]@{}
    $rebootRequired = $false

    Write-Host "`n╔════════════════════════════════════════════╗" -ForegroundColor Cyan
    Write-Host "║ AitherOS Prerequisite Resolver ║" -ForegroundColor Cyan
    Write-Host "║ Scope: $Scope$((' ' * (35 - $Scope.Length)))║" -ForegroundColor Cyan
    Write-Host "╚════════════════════════════════════════════╝`n" -ForegroundColor Cyan

    # ── Helper: invoke installer script ──
    function Invoke-InstallerScript {
        param([string]$ScriptName, [hashtable]$ScriptParams = @{})
        $scriptPath = Join-Path $ScriptsDir $ScriptName
        if (-not (Test-Path $scriptPath)) {
            Write-Warning " Installer script not found: $scriptPath"
            return $false
        }
        try {
            & $scriptPath @ScriptParams
            return ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq 200)
        }
        catch {
            Write-Warning " Installer failed: $($_.Exception.Message)"
            return $false
        }
    }

    # ═══════════════════════════════════════════
    # 1. Administrator check (needed for all scopes)
    # ═══════════════════════════════════════════
    $isAdmin = if ($IsWindows) {
        ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
            [Security.Principal.WindowsBuiltInRole]::Administrator)
    } else { (id -u) -eq 0 }

    $results['Administrator'] = $isAdmin
    if ($isAdmin) {
        Write-Host " [OK] Administrator privileges" -ForegroundColor Green
    }
    else {
        Write-Host " [--] Not running as Administrator (some installs may fail)" -ForegroundColor Yellow
    }

    # ═══════════════════════════════════════════
    # 2. ISO Prerequisites (ADK + DISM)
    # ═══════════════════════════════════════════
    if ($Scope -in @('All', 'ISO')) {
        # DISM check
        $dismPresent = $null -ne (Get-Command dism.exe -ErrorAction SilentlyContinue)
        $results['DISM'] = $dismPresent
        if ($dismPresent) {
            Write-Host " [OK] DISM" -ForegroundColor Green
        }
        else {
            Write-Host " [--] DISM not found (Windows built-in — check PATH)" -ForegroundColor Red
        }

        # oscdimg / Windows ADK
        $oscdimg = $env:OSCDIMG_PATH
        if (-not $oscdimg) {
            $adkPaths = @(
                "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe",
                "${env:ProgramFiles}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe",
                "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
            )
            foreach ($p in $adkPaths) {
                if (Test-Path $p) { $oscdimg = $p; break }
            }
        }
        $adkPresent = $oscdimg -and (Test-Path $oscdimg)
        $results['WindowsADK'] = $adkPresent

        if ($adkPresent) {
            Write-Host " [OK] Windows ADK (oscdimg: $oscdimg)" -ForegroundColor Green
            $env:OSCDIMG_PATH = $oscdimg
        }
        else {
            Write-Host " [--] Windows ADK / oscdimg.exe not found" -ForegroundColor Red
            if ($AutoInstall) {
                Write-Host " → Installing..." -ForegroundColor Yellow
                $ok = Invoke-InstallerScript '0101_Install-WindowsADK.ps1'
                $results['WindowsADK'] = $ok
                if ($ok) { Write-Host " [OK] Windows ADK installed" -ForegroundColor Green }
            }
        }
    }

    # ═══════════════════════════════════════════
    # 3. Deploy Prerequisites (OpenTofu + Hyper-V)
    # ═══════════════════════════════════════════
    if ($Scope -in @('All', 'Deploy')) {
        # OpenTofu / Terraform
        $tofuPresent = $null -ne (Get-Command tofu -ErrorAction SilentlyContinue) -or
                       $null -ne (Get-Command terraform -ErrorAction SilentlyContinue)
        $results['OpenTofu'] = $tofuPresent

        if ($tofuPresent) {
            $cmd = if (Get-Command tofu -ErrorAction SilentlyContinue) { 'tofu' } else { 'terraform' }
            Write-Host " [OK] OpenTofu/Terraform ($cmd)" -ForegroundColor Green
        }
        else {
            Write-Host " [--] OpenTofu / Terraform not found" -ForegroundColor Red
            if ($AutoInstall) {
                Write-Host " → Installing OpenTofu..." -ForegroundColor Yellow
                $ok = Invoke-InstallerScript '0102_Install-OpenTofu.ps1' @{ IncludeHyperVProvider = $true }
                $results['OpenTofu'] = $ok
                if ($ok) { Write-Host " [OK] OpenTofu installed" -ForegroundColor Green }
            }
        }

        # Hyper-V
        $hvPresent = $false
        if ($IsWindows) {
            try {
                $hvFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -ErrorAction SilentlyContinue
                $hvPresent = $hvFeature -and $hvFeature.State -eq 'Enabled'
            }
            catch {
                try {
                    $hvRole = Get-WindowsFeature -Name Hyper-V -ErrorAction SilentlyContinue
                    $hvPresent = $hvRole -and $hvRole.Installed
                }
                catch { }
            }
        }
        $results['HyperV'] = $hvPresent

        if ($hvPresent) {
            Write-Host " [OK] Hyper-V" -ForegroundColor Green
        }
        else {
            Write-Host " [--] Hyper-V not enabled" -ForegroundColor Red
            if ($AutoInstall) {
                Write-Host " → Enabling Hyper-V..." -ForegroundColor Yellow
                $ok = Invoke-InstallerScript '0105_Enable-HyperV.ps1'
                if ($LASTEXITCODE -eq 200) {
                    $rebootRequired = $true
                    $results['HyperV'] = 'RebootRequired'
                    Write-Host " [!!] Hyper-V enabled — REBOOT REQUIRED before VMs can be created" -ForegroundColor Yellow
                }
                elseif ($ok) {
                    $results['HyperV'] = $true
                    Write-Host " [OK] Hyper-V enabled" -ForegroundColor Green
                }
            }
        }
    }

    # ═══════════════════════════════════════════
    # Summary
    # ═══════════════════════════════════════════
    $missing = ($results.GetEnumerator() | Where-Object { $_.Value -eq $false }).Count

    Write-Host ""
    if ($missing -eq 0 -and -not $rebootRequired) {
        Write-Host " ✓ All prerequisites satisfied — pipeline ready!" -ForegroundColor Green
    }
    elseif ($rebootRequired) {
        Write-Host " ⚠ Reboot required before the pipeline can create VMs." -ForegroundColor Yellow
        Write-Host " ISO building is still possible without reboot." -ForegroundColor Gray
    }
    else {
        Write-Host " ✗ $missing prerequisite(s) still missing." -ForegroundColor Red
        if ($isValidateOnly) {
            Write-Host " Run: Resolve-AitherInfraPrereqs -Scope $Scope" -ForegroundColor Yellow
        }
    }
    Write-Host ""

    if ($PassThru) {
        return [PSCustomObject]@{
            PSTypeName     = 'AitherOS.PrereqResolverResult'
            Timestamp      = Get-Date -Format 'o'
            Scope          = $Scope
            Results        = $results
            AllSatisfied   = ($missing -eq 0 -and -not $rebootRequired)
            MissingCount   = $missing
            RebootRequired = $rebootRequired
        }
    }

    # If validate-only or missing items, don't throw — caller decides
    if ($missing -gt 0 -and -not $isValidateOnly -and -not $PassThru) {
        $missingNames = ($results.GetEnumerator() | Where-Object { $_.Value -eq $false } | ForEach-Object { $_.Key }) -join ', '
        throw "Missing infrastructure prerequisites: $missingNames. Run with -AutoInstall `$true or install manually."
    }
}