param(
  [string] $Version = "",
  [switch] $Service,
  [switch] $Help
)

$ErrorActionPreference = "Stop"
$DefaultDownloadBase = 'https://relay.cre8.xyz/releases'
$DefaultVersion = 'latest'

function Show-Usage {
  @"
Install the CRE8 runtime peer bundle.

Usage:
  irm https://cre8.xyz/install.ps1 | iex
  & ([scriptblock]::Create((irm https://cre8.xyz/install.ps1))) stable
  & ([scriptblock]::Create((irm https://cre8.xyz/install.ps1))) -Version stable

Environment:
  CRE8_DOWNLOAD_BASE   Override bundle base URL.
  CRE8_VERSION         Override default version/channel.
  CRE8_INSTALL_DIR     Install root. Default: `$HOME\.local\share\cre8
  CRE8_BIN_DIR         Shim directory. Default: `$HOME\.local\bin
  CRE8_SKIP_CHECKSUM   Set to 1 to allow unsigned local test bundles.
"@
}

if ($Help) {
  Show-Usage
  return
}

if ([string]::IsNullOrWhiteSpace($Version)) {
  $Version = if ([string]::IsNullOrWhiteSpace($env:CRE8_VERSION)) { $DefaultVersion } else { $env:CRE8_VERSION }
}

$DownloadBase = if ([string]::IsNullOrWhiteSpace($env:CRE8_DOWNLOAD_BASE)) {
  $DefaultDownloadBase
} else {
  $env:CRE8_DOWNLOAD_BASE.TrimEnd("/")
}
$InstallDir = if ([string]::IsNullOrWhiteSpace($env:CRE8_INSTALL_DIR)) {
  Join-Path $HOME ".local\share\cre8"
} else {
  $env:CRE8_INSTALL_DIR
}
$BinDir = if ([string]::IsNullOrWhiteSpace($env:CRE8_BIN_DIR)) {
  Join-Path $HOME ".local\bin"
} else {
  $env:CRE8_BIN_DIR
}

function Get-Cre8Os {
  try {
    $runtime = [System.Runtime.InteropServices.RuntimeInformation]
    $platform = [System.Runtime.InteropServices.OSPlatform]
    if ($runtime::IsOSPlatform($platform::Windows)) { return "windows" }
    if ($runtime::IsOSPlatform($platform::Linux)) { return "linux" }
    if ($runtime::IsOSPlatform($platform::OSX)) { return "darwin" }
  } catch {
    if ($env:OS -eq "Windows_NT") { return "windows" }
  }
  throw "cre8 install: unsupported operating system"
}

function Get-Cre8Arch {
  try {
    $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()
  } catch {
    $arch = "$env:PROCESSOR_ARCHITECTURE".ToLowerInvariant()
  }
  switch -Regex ($arch) {
    "^(x64|amd64)$" { return "x64" }
    "^(arm64|aarch64)$" { return "arm64" }
    default { throw "cre8 install: unsupported architecture $arch" }
  }
}

function Invoke-Cre8Download([string] $Uri, [string] $OutFile) {
  Invoke-WebRequest -UseBasicParsing -Uri $Uri -OutFile $OutFile
}

function Test-Cre8Checksum([string] $Archive, [string] $Artifact, [string] $SumsPath) {
  if ($env:CRE8_SKIP_CHECKSUM -eq "1") {
    Write-Warning "cre8 install: skipping checksum because CRE8_SKIP_CHECKSUM=1"
    return
  }

  $escapedArtifact = [regex]::Escape("  $Artifact")
  $entry = Get-Content -Path $SumsPath | Where-Object { $_ -match "$escapedArtifact$" } | Select-Object -First 1
  if ([string]::IsNullOrWhiteSpace($entry)) {
    throw "cre8 install: checksum entry missing for $Artifact"
  }

  $expected = ($entry -split "\s+")[0].ToLowerInvariant()
  $actual = (Get-FileHash -Path $Archive -Algorithm SHA256).Hash.ToLowerInvariant()
  if ($actual -ne $expected) {
    throw "cre8 install: checksum mismatch for $Artifact (expected $expected, got $actual)"
  }
}

function Expand-Cre8Archive([string] $Archive, [string] $Extension, [string] $Destination) {
  $extractDir = Join-Path ([System.IO.Path]::GetDirectoryName($Archive)) "extract"
  if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir }
  New-Item -ItemType Directory -Force -Path $extractDir | Out-Null

  if ($Extension -eq "zip") {
    Expand-Archive -Path $Archive -DestinationPath $extractDir -Force
  } else {
    tar -xzf $Archive -C $extractDir
  }

  $sourceDir = $extractDir
  $directBins = @(
    (Join-Path $sourceDir "bin\cre8runtime.exe"),
    (Join-Path $sourceDir "bin\cre8_runtime.exe"),
    (Join-Path $sourceDir "bin\cre8runtime"),
    (Join-Path $sourceDir "bin\cre8_runtime")
  )
  if (-not ($directBins | Where-Object { Test-Path $_ } | Select-Object -First 1)) {
    $child = Get-ChildItem -Path $extractDir -Directory | Where-Object {
      (Test-Path (Join-Path $_.FullName "bin\cre8runtime.exe")) -or
      (Test-Path (Join-Path $_.FullName "bin\cre8_runtime.exe")) -or
      (Test-Path (Join-Path $_.FullName "bin\cre8runtime")) -or
      (Test-Path (Join-Path $_.FullName "bin\cre8_runtime"))
    } | Select-Object -First 1
    if ($null -ne $child) { $sourceDir = $child.FullName }
  }

  if (Test-Path $Destination) { Remove-Item -Recurse -Force $Destination }
  New-Item -ItemType Directory -Force -Path $Destination | Out-Null
  Copy-Item -Path (Join-Path $sourceDir "*") -Destination $Destination -Recurse -Force
}

function Install-Cre8Shim([string] $TargetBin, [string] $BinDir) {
  New-Item -ItemType Directory -Force -Path $BinDir | Out-Null
  $shimName = if ($TargetBin.EndsWith(".exe", [System.StringComparison]::OrdinalIgnoreCase)) {
    "cre8runtime.exe"
  } else {
    "cre8runtime"
  }
  $shimPath = Join-Path $BinDir $shimName
  if (Test-Path $shimPath) { Remove-Item -Force $shimPath }

  try {
    New-Item -ItemType SymbolicLink -Path $shimPath -Target $TargetBin -Force | Out-Null
  } catch {
    Copy-Item -Path $TargetBin -Destination $shimPath -Force
  }
  return $shimPath
}

$Platform = "$(Get-Cre8Os)-$(Get-Cre8Arch)"
$Extension = if ($Platform.StartsWith("windows-")) { "zip" } else { "tar.gz" }
$Artifact = "cre8-runtime-peer-$Version-$Platform.$Extension"
$ArchiveUrl = "$DownloadBase/$Version/$Artifact"
$SumsUrl = "$DownloadBase/$Version/SHA256SUMS"
$TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("cre8-install-" + [System.Guid]::NewGuid().ToString("N"))
$PeerDir = Join-Path $InstallDir "peer"

New-Item -ItemType Directory -Force -Path $TempDir | Out-Null
try {
  $Archive = Join-Path $TempDir $Artifact
  $Sums = Join-Path $TempDir "SHA256SUMS"

  Write-Host "cre8 install: downloading $Artifact"
  Invoke-Cre8Download -Uri $ArchiveUrl -OutFile $Archive
  Invoke-Cre8Download -Uri $SumsUrl -OutFile $Sums
  Test-Cre8Checksum -Archive $Archive -Artifact $Artifact -SumsPath $Sums

  Expand-Cre8Archive -Archive $Archive -Extension $Extension -Destination $PeerDir

  $candidates = @(
    (Join-Path $PeerDir "bin\cre8runtime.exe"),
    (Join-Path $PeerDir "bin\cre8_runtime.exe"),
    (Join-Path $PeerDir "bin\cre8runtime"),
    (Join-Path $PeerDir "bin\cre8_runtime"),
    (Join-Path $PeerDir "cre8runtime.exe"),
    (Join-Path $PeerDir "cre8runtime")
  )
  $TargetBin = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
  if ([string]::IsNullOrWhiteSpace($TargetBin)) {
    throw "cre8 install: bundle did not contain bin/cre8runtime"
  }

  $ShimPath = Install-Cre8Shim -TargetBin $TargetBin -BinDir $BinDir

  if ($Service) {
    Write-Warning "cre8 install: -Service is not wired for the PowerShell installer yet; run `$ShimPath manually for now."
  }

  Write-Host "cre8 install: installed $TargetBin"
  Write-Host "cre8 install: linked $ShimPath"
  Write-Host "cre8 install: start with: `$env:CRE8_GATEWAY_AUTOSTART='true'; & '$ShimPath'"
} finally {
  if (Test-Path $TempDir) { Remove-Item -Recurse -Force $TempDir }
}
