#
# Upload-Image.ps1 — Copy MCL cached image to the PXE server
#
# Reads user_selections.json to upload only the selected OS, matching
# packages, and config files. Drivers are EXCLUDED by default.
#
# Usage:
#   .\Upload-Image.ps1                              (selected OS + packages, no drivers)
#   .\Upload-Image.ps1 -IncludeDrivers              (also upload selected hardware drivers)
#   .\Upload-Image.ps1 -CachePath "D:\MCL\Cache"    (custom cache location)
#   .\Upload-Image.ps1 -Server 10.9.100.1           (custom server IP)
#
# After upload, use the PXE webapp (http://10.9.100.1:9009) to import
# the uploaded content into the desired image type.
#

param(
    [string]$CachePath = "C:\ProgramData\GEAerospace\MediaCreator\Cache",
    [string]$Server = "10.9.100.1",
    [string]$User = "pxe-upload",
    [string]$Pass = "pxe",
    [switch]$IncludeDrivers
)

$Share = "\\$Server\image-upload"

function Format-Size {
    param([long]$Bytes)
    if ($Bytes -ge 1GB) { return "{0:N1} GB" -f ($Bytes / 1GB) }
    if ($Bytes -ge 1MB) { return "{0:N1} MB" -f ($Bytes / 1MB) }
    return "{0:N0} KB" -f ($Bytes / 1KB)
}

function Resolve-DestDir {
    param([string]$Dir)
    return ($Dir -replace '^\*destinationdir\*\\?', '')
}

Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "  PXE Server Image Uploader" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

# --- Validate source paths ---
$DeployPath = Join-Path $CachePath "Deploy"
$ToolsPath  = Join-Path (Split-Path $CachePath -Parent) "Tools"
if (-not (Test-Path $ToolsPath -PathType Container)) {
    $ToolsPath = "C:\ProgramData\GEAerospace\MediaCreator\Tools"
}
$SourcesZip = Join-Path $CachePath "Boot\Sources.zip"

if (-not (Test-Path $DeployPath -PathType Container)) {
    Write-Host "ERROR: Deploy directory not found at $DeployPath" -ForegroundColor Red
    Write-Host "  .\Upload-Image.ps1 -CachePath ""D:\Path\To\Cache""" -ForegroundColor Yellow
    exit 1
}

# --- Parse user_selections.json ---
$SelectionsFile = Join-Path $ToolsPath "user_selections.json"
if (-not (Test-Path $SelectionsFile)) {
    Write-Host "ERROR: user_selections.json not found at $SelectionsFile" -ForegroundColor Red
    Write-Host "  Run Media Creator Lite first to create a configuration." -ForegroundColor Yellow
    exit 1
}

$selections = (Get-Content $SelectionsFile -Raw | ConvertFrom-Json)[0]
$selectedOsId = $selections.OperatingSystemSelection
$selectedModelIds = @($selections.HardwareModelSelection | ForEach-Object { $_.Id } | Select-Object -Unique)

# --- Parse control JSONs ---
$ControlPath = Join-Path $DeployPath "Control"

$osJsonFile     = Join-Path $ControlPath "OperatingSystem.json"
$driverJsonFile = Join-Path $ControlPath "HardwareDriver.json"
$pkgJsonFile    = Join-Path $ControlPath "packages.json"

if (-not (Test-Path $osJsonFile)) {
    Write-Host "ERROR: OperatingSystem.json not found in $ControlPath" -ForegroundColor Red
    exit 1
}

$osJson     = Get-Content $osJsonFile -Raw | ConvertFrom-Json
$driverJson = if (Test-Path $driverJsonFile) { Get-Content $driverJsonFile -Raw | ConvertFrom-Json } else { @() }
$pkgJson    = if (Test-Path $pkgJsonFile)    { Get-Content $pkgJsonFile -Raw | ConvertFrom-Json }    else { @() }

# --- Resolve selections to paths ---

