[CmdletBinding()]
param(
    [ValidateSet('Release','Debug')]
    [string]$Config = 'Release',
    [string]$ExeName = 'bio.exe',
    [switch]$Tests,
    [switch]$Clean
)

$ErrorActionPreference = 'Stop'
$root = $PSScriptRoot

$zigCandidates = @(
    (Join-Path $root '..\tools\zig\zig.exe'),
    (Join-Path $root '..\..\trinary\tools\zig\zig.exe'),
    'zig'
)

$zig = $null
foreach ($candidate in $zigCandidates) {
    try {
        if ($candidate -eq 'zig') {
            & $candidate version *> $null
            if ($LASTEXITCODE -eq 0) { $zig = $candidate; break }
        } elseif (Test-Path $candidate) {
            $zig = $candidate
            break
        }
    } catch {
        continue
    }
}

if (-not $zig) {
    throw "zig not found. Install zig or place it in tools\zig\zig.exe"
}

$build = Join-Path $root 'build'
$obj = Join-Path $build 'obj'
$lib = Join-Path $build 'lib'
$bin = Join-Path $build 'bin'

if ($Clean -and (Test-Path $build)) {
    Remove-Item $build -Recurse -Force
}
foreach ($d in @($obj, $lib, $bin)) {
    New-Item -ItemType Directory -Path $d -Force | Out-Null
}

$target = 'x86_64-windows-gnu'
$include = "-I$root\include"
$warn = @('-Wall','-Wextra','-Wpedantic')
$arch = @('-mavx2','-mpopcnt','-mfma')
$opt = if ($Config -eq 'Release') { @('-O3','-DNDEBUG') + $arch } else { @('-O0','-g','-DDEBUG') + $arch }
$std = '-std=c11'

$sources = @(
    'src\bio_opcode.c',
    'src\bio_dispatch.c',
    'src\bio_handlers.c',
    'src\bio_lexer.c',
    'src\bio_context.c',
    'src\bio_gguf.c',
    'src\bio_cpuid.c',
    'src\bio_kernels.c',
    'src\bio_sparse.c',
    'src\bio_quant.c',
    'src\bio_forward.c',
    'src\bio_bdl.c',
    'src\bio_state.c',
    'src\bio_product.c',
    'src\bio_capi.c'
)

function Invoke-Cc {
    param([string[]]$CcArgs)
    & $script:zig cc -target $script:target @CcArgs
    if ($LASTEXITCODE -ne 0) {
        throw "zig cc failed: $($CcArgs -join ' ')"
    }
}

$objs = @()
Write-Host "[build] compiling Bio-Digital native sources ..." -ForegroundColor Cyan
foreach ($s in $sources) {
    $src = Join-Path $root $s
    $dst = Join-Path $obj ([IO.Path]::ChangeExtension([IO.Path]::GetFileName($s),'.obj'))
    Invoke-Cc -CcArgs (@('-c', $src, '-o', $dst, $std, $include) + $opt + $warn)
    $objs += $dst
}

$libPath = Join-Path $lib 'libbio.a'
if (Test-Path $libPath) { Remove-Item $libPath -Force }
& $zig ar rcs $libPath @objs
if ($LASTEXITCODE -ne 0) { throw "zig ar failed" }

$exePath = Join-Path $bin $ExeName
$mainSrc = Join-Path $root 'src\bio_main.c'
Write-Host "[build] linking $ExeName ..." -ForegroundColor Cyan
Invoke-Cc -CcArgs (@($mainSrc, '-o', $exePath, $libPath, $std, $include) + $opt + $warn)

Write-Host "[build] OK" -ForegroundColor Green
Write-Host "  exe: $exePath" -ForegroundColor DarkGray
Write-Host "  lib: $libPath" -ForegroundColor DarkGray

