<#
.SYNOPSIS
    Regenerate bundle-lock.json from the staged bundle, after showing what would
    change.

.DESCRIPTION
    Run this on the machine that assembled the wheelhouse, once the bundle holds
    the payload you intend to ship:

      bundle\wheels\               the wheels, built with the matching Python
      bundle\python\               the Python installer
      bundle\httpplatformhandler\  the IIS module MSI
      bundle\urlrewrite\           URL Rewrite MSI     (optional)
      bundle\mysql\                MySQL MSI           (optional)

    Then COMMIT the resulting bundle-lock.json. That commit is the review: it is
    the only place a change to what runs as SYSTEM on a customer's server becomes
    visible to a human. A lock regenerated and committed without reading the diff
    provides nothing, so this refuses to overwrite an existing lock until you
    have seen the change and passed -Yes.

.EXAMPLE
    .\refresh-bundle-lock.ps1                 # show the diff, write nothing
    .\refresh-bundle-lock.ps1 -Yes            # write it

.NOTES
    Building the wheelhouse itself no longer requires Windows. From any machine:
      pip download -r requirements.txt -d wheels --only-binary=:all: `
          --platform win_amd64 --python-version 314 --implementation cp --abi cp314
    Do it wherever you like; this script records what came out.
#>

[CmdletBinding()]
param(
    [string] $BundleRoot = (Join-Path $PSScriptRoot 'bundle'),
    [string] $LockPath   = (Join-Path $PSScriptRoot 'bundle-lock.json'),
    [string] $PythonTag  = 'cp314',
    [string] $Platform   = 'win_amd64',
    [switch] $Yes
)

$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'bundle-lock.ps1')

function Say { param($m, $c = 'Gray') Write-Host $m -ForegroundColor $c }

if (-not (Test-Path $BundleRoot)) {
    Say "bundle not found: $BundleRoot" 'Red'
    Say 'Stage it first with build-installer.ps1 (or build-installer.sh), then add' 'Yellow'
    Say 'the wheels and installers by hand.' 'Yellow'
    exit 1
}

Say ''
Say "  Hashing $BundleRoot" 'Cyan'
$new = New-BundleLock -BundleRoot $BundleRoot -PythonTag $PythonTag -Platform $Platform

foreach ($name in $new.payloads.Keys) {
    Say ("    {0,-22} {1,4} files" -f $name, $new.payloads[$name].files.Count)
}

$old = Read-BundleLock $LockPath
if (-not $old) {
    Say ''
    Say '  No existing lock - this will be the first one.' 'Yellow'
} else {
    # Diff by file, per payload, so the operator sees exactly which artifacts
    # changed rather than "the lock is different".
    Say ''
    Say '  Changes against the committed lock:' 'Cyan'
    $changes = 0
    foreach ($name in $new.payloads.Keys) {
        $oldFiles = @{}
        if ($old.payloads.PSObject.Properties.Name -contains $name) {
            foreach ($p in $old.payloads.$name.files.PSObject.Properties) { $oldFiles[$p.Name] = $p.Value.sha256 }
        }
        $newFiles = $new.payloads[$name].files
        foreach ($rel in ($newFiles.Keys | Sort-Object)) {
            if (-not $oldFiles.ContainsKey($rel))                 { Say "    + $name/$rel" 'Green';  $changes++ }
            elseif ($oldFiles[$rel] -ne $newFiles[$rel].sha256)   { Say "    ~ $name/$rel  (content changed)" 'Yellow'; $changes++ }
        }
        foreach ($rel in ($oldFiles.Keys | Sort-Object)) {
            if (-not $newFiles.ContainsKey($rel))                 { Say "    - $name/$rel" 'Red'; $changes++ }
        }
    }
    foreach ($p in $old.payloads.PSObject.Properties.Name) {
        if (-not $new.payloads.Contains($p)) { Say "    - $p/ (whole payload gone)" 'Red'; $changes++ }
    }
    if ($changes -eq 0) {
        Say '    none - the bundle already matches the lock' 'Green'
        exit 0
    }
    Say ''
    Say ("  {0} change(s)." -f $changes) 'White'
}

if (-not $Yes) {
    Say ''
    Say '  Nothing written. Read the list above, then re-run with -Yes.' 'Yellow'
    Say '  Commit the resulting bundle-lock.json - that commit IS the review.' 'Yellow'
    exit 2
}

# ConvertTo-Json defaults to a depth of 2, which silently flattens the per-file
# entries into "System.Collections.Hashtable" strings and produces a lock that
# verifies against nothing.
$new | ConvertTo-Json -Depth 8 | Set-Content -Path $LockPath -Encoding UTF8
Say ''
Say "  Written: $LockPath" 'Green'
Say '  Commit it.' 'Green'