# OS: match OperatingSystemSelection ID to OperatingSystem.json entries
$matchedOs = @($osJson | Where-Object { $_.operatingSystemVersion.id -eq [int]$selectedOsId })
$osDirs = @()
$osTotalSize = [long]0
foreach ($os in $matchedOs) {
    $rel = Resolve-DestDir $os.operatingSystemVersion.wim.DestinationDir
    $osDirs += $rel
    $osTotalSize += $os.operatingSystemVersion.wim.size
}

# Packages: enabled + matching OS ID
$matchedPkgs = @($pkgJson | Where-Object { $_.aOsIds -contains $selectedOsId -and $_.enabled -eq 1 })
$pkgTotalSize = [long]0
foreach ($pkg in $matchedPkgs) { $pkgTotalSize += $pkg.size }

# Drivers: match selected model IDs (family) + OS ID, deduplicate by path
$allMatchingDrivers = @($driverJson | Where-Object {
    $selectedModelIds -contains $_.family -and $_.aOsIds -contains $selectedOsId
})
$allDriverDirSet = [ordered]@{}
foreach ($drv in $allMatchingDrivers) {
    $rel = Resolve-DestDir $drv.DestinationDir
    if (-not $allDriverDirSet.Contains($rel)) { $allDriverDirSet[$rel] = $drv.size }
}
$allDriverCount = $allDriverDirSet.Count
$allDriverTotalSize = [long]0
$allDriverDirSet.Values | ForEach-Object { $allDriverTotalSize += $_ }

$driverDirs = @()
$driverTotalSize = [long]0
if ($IncludeDrivers) {
    $driverDirs = @($allDriverDirSet.Keys)
    $driverTotalSize = $allDriverTotalSize
}

# --- Display upload plan ---
Write-Host "  Cache:     $CachePath"
Write-Host "  Server:    $Server"
Write-Host ""
Write-Host "  Upload Plan (from user_selections.json):" -ForegroundColor Cyan
Write-Host "  ------------------------------------------"

if ($matchedOs.Count -gt 0) {
    $osName = $matchedOs[0].operatingSystemVersion.marketingName
    Write-Host "  OS:        $osName ($(Format-Size $osTotalSize))" -ForegroundColor Green
} else {
    Write-Host "  OS:        No match for selection ID $selectedOsId" -ForegroundColor Red
}

Write-Host "  Packages:  $($matchedPkgs.Count) update(s) ($(Format-Size $pkgTotalSize))" -ForegroundColor Green

if ($IncludeDrivers) {
    Write-Host "  Drivers:   $($driverDirs.Count) model(s) ($(Format-Size $driverTotalSize))" -ForegroundColor Green
} else {
    Write-Host "  Drivers:   SKIPPED -- $allDriverCount available, use -IncludeDrivers" -ForegroundColor Yellow
}

Write-Host "  Control:   Always included" -ForegroundColor Gray
Write-Host "  Tools:     $(if (Test-Path $ToolsPath) { 'Yes' } else { 'Not found' })" -ForegroundColor $(if (Test-Path $ToolsPath) { "Gray" } else { "Yellow" })
Write-Host "  Sources:   $(if (Test-Path $SourcesZip) { 'Yes (from Boot\Sources.zip)' } else { 'Not found' })" -ForegroundColor $(if (Test-Path $SourcesZip) { "Gray" } else { "Yellow" })
Write-Host ""

# --- Connect to SMB share ---
Write-Host "Connecting to $Share ..." -ForegroundColor Gray
net use $Share /delete 2>$null | Out-Null

$netResult = net use $Share /user:$User $Pass 2>&1
if ($LASTEXITCODE -ne 0) {
    Write-Host "ERROR: Could not connect to $Share" -ForegroundColor Red
    Write-Host $netResult -ForegroundColor Red
    Write-Host ""
    Write-Host "Make sure:" -ForegroundColor Yellow
    Write-Host "  - The PXE server is running at $Server" -ForegroundColor Yellow
    Write-Host "  - This PC is on the 10.9.100.x network" -ForegroundColor Yellow
    Write-Host "  - Samba is running on the PXE server" -ForegroundColor Yellow
    exit 1
}
Write-Host "Connected." -ForegroundColor Green
Write-Host ""

