# llmtrim — Windows installer (PowerShell 5.1+)
#
#   irm https://raw.githubusercontent.com/fkiene/llmtrim/main/install.ps1 | iex
#
# Downloads the prebuilt binary for the latest release and wires the interceptor with
# `llmtrim setup` (CA into the Windows trust flow, HTTPS_PROXY/NODE_EXTRA_CA_CERTS into
# your PowerShell profile, autostart, and the background daemon). Falls back to a
# from-source `cargo install` when no prebuilt binary exists for your architecture
# (e.g. ARM64). WSL users: use install.sh instead.
#
# Override:
#   $env:LLMTRIM_VERSION  = "v0.1.0"   pin a specific release
#   $env:LLMTRIM_NO_SETUP = "1"        install the binary only, skip setup

$ErrorActionPreference = "Stop"
$Repo = "fkiene/llmtrim"
$BinDir = Join-Path $env:LOCALAPPDATA "llmtrim\bin"

function Info($m) { Write-Host "[INFO] $m" -ForegroundColor Green }
function Warn($m) { Write-Host "[WARN] $m" -ForegroundColor Yellow }

# Map the OS architecture to a release target triple. PROCESSOR_ARCHITEW6432 holds the
# real machine arch when a 32-bit PowerShell runs under WOW64; prefer it when set.
# Unknown arches (e.g. 32-bit x86) return $null and take the cargo fallback.
function Get-Target {
    $arch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE }
    switch ($arch) {
        "AMD64" { return "x86_64-pc-windows-msvc" }
        "ARM64" { return "aarch64-pc-windows-msvc" }
        default { return $null }
    }
}

# Latest version via the releases/latest redirect, with the GitHub API as a fallback.
# Reads the resolved URI cross-edition (PS 5.1: ResponseUri; PS 7+: RequestMessage).
function Get-LatestVersion {
    try {
        $resp = Invoke-WebRequest "https://github.com/$Repo/releases/latest" -UseBasicParsing
        $uri = if ($resp.BaseResponse.ResponseUri) { $resp.BaseResponse.ResponseUri.AbsoluteUri }
               elseif ($resp.BaseResponse.RequestMessage) { $resp.BaseResponse.RequestMessage.RequestUri.AbsoluteUri }
               else { "" }
        if ($uri -match '/tag/([^/]+)$') { return $Matches[1] }
    } catch { }
    Warn "Redirect lookup failed, falling back to GitHub API..."
    return (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name
}

# Build from source when no prebuilt binary matches the architecture.
function Install-FromSource {
    if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
        Write-Error "No prebuilt binary for this architecture and cargo not found. Install Rust from https://rustup.rs, reopen PowerShell, then re-run."
        exit 1
    }
    Info "Building from source with cargo..."
    cargo install --git "https://github.com/$Repo"
    if ($LASTEXITCODE -ne 0) { Write-Error "cargo install failed."; exit 1 }
    return (Join-Path $env:USERPROFILE ".cargo\bin\llmtrim.exe")
}

# Download + extract the prebuilt zip, placing llmtrim.exe in $BinDir on the user PATH.
# Returns $null (so the caller falls back to source) when no asset exists for the tag.
function Install-Prebuilt($target) {
    $version = if ($env:LLMTRIM_VERSION) { $env:LLMTRIM_VERSION } else { Get-LatestVersion }
    if (-not $version) { Write-Error "Failed to resolve latest version (set LLMTRIM_VERSION=vX.Y.Z to pin)."; exit 1 }

    $asset = "llmtrim-$target.zip"
    $url = "https://github.com/$Repo/releases/download/$version/$asset"
    Info "Detected: windows $target, version $version"
    Info "Downloading $url"

    $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("llmtrim-" + [System.IO.Path]::GetRandomFileName())
    New-Item -ItemType Directory -Path $tmp -Force | Out-Null
    $zip = Join-Path $tmp $asset
    try {
        Invoke-WebRequest $url -OutFile $zip -UseBasicParsing
    } catch {
        Warn "No prebuilt asset at $url"
        Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
        return $null
    }

    # Verify SHA-256 checksum against the .sha256 sidecar uploaded by CI.
    $sha256Url = ($url -replace '\.zip$', '.sha256')
    try {
        # GitHub serves the sidecar as octet-stream, making .Content a byte[] — download
        # to a file instead of touching .Content.
        $shaFile = "$zip.sha256"
        Invoke-WebRequest $sha256Url -OutFile $shaFile -UseBasicParsing
        $expectedLine = (Get-Content $shaFile -Raw).Trim()
        $expectedHash = ($expectedLine -split '\s+')[0].ToUpper()
        $actualHash = (Get-FileHash $zip -Algorithm SHA256).Hash.ToUpper()
        if ($actualHash -ne $expectedHash) {
            Write-Error "Checksum mismatch! Expected $expectedHash, got $actualHash. Aborting."
            Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
            exit 1
        }
        Info "Checksum verified."
    } catch {
        Write-Error "Failed to fetch or verify checksum from ${sha256Url}: $_"
        Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
        exit 1
    }

    # Reject absolute or parent-traversal paths before extracting (CWE-22).
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $archive = [System.IO.Compression.ZipFile]::OpenRead($zip)
    try {
        foreach ($entry in $archive.Entries) {
            if ($entry.FullName -match '(^|[\\/])\.\.([\\/]|$)' -or [System.IO.Path]::IsPathRooted($entry.FullName)) {
                Write-Error "Archive contains unsafe paths — refusing to extract."
                exit 1
            }
        }
    } finally {
        $archive.Dispose()
    }

    Expand-Archive -Path $zip -DestinationPath $tmp -Force
    $exe = Get-ChildItem -Path $tmp -Recurse -Filter "llmtrim.exe" | Select-Object -First 1
    if (-not $exe) { Write-Error "llmtrim.exe not found in archive."; exit 1 }

    New-Item -ItemType Directory -Path $BinDir -Force | Out-Null
    Copy-Item $exe.FullName (Join-Path $BinDir "llmtrim.exe") -Force
    Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
    Info "Installed llmtrim to $BinDir\llmtrim.exe"

    # Persist $BinDir on the user PATH (and this session) if it isn't already there.
    $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
    if ($userPath -notlike "*$BinDir*") {
        [Environment]::SetEnvironmentVariable("Path", "$userPath;$BinDir", "User")
        $env:PATH = "$env:PATH;$BinDir"
        Info "Added $BinDir to your user PATH."
    }
    return (Join-Path $BinDir "llmtrim.exe")
}

Write-Host "Installing llmtrim..."

$target = Get-Target
$llmtrim = $null
if ($target) { $llmtrim = Install-Prebuilt $target }
if (-not $llmtrim) { $llmtrim = Install-FromSource }

if ($env:LLMTRIM_NO_SETUP -eq "1") {
    Write-Host ""
    Write-Host "Binary installed. Skipped setup (LLMTRIM_NO_SETUP=1)."
    Write-Host "Wire it later with:  llmtrim setup"
    exit 0
}

Write-Host "Running setup (CA + HTTPS_PROXY in your PowerShell profile + autostart + start)..."
& $llmtrim setup

Write-Host ""
Write-Host "Done. Open a new PowerShell window so the profile env applies."
Write-Host "Watch savings:  llmtrim status"
Write-Host "Back out:       llmtrim uninstall"