if ($Tests) {
    New-Item -ItemType Directory -Path (Join-Path $root 'tests\fixtures') -Force | Out-Null

    $testSrc = Join-Path $root 'tests\test_lexer.c'
    $testExe = Join-Path $bin 'bio-tests.exe'
    Write-Host "[test] compiling bio-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($testSrc, '-o', $testExe, $libPath, $std, $include) + $opt + $warn)
    & $testExe
    if ($LASTEXITCODE -ne 0) { throw "tests failed" }

    $modelTestSrc = Join-Path $root 'tests\test_model.c'
    $modelTestExe = Join-Path $bin 'bio-model-tests.exe'
    Write-Host "[test] compiling bio-model-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($modelTestSrc, '-o', $modelTestExe, $libPath, $std, $include) + $opt + $warn)
    & $modelTestExe
    if ($LASTEXITCODE -ne 0) { throw "model tests failed" }

    $ggufTestSrc = Join-Path $root 'tests\test_gguf.c'
    $ggufTestExe = Join-Path $bin 'bio-gguf-tests.exe'
    $ggufFixture = Join-Path $root 'tests\fixtures\minimal-q4.gguf'
    Write-Host "[test] compiling bio-gguf-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($ggufTestSrc, '-o', $ggufTestExe, $libPath, $std, $include) + $opt + $warn)
    & $ggufTestExe $ggufFixture
    if ($LASTEXITCODE -ne 0) { throw "GGUF tests failed" }

    $sparseTestSrc = Join-Path $root 'tests\test_sparse.c'
    $sparseTestExe = Join-Path $bin 'bio-sparse-tests.exe'
    Write-Host "[test] compiling bio-sparse-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($sparseTestSrc, '-o', $sparseTestExe, $libPath, $std, $include) + $opt + $warn)
    & $sparseTestExe
    if ($LASTEXITCODE -ne 0) { throw "sparse tests failed" }

    $quantTestSrc = Join-Path $root 'tests\test_quant.c'
    $quantTestExe = Join-Path $bin 'bio-quant-tests.exe'
    Write-Host "[test] compiling bio-quant-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($quantTestSrc, '-o', $quantTestExe, $libPath, $std, $include) + $opt + $warn)
    & $quantTestExe
    if ($LASTEXITCODE -ne 0) { throw "quant tests failed" }

    $forwardTestSrc = Join-Path $root 'tests\test_forward.c'
    $forwardTestExe = Join-Path $bin 'bio-forward-tests.exe'
    Write-Host "[test] compiling bio-forward-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($forwardTestSrc, '-o', $forwardTestExe, $libPath, $std, $include) + $opt + $warn)
    & $forwardTestExe
    if ($LASTEXITCODE -ne 0) { throw "forward tests failed" }

    $bdlTestSrc = Join-Path $root 'tests\test_bdl.c'
    $bdlTestExe = Join-Path $bin 'bio-bdl-tests.exe'
    $bdlFixture = Join-Path $root 'tests\fixtures\test.bdl'
    Write-Host "[test] compiling bio-bdl-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($bdlTestSrc, '-o', $bdlTestExe, $libPath, $std, $include) + $opt + $warn)
    & $bdlTestExe $bdlFixture
    if ($LASTEXITCODE -ne 0) { throw "bdl tests failed" }

    $stateTestSrc = Join-Path $root 'tests\test_state.c'
    $stateTestExe = Join-Path $bin 'bio-state-tests.exe'
    $stateFixture = Join-Path $root 'tests\fixtures\minimal.bdst'
    $stateDeltaFixture = Join-Path $root 'tests\fixtures\minimal-state.bdl'
    Write-Host "[test] compiling bio-state-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($stateTestSrc, '-o', $stateTestExe, $libPath, $std, $include) + $opt + $warn)
    & $stateTestExe $ggufFixture $stateFixture $stateDeltaFixture
    if ($LASTEXITCODE -ne 0) { throw "state tests failed" }

    Write-Host "[test] product CLI ingest/learn/chat ..." -ForegroundColor Cyan
    & $exePath ingest $ggufFixture --out $stateFixture --delta $stateDeltaFixture
    if ($LASTEXITCODE -ne 0) { throw "product ingest failed" }
    & $exePath learn $stateFixture --seconds 1 --checkpoint-sec 1 --profile nrl-edge --target-steps 1000000
    if ($LASTEXITCODE -ne 0) { throw "product learn failed" }
    & $exePath chat $stateFixture --prompt hi --max-tokens 8 --idle-learn-after-sec 1
    if ($LASTEXITCODE -ne 0) { throw "product chat failed" }
    "hi`n/stats`n/exit`n" | & $exePath serve $stateFixture --max-tokens 4 --idle-learn-after-sec 1 --checkpoint-sec 1 --profile nrl-edge
    if ($LASTEXITCODE -ne 0) { throw "product serve failed" }

    $phase4TestSrc = Join-Path $root 'tests\test_phase4.c'
    $phase4TestExe = Join-Path $bin 'bio-phase4-tests.exe'
    Write-Host "[test] compiling bio-phase4-tests.exe ..." -ForegroundColor Cyan
    Invoke-Cc -CcArgs (@($phase4TestSrc, '-o', $phase4TestExe, $libPath, $std, $include) + $opt + $warn)
    & $phase4TestExe (Join-Path $root 'examples\phase1_all_opcodes.bio')
    if ($LASTEXITCODE -ne 0) { throw "phase4 tests failed" }

    Write-Host "[test] running sample opcode stream ..." -ForegroundColor Cyan
    & $exePath run (Join-Path $root 'examples\phase1_all_opcodes.bio')
    if ($LASTEXITCODE -ne 0) { throw "sample .bio failed" }
}
