#Requires -Version 5.1
<#
  Shared build helpers for Proxy God / ProxyChainManager.
  Dot-sourced by setup_and_build.ps1 and release_build.ps1 — do not run directly.
#>

Set-StrictMode -Version Latest

function Get-RepoRoot {
    # This file lives in scripts/ — parent is the repository root.
    return (Split-Path -Parent $PSScriptRoot)
}

function Resolve-PythonExe {
    if (Get-Command py -ErrorAction SilentlyContinue) {
        try {
            $out = (& py -3 -c "import sys; print(sys.executable)" 2>$null).Trim()
            if ($out -and (Test-Path -LiteralPath $out)) { return $out }
        } catch { }
    }
    $p = Get-Command python.exe -ErrorAction SilentlyContinue
    if ($p) { return $p.Source }
    throw @(
        "Python 3.10+ not found (tried 'py -3' and 'python'). Install, then re-run:",
        "  winget install Python.Python.3.12 --accept-package-agreements --accept-source-agreements",
        "Or https://www.python.org/downloads/ (enable 'Add python.exe to PATH')."
    ) -join "`n"
}

function Get-GitCommit {
    try {
        $c = (git -C (Get-RepoRoot) rev-parse --short HEAD 2>$null).Trim()
        if ($c) { return $c }
    } catch { }
    return "unknown"
}

function Get-ReleaseVersion {
    param([string]$Override = "")
    if ($Override) { return $Override.Trim() }
    $root = Get-RepoRoot
    try {
        $tag = (git -C $root describe --tags --exact-match 2>$null).Trim()
        if ($tag) { return $tag.TrimStart("v") }
    } catch { }
    try {
        $desc = (git -C $root describe --tags --always --dirty 2>$null).Trim()
        if ($desc) { return ($desc -replace '^v', '') }
    } catch { }
    $py = Resolve-PythonExe
    $v = (& $py -c "from proxy_chain_manager import __version__; print(__version__)" 2>$null).Trim()
    if ($v) { return $v }
    return "0.0.0-dev"
}

function Install-BuildDependencies {
    param([string]$PythonExe, [string]$Root)
    Write-Host "`n== pip (upgrade) =="
    & $PythonExe -m pip install --upgrade pip
    Write-Host "`n== runtime dependencies =="
    & $PythonExe -m pip install -r (Join-Path $Root "requirements.txt")
    Write-Host "`n== build dependencies =="
    & $PythonExe -m pip install -r (Join-Path $Root "dev-requirements.txt")
}

function Invoke-UnitTests {
    param([string]$PythonExe, [string]$Root)
    Write-Host "`n== compileall =="
    & $PythonExe -m compileall -q (Join-Path $Root "proxy_chain_manager")
    if ($LASTEXITCODE -ne 0) { throw "compileall failed (exit $LASTEXITCODE)" }
    Write-Host "`n== unittest discover =="
    Push-Location $Root
    try {
        & $PythonExe -m unittest discover -s tests -v
        if ($LASTEXITCODE -ne 0) { throw "unittest failed (exit $LASTEXITCODE)" }
    } finally {
        Pop-Location
    }
}

function Invoke-StageBundledGost {
    param([string]$Root)
    Write-Host "`n== Stage bundled GOST =="
    $prep = Join-Path $Root "scripts\prepare_bundled_gost.ps1"
    & powershell -NoProfile -ExecutionPolicy Bypass -File $prep
    if ($LASTEXITCODE -ne 0) { throw "prepare_bundled_gost.ps1 failed (exit $LASTEXITCODE)" }
}

function Invoke-GenerateVersionInfo {
    param(
        [string]$PythonExe,
        [string]$Root,
        [string]$Version,
        [string]$Commit = ""
    )
    Write-Host "`n== Version metadata ($Version) =="
    $gen = Join-Path $Root "scripts\generate_version_info.py"
    $out = Join-Path $Root "build\version_info.txt"
    $args = @($gen, "--version", $Version, "--out", $out)
    if ($Commit) { $args += @("--commit", $Commit) }
    & $PythonExe @args
    if ($LASTEXITCODE -ne 0) { throw "generate_version_info.py failed (exit $LASTEXITCODE)" }
}

function Invoke-PyInstallerBuild {
    param([string]$PythonExe, [string]$Root)
    Write-Host "`n== PyInstaller (ProxyChainManager.spec) =="
    $spec = Join-Path $Root "ProxyChainManager.spec"
    if (-not (Test-Path $spec)) { throw "Spec not found: $spec" }
    & $PythonExe -m PyInstaller --noconfirm --clean $spec
    if ($LASTEXITCODE -ne 0) { throw "PyInstaller failed (exit $LASTEXITCODE)" }
    $exe = Join-Path $Root "dist\ProxyChainManager.exe"
    if (-not (Test-Path $exe)) { throw "Build failed: missing $exe" }
    return $exe
}

