$ErrorActionPreference = 'Stop'

Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem

$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
$projectDir = Join-Path $repoRoot 'ComputerShopWpf'
$binDir = Join-Path $projectDir 'bin\Debug'
$databaseDir = Join-Path $projectDir 'Database'
$buildDir = Join-Path $repoRoot 'Installer\build'
$packageDir = Join-Path $buildDir 'package'
$payloadZip = Join-Path $buildDir 'payload.zip'
$stubExe = Join-Path $buildDir 'ComputerShopWpf_Setup.stub.exe'
$setupPath = Join-Path (Resolve-Path 'outputs') 'ComputerShopWpf_Setup.exe'
$sourcePath = Join-Path $PSScriptRoot 'SetupStub.cs'
$cscPath = Join-Path $env:WINDIR 'Microsoft.NET\Framework\v4.0.30319\csc.exe'
$marker = [System.Text.Encoding]::ASCII.GetBytes('CSWPF_SETUP_PAYLOAD_V1')

$resolvedRepo = [System.IO.Path]::GetFullPath($repoRoot.Path)
$resolvedBuild = [System.IO.Path]::GetFullPath($buildDir)
if (-not $resolvedBuild.StartsWith($resolvedRepo, [System.StringComparison]::OrdinalIgnoreCase)) {
    throw "Refusing to clean outside repository: $resolvedBuild"
}

if (-not (Test-Path -LiteralPath $cscPath)) {
    throw "C# compiler was not found at $cscPath"
}

if (-not (Test-Path -LiteralPath (Join-Path $binDir 'ComputerShopWpf.exe'))) {
    throw "Build output was not found. Rebuild the solution first: $binDir"
}

if (Test-Path -LiteralPath $buildDir) {
    Remove-Item -LiteralPath $buildDir -Recurse -Force
}

New-Item -ItemType Directory -Path $packageDir | Out-Null
New-Item -ItemType Directory -Path (Join-Path $packageDir 'Database') | Out-Null

Get-ChildItem -LiteralPath $binDir -File |
    Where-Object { $_.Extension -in @('.exe', '.config', '.dll') } |
    Copy-Item -Destination $packageDir -Force

Copy-Item -LiteralPath (Join-Path $repoRoot 'README.md') -Destination $packageDir -Force
Copy-Item -LiteralPath (Join-Path $databaseDir 'create_database.sql') -Destination (Join-Path $packageDir 'Database') -Force
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'uninstall.ps1') -Destination $packageDir -Force

if (Test-Path -LiteralPath $payloadZip) {
    Remove-Item -LiteralPath $payloadZip -Force
}

$zip = [System.IO.Compression.ZipFile]::Open($payloadZip, [System.IO.Compression.ZipArchiveMode]::Create)
try {
    Get-ChildItem -LiteralPath $packageDir -Recurse -File | ForEach-Object {
        $relative = $_.FullName.Substring($packageDir.Length + 1).Replace('\', '/')
        [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
            $zip,
            $_.FullName,
            $relative,
            [System.IO.Compression.CompressionLevel]::Optimal) | Out-Null
    }
}
finally {
    $zip.Dispose()
}

& $cscPath `
    /nologo `
    /target:winexe `
    /platform:anycpu `
    /out:$stubExe `
    /reference:System.Windows.Forms.dll `
    /reference:System.Drawing.dll `
    /reference:System.IO.Compression.dll `
    /reference:System.IO.Compression.FileSystem.dll `
    $sourcePath

if ($LASTEXITCODE -ne 0) {
    throw "C# installer stub compilation failed with exit code $LASTEXITCODE"
}

$stubBytes = [System.IO.File]::ReadAllBytes($stubExe)
$payloadBytes = [System.IO.File]::ReadAllBytes($payloadZip)
$lengthBytes = [System.BitConverter]::GetBytes([Int64]$payloadBytes.Length)

if (Test-Path -LiteralPath $setupPath) {
    Remove-Item -LiteralPath $setupPath -Force
}

$out = [System.IO.File]::Open($setupPath, [System.IO.FileMode]::CreateNew, [System.IO.FileAccess]::Write)
try {
    $out.Write($stubBytes, 0, $stubBytes.Length)
    $out.Write($payloadBytes, 0, $payloadBytes.Length)
    $out.Write($lengthBytes, 0, $lengthBytes.Length)
    $out.Write($marker, 0, $marker.Length)
}
finally {
    $out.Dispose()
}

Get-Item -LiteralPath $setupPath | Select-Object FullName, Length, LastWriteTime

