param(
    [string]$InnoCompiler = ""
)

$ErrorActionPreference = "Stop"
$ProjectRoot = [System.IO.Path]::GetFullPath(
    (Join-Path -Path $PSScriptRoot -ChildPath "..")
)
$BuildRoot = Join-Path -Path $ProjectRoot -ChildPath "build_artifacts"
$ReleaseRoot = Join-Path -Path $ProjectRoot -ChildPath "release"

# version.py is the single source of truth for the released version number.
$Version = (
    python -c "import sys; sys.path.insert(0, r'$ProjectRoot'); import version; print(version.APP_VERSION)"
)
if ($LASTEXITCODE -ne 0 -or -not $Version) {
    throw "Could not read APP_VERSION from version.py."
}
$Version = $Version.Trim()

$StageName = "QTFM Simple Gradiometer $Version"
$StageRoot = Join-Path -Path $ReleaseRoot -ChildPath $StageName
$ZipPath = Join-Path -Path $ReleaseRoot -ChildPath (
    "QTFM_Simple_Gradiometer_${Version}_Windows_x64.zip"
)

function Assert-ProjectPath {
    param([Parameter(Mandatory = $true)][string]$Path)
    $FullPath = [System.IO.Path]::GetFullPath($Path)
    $RootPrefix = $ProjectRoot.TrimEnd(
        [System.IO.Path]::DirectorySeparatorChar
    ) + [System.IO.Path]::DirectorySeparatorChar
    if (-not $FullPath.StartsWith(
        $RootPrefix,
        [System.StringComparison]::OrdinalIgnoreCase
    )) {
        throw "Refusing to modify a path outside the project: $FullPath"
    }
    return $FullPath
}

function Reset-ProjectDirectory {
    param([Parameter(Mandatory = $true)][string]$Path)
    $SafePath = Assert-ProjectPath -Path $Path
    if (Test-Path -LiteralPath $SafePath) {
        Remove-Item -LiteralPath $SafePath -Recurse -Force
    }
    New-Item -ItemType Directory -Path $SafePath | Out-Null
}

Set-Location -LiteralPath $ProjectRoot
Reset-ProjectDirectory -Path $BuildRoot
New-Item -ItemType Directory -Path $ReleaseRoot -Force | Out-Null
Reset-ProjectDirectory -Path $StageRoot

python -m pytest -q
if ($LASTEXITCODE -ne 0) {
    throw "Tests failed."
}

python .\tools\create_app_icon.py --output .\assets\qtfm.ico
if ($LASTEXITCODE -ne 0) {
    throw "Icon generation failed."
}

python .\tools\generate_sample_asd.py `
    --input .\qtfm_recording_20260730_190043_gradiometer.csv `
    --output .\examples\sample_asd.png
if ($LASTEXITCODE -ne 0) {
    throw "ASD example generation failed."
}