function Invoke-CodeSign {
    param([string]$ExePath)
    $cert = $env:SIGN_CERT_PATH
    if (-not $cert) {
        Write-Host "  (signing skipped - set SIGN_CERT_PATH to a .pfx to Authenticode-sign)"
        return
    }
    if (-not (Test-Path $cert)) { throw "SIGN_CERT_PATH not found: $cert" }
    $signtool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe"
    if (-not (Test-Path $signtool)) {
        $signtool = (Get-Command signtool.exe -ErrorAction SilentlyContinue).Source
    }
    if (-not $signtool) { throw "signtool.exe not found - install Windows SDK" }
    Write-Host "`n== Authenticode sign =="
    $args = @("sign", "/fd", "SHA256", "/f", $cert, "/tr", "http://timestamp.digicert.com", "/td", "SHA256")
    if ($env:SIGN_CERT_PASSWORD) {
        $args += @("/p", $env:SIGN_CERT_PASSWORD)
    }
    $args += $ExePath
    & $signtool @args
    if ($LASTEXITCODE -ne 0) { throw "signtool sign failed (exit $LASTEXITCODE)" }
}

function Write-ExeHashSidecar {
    param([string]$ExePath)
    $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $ExePath).Hash.ToLower()
    $sidecar = "$ExePath.sha256"
    "$hash *$(Split-Path -Leaf $ExePath)" | Out-File -FilePath $sidecar -Encoding ascii -Force
    Write-Host "SHA256: $hash -> $sidecar"
    return @{ Hash = $hash; Sidecar = $sidecar }
}

function Write-Sbom {
    param(
        [string]$PythonExe,
        [string]$OutDir,
        [string]$Version
    )
    Write-Host "`n== SBOM =="
    $freeze = Join-Path $OutDir "requirements-frozen.txt"
    & $PythonExe -m pip freeze | Out-File -FilePath $freeze -Encoding utf8
    $sbom = Join-Path $OutDir "SBOM.json"
    $pkgs = @()
    Get-Content $freeze | ForEach-Object {
        if ($_ -match '^([^=]+)==(.+)$') {
            $pkgs += @{ name = $Matches[1]; version = $Matches[2] }
        }
    }
    $manifest = @{
        product     = "Proxy God"
        version     = $Version
        generated   = (Get-Date).ToUniversalTime().ToString("o")
        python      = (& $PythonExe -c "import sys; print(sys.version.split()[0])").Trim()
        packages    = $pkgs
    }
    ($manifest | ConvertTo-Json -Depth 4) | Out-File -FilePath $sbom -Encoding utf8
    Write-Host "Wrote $sbom and $freeze"
    return @{ Sbom = $sbom; Freeze = $freeze }
}

function Write-ReleaseManifest {
    param(
        [string]$OutDir,
        [string]$Version,
        [string]$Commit,
        [string]$ExePath,
        [string]$Sha256,
        [hashtable]$Extra = @{}
    )
    $manifest = @{
        product        = "Proxy God"
        executable     = "ProxyChainManager.exe"
        version        = $Version
        git_commit     = $Commit
        built_at_utc   = (Get-Date).ToUniversalTime().ToString("o")
        platform       = "windows-amd64"
        sha256         = $Sha256
        uac_admin      = $true
        bundled_gost   = $true
        pyinstaller    = "6.10.0"
    }
    foreach ($k in $Extra.Keys) { $manifest[$k] = $Extra[$k] }
    $path = Join-Path $OutDir "RELEASE_MANIFEST.json"
    ($manifest | ConvertTo-Json -Depth 4) | Out-File -FilePath $path -Encoding utf8
    Write-Host "Wrote $path"
    return $path
}

function New-ReleaseZip {
    param(
        [string]$ReleaseDir,
        [string]$Version,
        [string]$Root
    )
    $zipName = "ProxyGod-v$Version-windows-amd64.zip"
    $zipPath = Join-Path $Root "releases\$zipName"
    $releasesRoot = Split-Path $zipPath -Parent
    if (-not (Test-Path $releasesRoot)) {
        New-Item -ItemType Directory -Path $releasesRoot | Out-Null
    }
    if (Test-Path $zipPath) { Remove-Item -LiteralPath $zipPath -Force }
    Compress-Archive -Path (Join-Path $ReleaseDir "*") -DestinationPath $zipPath -Force
    Write-Host "Release zip: $zipPath"
    return $zipPath
}