$failed = $false
$stepNum = 0
$totalSteps = 1  # Deploy base always
if ($matchedOs.Count -gt 0) { $totalSteps++ }
if ($matchedPkgs.Count -gt 0) { $totalSteps++ }
if ($IncludeDrivers -and $driverDirs.Count -gt 0) { $totalSteps++ }
if (Test-Path $ToolsPath -PathType Container) { $totalSteps++ }
if (Test-Path $SourcesZip) { $totalSteps++ }

# --- Step: Deploy base (Control, Applications, config -- skip big dirs) ---
$stepNum++
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "[$stepNum/$totalSteps] Copying Deploy\ base (Control, Applications, config) ..." -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

robocopy $DeployPath "$Share\Deploy" /E /XD "Operating Systems" "Out-of-box Drivers" "Packages" /R:3 /W:5 /NP /ETA
if ($LASTEXITCODE -ge 8) {
    Write-Host "ERROR: Deploy base copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
    $failed = $true
}

# --- Step: Operating System ---
if ($matchedOs.Count -gt 0) {
    $stepNum++
    Write-Host ""
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "[$stepNum/$totalSteps] Copying Operating System ..." -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan

    foreach ($osDir in $osDirs) {
        $src = Join-Path $CachePath $osDir
        $dst = Join-Path $Share $osDir
        if (Test-Path $src -PathType Container) {
            Write-Host "  $osDir" -ForegroundColor Gray
            robocopy $src $dst /E /R:3 /W:5 /NP /ETA
            if ($LASTEXITCODE -ge 8) {
                Write-Host "ERROR: OS copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
                $failed = $true
            }
        } else {
            Write-Host "  SKIPPED (not cached): $osDir" -ForegroundColor Yellow
        }
    }
}

# --- Step: Packages ---
if ($matchedPkgs.Count -gt 0) {
    $stepNum++
    Write-Host ""
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "[$stepNum/$totalSteps] Copying Packages ..." -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan

    # Group packages by destination directory for efficient robocopy
    $pkgGroups = [ordered]@{}
    foreach ($pkg in $matchedPkgs) {
        $rel = Resolve-DestDir $pkg.destinationDir
        if (-not $pkgGroups.Contains($rel)) { $pkgGroups[$rel] = @() }
        $pkgGroups[$rel] += $pkg.fileName
    }

    foreach ($dir in $pkgGroups.Keys) {
        $src = Join-Path $CachePath $dir
        $dst = Join-Path $Share $dir
        $files = $pkgGroups[$dir]
        if (Test-Path $src -PathType Container) {
            foreach ($f in $files) { Write-Host "  $f" -ForegroundColor Gray }
            $robocopyArgs = @($src, $dst) + $files + @("/R:3", "/W:5", "/NP", "/ETA")
            & robocopy @robocopyArgs
            if ($LASTEXITCODE -ge 8) {
                Write-Host "ERROR: Package copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
                $failed = $true
            }
        } else {
            Write-Host "  SKIPPED (not cached): $dir" -ForegroundColor Yellow
        }
    }
}

# --- Step: Drivers (only with -IncludeDrivers) ---
if ($IncludeDrivers -and $driverDirs.Count -gt 0) {
    $stepNum++
    Write-Host ""
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "[$stepNum/$totalSteps] Copying Drivers ($($driverDirs.Count) model(s)) ..." -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan

    $drvCopied = 0
    foreach ($drvDir in $driverDirs) {
        $drvCopied++
        $src = Join-Path $CachePath $drvDir
        $dst = Join-Path $Share $drvDir
        if (Test-Path $src -PathType Container) {
            Write-Host "  [$drvCopied/$($driverDirs.Count)] $drvDir" -ForegroundColor Gray
            robocopy $src $dst /E /R:3 /W:5 /NP /ETA
            if ($LASTEXITCODE -ge 8) {
                Write-Host "ERROR: Driver copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
                $failed = $true
            }
        } else {
            Write-Host "  SKIPPED (not cached): $drvDir" -ForegroundColor Yellow
        }
    }
}