$PyInstallerDist = Join-Path -Path $BuildRoot -ChildPath "pyinstaller-dist"
$PyInstallerWork = Join-Path -Path $BuildRoot -ChildPath "pyinstaller-work"
python -m PyInstaller `
    --noconfirm `
    --clean `
    --distpath $PyInstallerDist `
    --workpath $PyInstallerWork `
    .\packaging\qtfm.spec
if ($LASTEXITCODE -ne 0) {
    throw "PyInstaller build failed."
}

$BuiltApp = Join-Path -Path $PyInstallerDist -ChildPath (
    "QTFM Simple Gradiometer"
)
$AppStage = Join-Path -Path $StageRoot -ChildPath "App"
Copy-Item -LiteralPath $BuiltApp -Destination $AppStage -Recurse
# The app saves qtfm_settings.json beside its exe (a smoke-test run creates
# one). Never ship it: on upgrade the installer would overwrite the user's
# saved settings.
$StagedSettings = Join-Path -Path $AppStage -ChildPath "qtfm_settings.json"
if (Test-Path -LiteralPath $StagedSettings) {
    Remove-Item -LiteralPath $StagedSettings -Force
}

$SourceStage = Join-Path -Path $StageRoot -ChildPath "Source"
New-Item -ItemType Directory -Path $SourceStage | Out-Null
# Every top-level Python module ships automatically so a newly added module
# cannot be silently left out of the GPL source distribution.
Get-ChildItem -LiteralPath $ProjectRoot -File -Filter "*.py" |
    ForEach-Object {
        Copy-Item -LiteralPath $_.FullName -Destination $SourceStage
    }
$SourceFiles = @(
    ".gitignore",
    "GRADIOMETER_GUIDE.md",
    "LICENSE",
    "OPEN_SOURCE_NOTICE.md",
    "README.md",
    "requirements.txt",
    "run_app.bat",
    "THIRD_PARTY_NOTICES.md"
)
foreach ($SourceFile in $SourceFiles) {
    Copy-Item -LiteralPath (Join-Path $ProjectRoot $SourceFile) `
        -Destination $SourceStage
}
foreach ($SourceDirectory in @("assets", "packaging", "tests", "tools")) {
    Copy-Item -LiteralPath (Join-Path $ProjectRoot $SourceDirectory) `
        -Destination $SourceStage -Recurse
}
$SourcePrefix = [System.IO.Path]::GetFullPath($SourceStage).TrimEnd(
    [System.IO.Path]::DirectorySeparatorChar
) + [System.IO.Path]::DirectorySeparatorChar
$CacheDirectories = Get-ChildItem -LiteralPath $SourceStage `
    -Directory -Filter "__pycache__" -Recurse
foreach ($CacheDirectory in $CacheDirectories) {
    $CachePath = [System.IO.Path]::GetFullPath($CacheDirectory.FullName)
    if (-not $CachePath.StartsWith(
        $SourcePrefix,
        [System.StringComparison]::OrdinalIgnoreCase
    )) {
        throw "Refusing to remove an unexpected cache path: $CachePath"
    }
    Remove-Item -LiteralPath $CachePath -Recurse -Force
}

$SampleStage = Join-Path -Path $StageRoot -ChildPath "Sample Data"
New-Item -ItemType Directory -Path $SampleStage | Out-Null
Copy-Item `
    -LiteralPath (
        Join-Path $ProjectRoot "qtfm_recording_20260730_190043_gradiometer.csv"
    ) `
    -Destination $SampleStage
Copy-Item `
    -LiteralPath (Join-Path $ProjectRoot "examples\sample_asd.png") `
    -Destination $SampleStage

foreach ($Document in @(
    "README.md",
    "GRADIOMETER_GUIDE.md",
    "LICENSE",
    "OPEN_SOURCE_NOTICE.md",
    "THIRD_PARTY_NOTICES.md"
)) {
    Copy-Item -LiteralPath (Join-Path $ProjectRoot $Document) `
        -Destination $StageRoot
}

$BuiltExe = Join-Path -Path $AppStage -ChildPath (
    "QTFM Simple Gradiometer.exe"
)
& $BuiltExe --smoke-test
if ($LASTEXITCODE -ne 0) {
    throw "Packaged application smoke test failed."
}

$SafeZipPath = Assert-ProjectPath -Path $ZipPath
if (Test-Path -LiteralPath $SafeZipPath) {
    Remove-Item -LiteralPath $SafeZipPath -Force
}
Compress-Archive -LiteralPath $StageRoot -DestinationPath $SafeZipPath

if (-not $InnoCompiler) {
    # Inno Setup installs per-machine or per-user, and offers both 6 and 7.
    $Candidates = @(
        (Join-Path $ProjectRoot "build_tools\Inno Setup 7\ISCC.exe"),
        (Join-Path $ProjectRoot "build_tools\Inno Setup 6\ISCC.exe"),
        "$env:LOCALAPPDATA\Programs\Inno Setup 7\ISCC.exe",
        "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe",
        "C:\Program Files\Inno Setup 7\ISCC.exe",
        "C:\Program Files\Inno Setup 6\ISCC.exe",
        "C:\Program Files (x86)\Inno Setup 7\ISCC.exe",
        "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
    )
    $InnoCompiler = $Candidates |
        Where-Object { Test-Path -LiteralPath $_ } |
        Select-Object -First 1
}
if (-not $InnoCompiler) {
    throw (
        "The app and ZIP were built, but Inno Setup was not found. " +
        "Pass -InnoCompiler with the full path to ISCC.exe."
    )
}

& $InnoCompiler "/DMyAppVersion=$Version" .\packaging\installer.iss
if ($LASTEXITCODE -ne 0) {
    throw "Installer compilation failed."
}

Write-Host ""
Write-Host "Distribution complete:"
Write-Host "  Version:   $Version"
Write-Host "  Folder:    $StageRoot"
Write-Host "  ZIP:       $ZipPath"
Write-Host (
    "  Installer: " +
    (Join-Path $ReleaseRoot "QTFM_Simple_Gradiometer_Setup_$Version.exe")
)
