<#
.SYNOPSIS
  Build the shopdb-flask image on a CONNECTED box and bundle it (plus the MySQL
  image) into one tarball for transfer to an air-gapped site.

.DESCRIPTION
  Air-gapped sites cannot pip install / npm ci / docker pull. This builds the
  fully self-contained application image where connectivity exists, pulls the
  MySQL image the stack needs, and `docker save`s both into a single gzipped
  tarball with a SHA-256 checksum. Carry the .tar.gz to the site and follow
  docs/DEPLOY-AIRGAP.md (docker load + docker-compose.airgap.yml).

  Run from the repo root. Requires Docker Desktop. The connected build box needs
  clean access to Docker Hub + PyPI + npm. If it is behind GE Zscaler and the
  build fails on pip/npm SSL (CERTIFICATE_VERIFY_FAILED), build from a box with
  clean internet, or trust the corp root CA in the build - see
  docs/DEPLOY-AIRGAP.md "Building behind Zscaler".

.PARAMETER Version
  Image tag for shopdb-flask (default 0.7.0). This is the IMAGE_TAG the site
  sets in its .env so docker-compose.airgap.yml runs the matching image.

.PARAMETER MysqlImage
  MySQL image the stack runs (default mysql:8.4). Must match db.image in
  docker-compose.airgap.yml.

.PARAMETER OutDir
  Where to write the tarball + checksum (default the repo root).

.PARAMETER SkipBuild
  Reuse an already-built shopdb-flask:<Version> image; only pull + save.

.EXAMPLE
  pwsh scripts/build-offline-bundle.ps1 -Version 0.7.0
#>
[CmdletBinding()]
param(
    [string]$Version = '0.7.0',
    [string]$MysqlImage = 'mysql:8.4',
    [string]$OutDir = '.',
    [switch]$SkipBuild
)

$ErrorActionPreference = 'Stop'

function Assert-LastExit([string]$What) {
    if ($LASTEXITCODE -ne 0) { throw "$What failed (exit $LASTEXITCODE)" }
}

# Fail early if docker is missing rather than midway through a long build.
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
    throw 'docker not found on PATH. Install Docker Desktop and retry.'
}

$appImage = "shopdb-flask:$Version"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
Push-Location $repoRoot
try {
    if (-not $SkipBuild) {
        Write-Host "==> Building $appImage" -ForegroundColor Cyan
        docker build -t $appImage .
        Assert-LastExit 'docker build'
    } else {
        Write-Host "==> Skipping build; reusing $appImage" -ForegroundColor Yellow
    }

    Write-Host "==> Pulling $MysqlImage" -ForegroundColor Cyan
    docker pull $MysqlImage
    Assert-LastExit 'docker pull'

    if (-not (Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null }
    $stem = "shopdb-stack-$Version"
    $tar = Join-Path $OutDir "$stem.tar"
    $gz = Join-Path $OutDir "$stem.tar.gz"

    Write-Host "==> Saving $appImage + $MysqlImage" -ForegroundColor Cyan
    docker save -o $tar $appImage $MysqlImage
    Assert-LastExit 'docker save'

    Write-Host "==> Compressing to $gz" -ForegroundColor Cyan
    $inStream = [System.IO.File]::OpenRead($tar)
    $outStream = [System.IO.File]::Create($gz)
    $gzip = New-Object System.IO.Compression.GzipStream($outStream, [System.IO.Compression.CompressionMode]::Compress)
    try {
        $inStream.CopyTo($gzip)
    } finally {
        $gzip.Dispose(); $outStream.Dispose(); $inStream.Dispose()
    }
    Remove-Item $tar

    $hash = (Get-FileHash $gz -Algorithm SHA256).Hash.ToLower()
    $sumFile = "$gz.sha256"
    "$hash  $stem.tar.gz" | Set-Content -Path $sumFile -NoNewline
    $sizeMb = [math]::Round((Get-Item $gz).Length / 1MB, 1)

    Write-Host ''
    Write-Host "Bundle ready: $gz ($sizeMb MB)" -ForegroundColor Green
    Write-Host "SHA-256:      $hash"
    Write-Host "Checksum:     $sumFile"
    Write-Host ''
    Write-Host 'At the air-gapped site (verify the checksum first):'
    Write-Host "  docker load -i $stem.tar.gz"
    Write-Host "  # set IMAGE_TAG=$Version in .env, then:"
    Write-Host '  docker compose -f docker-compose.airgap.yml up -d'
}
finally {
    Pop-Location
}