# --- Step: Tools ---
if (Test-Path $ToolsPath -PathType Container) {
    $stepNum++
    Write-Host ""
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "[$stepNum/$totalSteps] Copying Tools\ ..." -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan

    robocopy $ToolsPath "$Share\Tools" /E /R:3 /W:5 /NP /ETA
    if ($LASTEXITCODE -ge 8) {
        Write-Host "ERROR: Tools copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
        $failed = $true
    }
}

# --- Step: Sources ---
$TempSources = $null
if (Test-Path $SourcesZip) {
    $stepNum++
    Write-Host ""
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "[$stepNum/$totalSteps] Extracting and copying Sources\ ..." -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan

    $TempExtract = Join-Path $env:TEMP "SourcesExtract"
    Remove-Item -Recurse -Force $TempExtract -ErrorAction SilentlyContinue
    Expand-Archive $SourcesZip -DestinationPath $TempExtract -Force

    # Handle nested Sources folder (zip may contain Sources/ at root)
    $TempSources = $TempExtract
    if ((Test-Path (Join-Path $TempExtract "Sources")) -and -not (Test-Path (Join-Path $TempExtract "Diskpart"))) {
        $TempSources = Join-Path $TempExtract "Sources"
    }

    robocopy $TempSources "$Share\Sources" /E /R:3 /W:5 /NP /ETA
    if ($LASTEXITCODE -ge 8) {
        Write-Host "ERROR: Sources copy failed (exit code $LASTEXITCODE)" -ForegroundColor Red
        $failed = $true
    }

    Remove-Item -Recurse -Force $TempExtract -ErrorAction SilentlyContinue
}

# --- Verify small files (SMB write-cache workaround) ---
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Verifying small files ..." -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

$fixCount = 0
$verifyPairs = @(
    @{ Local = (Join-Path $DeployPath "Control"); Remote = "$Share\Deploy\Control" }
)
if (Test-Path $ToolsPath -PathType Container) {
    $verifyPairs += @{ Local = $ToolsPath; Remote = "$Share\Tools" }
}

foreach ($pair in $verifyPairs) {
    $localDir  = $pair.Local
    $remoteDir = $pair.Remote
    if (-not (Test-Path $localDir -PathType Container)) { continue }

    Get-ChildItem -Path $localDir -Recurse -File -ErrorAction SilentlyContinue |
        Where-Object { $_.Length -gt 0 -and $_.Length -lt 1MB } |
        ForEach-Object {
            $rel = $_.FullName.Substring($localDir.Length)
            $dstFile = Join-Path $remoteDir $rel
            if (Test-Path $dstFile) {
                $dstSize = (Get-Item $dstFile).Length
                if ($dstSize -ne $_.Length) {
                    Write-Host "  Fixing: $rel ($dstSize -> $($_.Length) bytes)" -ForegroundColor Yellow
                    $bytes = [System.IO.File]::ReadAllBytes($_.FullName)
                    [System.IO.File]::WriteAllBytes($dstFile, $bytes)
                    $fixCount++
                }
            }
        }
}

if ($fixCount -eq 0) {
    Write-Host "  All files verified OK." -ForegroundColor Green
} else {
    Write-Host "  Fixed $fixCount file(s)." -ForegroundColor Yellow
}

# --- Disconnect ---
net use $Share /delete 2>$null | Out-Null

# --- Summary ---
Write-Host ""
if ($failed) {
    Write-Host "========================================" -ForegroundColor Red
    Write-Host "  Upload completed with errors." -ForegroundColor Red
    Write-Host "========================================" -ForegroundColor Red
} else {
    Write-Host "========================================" -ForegroundColor Green
    Write-Host "  Upload complete!" -ForegroundColor Green
    Write-Host "========================================" -ForegroundColor Green
}
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Cyan
Write-Host "  1. Open the PXE webapp: http://$Server`:9009" -ForegroundColor White
Write-Host "  2. Go to Image Import" -ForegroundColor White
Write-Host "  3. Select source 'image-upload' and target image type" -ForegroundColor White
Write-Host "  4. Click Import" -ForegroundColor White
Write-Host ""
