param(
  [switch]$Install,
  [switch]$SkipVisualStudioBuildTools,
  [switch]$SkipMsys2Packages,
  [switch]$SkipCodexInstall,
  [string]$Msys2Root = 'C:\msys64'
)

$ErrorActionPreference = 'Stop'

function Write-Section([string]$Message) {
  Write-Host ''
  Write-Host "== $Message =="
}

function Test-CommandExists([string]$Name) {
  return [bool](Get-Command $Name -ErrorAction SilentlyContinue)
}

function Test-IsAdmin {
  $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
  $principal = [Security.Principal.WindowsPrincipal]::new($identity)
  return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Invoke-External([string]$Name, [string[]]$Args) {
  Write-Host "> $Name $($Args -join ' ')"
  & $Name @Args
  if ($LASTEXITCODE -ne 0) {
    throw "$Name failed with exit code $LASTEXITCODE"
  }
}

function Get-ToolVersion([string]$Name, [string[]]$Args = @('--version')) {
  if (!(Test-CommandExists $Name)) {
    return 'missing'
  }
  try {
    $value = (& $Name @Args 2>&1 | Select-Object -First 1)
    if (!$value) {
      return 'present'
    }
    return "$value"
  } catch {
    return "present, version check failed: $($_.Exception.Message)"
  }
}

function Ensure-Winget {
  if (!(Test-CommandExists winget)) {
    throw 'winget is missing. Install App Installer from Microsoft Store, then rerun this script.'
  }
}

function Ensure-Node {
  Write-Section 'Node.js'
  if (Test-CommandExists node) {
    Write-Host "node = $(Get-ToolVersion node)"
    Write-Host "npm = $(Get-ToolVersion npm)"
    return
  }
  if (!$Install) {
    Write-Host 'node = missing'
    Write-Host 'rerun with -Install to install OpenJS.NodeJS.LTS'
    return
  }
  Ensure-Winget
  Invoke-External winget @('install', '--id', 'OpenJS.NodeJS.LTS', '-e')
}

function Ensure-Codex {
  Write-Section 'Codex CLI'
  if (Test-CommandExists codex) {
    Write-Host "codex = $(Get-ToolVersion codex)"
    return
  }
  if ($SkipCodexInstall) {
    Write-Host 'codex = missing, skipped'
    return
  }
  if (!$Install) {
    Write-Host 'codex = missing'
    Write-Host 'rerun with -Install to install @openai/codex through npm'
    return
  }
  if (!(Test-CommandExists npm)) {
    throw 'npm is missing. Install Node.js first, reopen PowerShell, then rerun this script.'
  }
  Invoke-External npm @('install', '-g', '@openai/codex')
}

function Ensure-Rust {
  Write-Section 'Rust'
  if (!(Test-CommandExists rustup)) {
    if (!$Install) {
      Write-Host 'rustup = missing'
      Write-Host 'rerun with -Install to install Rustlang.Rustup'
      return
    }
    Ensure-Winget
    Invoke-External winget @('install', '--id', 'Rustlang.Rustup', '-e')
  }

  if (Test-CommandExists rustup) {
    if ($Install) {
      Invoke-External rustup @('default', 'stable')
      Invoke-External rustup @('target', 'add', 'x86_64-pc-windows-msvc')
    }
    Write-Host "rustup = $(Get-ToolVersion rustup)"
    Write-Host "rustc = $(Get-ToolVersion rustc)"
    Write-Host "cargo = $(Get-ToolVersion cargo)"
  } else {
    Write-Host 'rustup was installed but is not visible in this PowerShell session.'
    Write-Host 'Close PowerShell, reopen it, and rerun this script.'
  }
}

function Ensure-VisualStudioBuildTools {
  Write-Section 'Visual Studio C++ Build Tools'
  if ($SkipVisualStudioBuildTools) {
    Write-Host 'skipped'
    return
  }

  $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
  if (Test-Path $vswhere) {
    $installPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
    if ($installPath) {
      Write-Host "vc_tools = present"
      Write-Host "visual_studio = $installPath"
      return
    }
  }

  if (!$Install) {
    Write-Host 'vc_tools = missing or not detected'
    Write-Host 'rerun with -Install to install Microsoft.VisualStudio.2022.BuildTools'
    return
  }

  Ensure-Winget
  Invoke-External winget @(
    'install',
    '--id',
    'Microsoft.VisualStudio.2022.BuildTools',
    '-e',
    '--override',
    '--wait --quiet --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended'
  )
}

function Ensure-Msys2 {
  Write-Section 'MSYS2'
  $msys2Bash = Join-Path $Msys2Root 'usr\bin\bash.exe'
  $mingwBin = Join-Path $Msys2Root 'mingw64\bin'
  $windres = Join-Path $mingwBin 'windres.exe'
  $gcc = Join-Path $mingwBin 'gcc.exe'

  if (!(Test-Path $msys2Bash)) {
    if (!$Install) {
      Write-Host "msys2 = missing at $Msys2Root"
      Write-Host 'rerun with -Install to install MSYS2.MSYS2'
      return
    }
    Ensure-Winget
    Invoke-External winget @('install', '--id', 'MSYS2.MSYS2', '-e')
  }

  if (!(Test-Path $msys2Bash)) {
    Write-Host 'MSYS2 was installed but is not visible at the expected path yet.'
    Write-Host 'Close PowerShell, reopen it, and rerun this script.'
    return
  }

  if ($Install -and !$SkipMsys2Packages) {
    Invoke-External $msys2Bash @(
      '-lc',
      'pacman -S --needed --noconfirm mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf make cmake ninja unzip zip'
    )
  }

  if (Test-Path $windres) {
    Write-Host "windres = $(& $windres --version | Select-Object -First 1)"
  } else {
    Write-Host "windres = missing at $windres"
  }

  if (Test-Path $gcc) {
    Write-Host "gcc = $(& $gcc --version | Select-Object -First 1)"
  } else {
    Write-Host "gcc = missing at $gcc"
  }

  Write-Host "mingw_bin = $mingwBin"
}

function Write-Summary {
  Write-Section 'Summary'
  $msys2Bin = Join-Path $Msys2Root 'mingw64\bin'
  $windres = Join-Path $msys2Bin 'windres.exe'
  $gcc = Join-Path $msys2Bin 'gcc.exe'

  Write-Host "admin = $(Test-IsAdmin)"
  Write-Host "node = $(Get-ToolVersion node)"
  Write-Host "npm = $(Get-ToolVersion npm)"
  Write-Host "codex = $(Get-ToolVersion codex)"
  Write-Host "rustup = $(Get-ToolVersion rustup)"
  Write-Host "rustc = $(Get-ToolVersion rustc)"
  Write-Host "cargo = $(Get-ToolVersion cargo)"
  Write-Host "windres_path = $windres"
  Write-Host "windres_exists = $(Test-Path $windres)"
  Write-Host "gcc_path = $gcc"
  Write-Host "gcc_exists = $(Test-Path $gcc)"

  if ((Test-CommandExists node) -and (Test-CommandExists npm) -and
      (Test-CommandExists codex) -and (Test-CommandExists rustc) -and
      (Test-CommandExists cargo) -and (Test-Path $windres) -and (Test-Path $gcc)) {
    Write-Host ''
    Write-Host 'windows_codex_toolchain = ready'
    Write-Host "For this PowerShell session, use:"
    Write-Host "`$env:Path = '$msys2Bin;' + `$env:Path"
  } else {
    Write-Host ''
    Write-Host 'windows_codex_toolchain = incomplete'
    Write-Host 'Rerun from Administrator PowerShell with:'
    Write-Host '.\scripts\setup-windows-codex-toolchain.ps1 -Install'
  }
}

Write-Host 'QNE Windows Codex toolchain setup'
Write-Host "install_mode = $Install"
Write-Host "admin = $(Test-IsAdmin)"

if ($Install -and !(Test-IsAdmin)) {
  Write-Host ''
  Write-Host 'Some installers require Administrator PowerShell.'
  Write-Host 'Right click PowerShell and choose Run as administrator, then rerun with -Install.'
  exit 2
}

Ensure-Node
Ensure-Codex
Ensure-Rust
Ensure-VisualStudioBuildTools
Ensure-Msys2
Write-Summary
