[CmdletBinding()]
param(
  [string]$Destination = 'C:\NODEDC\dc-amd-connector',
  [string]$NasAddress = '172.22.0.222',
  [string]$BindAddress = '172.22.0.183',
  [int]$Port = 8791,
  [bool]$EnableAutoStart = $true
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Require-Administrator {
  $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
  $principal = New-Object Security.Principal.WindowsPrincipal($identity)
  if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    throw 'Run this installer from an elevated PowerShell window.'
  }
}

function Test-PackageChecksums([string]$Root) {
  $manifest = Join-Path $Root 'SHA256SUMS'
  if (-not (Test-Path -LiteralPath $manifest -PathType Leaf)) {
    throw "Checksum manifest not found: $manifest"
  }

  foreach ($line in Get-Content -LiteralPath $manifest) {
    if ([string]::IsNullOrWhiteSpace($line)) { continue }
    $parts = $line -split '\s{2,}', 2
    if ($parts.Count -ne 2 -or $parts[0] -notmatch '^[a-f0-9]{64}$') {
      throw "Invalid checksum line: $line"
    }
    $relative = $parts[1] -replace '^\./', ''
    $file = Join-Path $Root $relative
    $actual = (Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash.ToLowerInvariant()
    if ($actual -ne $parts[0]) { throw "Checksum mismatch: $relative" }
  }
}

function Get-DockerDesktopStartCommand([string]$DockerCliPath) {
  & $DockerCliPath desktop start --help 2>$null | Out-Null
  if ($LASTEXITCODE -eq 0) {
    return "`"$DockerCliPath`" desktop start --detach"
  }

  $dockerRoot = Split-Path (Split-Path (Split-Path $DockerCliPath -Parent) -Parent) -Parent
  $candidates = @(
    (Join-Path $dockerRoot 'Docker Desktop.exe'),
    (Join-Path $env:ProgramFiles 'Docker\Docker\Docker Desktop.exe'),
    (Join-Path $env:LOCALAPPDATA 'Programs\Docker\Docker\Docker Desktop.exe')
  ) | Select-Object -Unique
  $desktopExe = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
  if (-not $desktopExe) {
    throw 'Unable to find Docker Desktop start command or Docker Desktop.exe for restart recovery.'
  }
  return "start `"`" `"$desktopExe`""
}

function New-DockerDesktopAutoStart([string]$StartCommand) {
  $startup = [Environment]::GetFolderPath([Environment+SpecialFolder]::Startup)
  if ([string]::IsNullOrWhiteSpace($startup)) { throw 'Unable to resolve the current user Startup folder.' }

  $launcher = Join-Path $startup 'NODE.DC DC AMD Connector - Docker Desktop.cmd'
  if (Test-Path -LiteralPath $launcher) {
    throw "Docker Desktop auto-start launcher already exists: $launcher"
  }

  @(
    '@echo off',
    'rem NODE.DC DC AMD Connector - starts Docker Desktop after this user signs in.',
    $StartCommand
  ) | Set-Content -LiteralPath $launcher -Encoding ascii
  return $launcher
}

Require-Administrator
$source = Split-Path -Parent $PSCommandPath
if (-not (Test-Path -LiteralPath (Join-Path $source 'VERSION') -PathType Leaf)) {
  throw 'Run install.ps1 from the DC AMD Connector package folder.'
}
Test-PackageChecksums $source

if (Test-Path -LiteralPath $Destination) {
  throw "Destination already exists; refusing to overwrite: $Destination"
}

$null = [Net.IPAddress]::Parse($NasAddress)
$null = [Net.IPAddress]::Parse($BindAddress)
if ($Port -lt 1 -or $Port -gt 65535) {
  throw "Port must be between 1 and 65535: $Port"
}

$dockerCli = (Get-Command docker -CommandType Application -ErrorAction Stop | Select-Object -First 1).Path
if ([string]::IsNullOrWhiteSpace($dockerCli) -or -not (Test-Path -LiteralPath $dockerCli -PathType Leaf)) {
  throw 'Docker CLI executable could not be resolved.'
}
& $dockerCli version --format '{{.Server.Os}}/{{.Server.Arch}} {{.Server.Version}}' | Out-Null
if ($LASTEXITCODE -ne 0) {
  throw 'Docker Desktop engine is not available. Start it and wait for Engine running.'
}
$dockerDesktopStartCommand = Get-DockerDesktopStartCommand $dockerCli

$destinationCreated = $false
$firewallCreated = $false
$autoStartLauncher = $null
$parent = Split-Path -Parent $Destination
try {
  New-Item -ItemType Directory -Force $parent | Out-Null
  Copy-Item -LiteralPath $source -Destination $Destination -Recurse
  $destinationCreated = $true
  Test-PackageChecksums $Destination
  Set-Location $Destination

  New-Item -ItemType Directory -Force runtime | Out-Null
  $tokenPath = Join-Path $Destination 'runtime\connector-access'
  if (Test-Path -LiteralPath $tokenPath) { throw "Runtime token already exists: $tokenPath" }
  $bytes = New-Object byte[] 48
  $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
  try { $rng.GetBytes($bytes) } finally { $rng.Dispose() }
  $token = [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
  [IO.File]::WriteAllText($tokenPath, $token, [Text.UTF8Encoding]::new($false))

  Copy-Item -LiteralPath (Join-Path $Destination '.env.example') -Destination (Join-Path $Destination '.env')
  @(
    "AMD_CONNECTOR_BIND_IP=$BindAddress",
    "AMD_CONNECTOR_PORT=$Port"
  ) | Set-Content -LiteralPath (Join-Path $Destination '.env') -Encoding ascii

  $ruleName = 'NODE.DC DC AMD Connector (NAS only)'
  if (Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue) {
    throw "Firewall rule already exists; refusing to replace: $ruleName"
  }
  New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol TCP -Profile Any -LocalAddress $BindAddress -LocalPort $Port -RemoteAddress $NasAddress | Out-Null
  $firewallCreated = $true

  if ($EnableAutoStart) {
    $autoStartLauncher = New-DockerDesktopAutoStart $dockerDesktopStartCommand
  }

  & $dockerCli compose up -d --build
  $state = ''
  foreach ($attempt in 1..30) {
    $state = & $dockerCli inspect dc-amd-connector --format '{{.State.Status}} health={{if .State.Health}}{{.State.Health.Status}}{{end}}'
    if ($LASTEXITCODE -ne 0) { throw 'Container inspection failed after Docker Compose apply.' }
    if ($state -eq 'running health=healthy') { break }
    Start-Sleep -Seconds 2
  }
  if ($state -ne 'running health=healthy') {
    throw "Connector did not become healthy: $state"
  }

  Write-Host "DC AMD Connector installed: $state"
  Write-Host "Bound to $BindAddress`:$Port; inbound firewall scope is NAS $NasAddress."
  if ($autoStartLauncher) {
    Write-Host "Docker Desktop will start at the next sign-in through: $autoStartLauncher"
  }
  Write-Host 'The connector access token remains only in runtime\connector-access. Do not print or send it in chat.'
}
catch {
  if ($autoStartLauncher -and (Test-Path -LiteralPath $autoStartLauncher)) {
    Remove-Item -LiteralPath $autoStartLauncher -Force -ErrorAction SilentlyContinue
  }
  if ($firewallCreated) {
    Remove-NetFirewallRule -DisplayName 'NODE.DC DC AMD Connector (NAS only)' -ErrorAction SilentlyContinue
  }
  if ($destinationCreated -and (Test-Path -LiteralPath $Destination)) {
    Remove-Item -LiteralPath $Destination -Recurse -Force -ErrorAction SilentlyContinue
  }
  throw
}
