#Requires -Version 5.1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$serverUrl = if ($env:OTO_BOOTSTRAP_SERVER_URL) { $env:OTO_BOOTSTRAP_SERVER_URL } else { '' }
$socketUrl = if ($env:OTO_BOOTSTRAP_SOCKET_URL) { $env:OTO_BOOTSTRAP_SOCKET_URL } else { '' }
$agentId = if ($env:OTO_AGENT_ID) { $env:OTO_AGENT_ID } else { '' }
$enrollmentToken = ''
$releaseBaseUrl = if ($env:OTO_BOOTSTRAP_RELEASE_BASE_URL) { $env:OTO_BOOTSTRAP_RELEASE_BASE_URL } else { '' }
$agentAlias = if ($env:OTO_AGENT_ALIAS) { $env:OTO_AGENT_ALIAS } else { '' }
$installDir = Join-Path $env:LOCALAPPDATA 'oto\bin'
$configPath = Join-Path $env:LOCALAPPDATA 'oto\agent\config.yaml'
$workspaceRoot = Join-Path $env:LOCALAPPDATA 'oto\workspace'
$logDir = Join-Path $env:LOCALAPPDATA 'oto\agent\log'
$background = $true

function Get-HashHex([string]$value) {
    $sha = [System.Security.Cryptography.SHA256]::Create()
    try {
        $bytes = [System.Text.Encoding]::UTF8.GetBytes($value)
        $hashBytes = $sha.ComputeHash($bytes)
        return -join ($hashBytes | ForEach-Object { $_.ToString('x2') })
    } finally {
        $sha.Dispose()
    }
}

function Get-DeviceIdentitySource {
    $source = ''
    try {
        $machineGuid = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Cryptography' -Name MachineGuid -ErrorAction Stop).MachineGuid
        if ($machineGuid) { $source = [string]$machineGuid }
    } catch {
        $source = ''
    }
    if (-not $source -and $env:COMPUTERNAME) {
        $source = $env:COMPUTERNAME
    }
    if (-not $source) {
        $source = [System.Net.Dns]::GetHostName()
    }
    return $source
}

function Get-DefaultAgentId {
    $hash = Get-HashHex "oto:windows:$(Get-DeviceIdentitySource)"
    return "oto-$($hash.Substring(0, [Math]::Min(32, $hash.Length)))"
}

function Get-DefaultAgentAlias {
    if ($env:COMPUTERNAME) { return $env:COMPUTERNAME }
    $hostName = [System.Net.Dns]::GetHostName()
    if ($hostName) { return $hostName }
    return 'oto-runner'
}

$i = 0
while ($i -lt $args.Count) {
    switch ($args[$i]) {
        '--'                 { $i++ }
        '--server-url'       { $serverUrl = $args[$i + 1]; $i += 2 }
        '--socket-url'       { $socketUrl = $args[$i + 1]; $i += 2 }
        '--agent-id'         { $agentId = $args[$i + 1]; $i += 2 }
        '--token'            { $enrollmentToken = $args[$i + 1]; $i += 2 }
        '--release-base-url' { $releaseBaseUrl = $args[$i + 1]; $i += 2 }
        '--agent-alias'      { $agentAlias = $args[$i + 1]; $i += 2 }
        '--install-dir'      { $installDir = $args[$i + 1]; $i += 2 }
        '--config-path'      { $configPath = $args[$i + 1]; $i += 2 }
        '--workspace-root'   { $workspaceRoot = $args[$i + 1]; $i += 2 }
        '--log-dir'          { $logDir = $args[$i + 1]; $i += 2 }
        '--no-background'    { $background = $false; $i++ }
        default              { Write-Error "Unknown option: $($args[$i])"; exit 1 }
    }
}

if (-not $serverUrl) { $serverUrl = '__OTO_BOOTSTRAP_DEFAULT_SERVER_URL__' }
if (-not $socketUrl) { $socketUrl = '__OTO_BOOTSTRAP_DEFAULT_SOCKET_URL__' }
if (-not $releaseBaseUrl) { $releaseBaseUrl = '__OTO_BOOTSTRAP_DEFAULT_RELEASE_BASE_URL__' }
if ($serverUrl -eq '__OTO_BOOTSTRAP_DEFAULT_SERVER_URL__') { $serverUrl = '' }
if ($socketUrl -eq '__OTO_BOOTSTRAP_DEFAULT_SOCKET_URL__') { $socketUrl = '' }
if ($releaseBaseUrl -eq '__OTO_BOOTSTRAP_DEFAULT_RELEASE_BASE_URL__') { $releaseBaseUrl = '' }

if (-not $enrollmentToken) {
    Write-Error 'Error: Missing required argument: --token'
    exit 1
}
if (-not $serverUrl) {
    Write-Error 'Error: Missing OTO Core URL. Use --server-url or fetch this script from the OTO Core bootstrap endpoint.'
    exit 1
}
if (-not $releaseBaseUrl) {
    Write-Error 'Error: Missing release base URL. Use --release-base-url or fetch this script from the OTO Core bootstrap endpoint.'
    exit 1
}

if (-not $releaseBaseUrl.StartsWith('https://')) {
    Write-Error 'Error: --release-base-url must use https://'
    exit 1
}

if (-not $agentId) { $agentId = Get-DefaultAgentId }
if (-not $agentAlias) { $agentAlias = Get-DefaultAgentAlias }
if (-not $agentId) {
    Write-Error 'Error: Unable to derive agent id from this device. Use --agent-id to override.'
    exit 1
}

$arch = $env:PROCESSOR_ARCHITECTURE
$archName = switch ($arch) {
    'AMD64' { 'x64' }
    'ARM64' { 'arm64' }
    default {
        Write-Error "Error: Unsupported architecture: $arch"
        exit 1
    }
}

$assetName = "oto-windows-$archName.zip"
$downloadUrl = "$releaseBaseUrl/$assetName"

$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory -Path $tmpDir | Out-Null

try {
    $zipPath = Join-Path $tmpDir $assetName

    Write-Host 'Downloading OTO agent...'
    Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing

    Write-Host 'Extracting OTO agent...'
    Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force

    $null = New-Item -ItemType Directory -Path $installDir -Force
    Copy-Item -Path (Join-Path $tmpDir 'oto.exe') -Destination (Join-Path $installDir 'oto.exe') -Force

    $configDir = Split-Path $configPath -Parent
    $agentStateDir = Join-Path $env:LOCALAPPDATA 'oto\agent'
    $pidPath = Join-Path $agentStateDir 'oto-agent.pid'

    $null = New-Item -ItemType Directory -Path $configDir -Force
    $null = New-Item -ItemType Directory -Path $agentStateDir -Force
    $null = New-Item -ItemType Directory -Path $workspaceRoot -Force
    $null = New-Item -ItemType Directory -Path $logDir -Force

    $agentIdYaml = $agentId.Replace("'", "''")
    $agentAliasYaml = $agentAlias.Replace("'", "''")
    $enrollmentTokenYaml = $enrollmentToken.Replace("'", "''")
    $serverUrlYaml = $serverUrl.Replace("'", "''")
    $socketUrlYaml = $socketUrl.Replace("'", "''")
    $installDirYaml = ($installDir -replace '\\', '/').Replace("'", "''")
    $workspaceRootYaml = ($workspaceRoot -replace '\\', '/').Replace("'", "''")
    $logDirYaml = ($logDir -replace '\\', '/').Replace("'", "''")
    $socketUrlLine = ''
    if ($socketUrl) {
        $socketUrlLine = "  socket_url: '$socketUrlYaml'`n"
    }

    $configContent = @"
agent:
  id: '$agentIdYaml'
  alias: '$agentAliasYaml'
  enrollment_token: '$enrollmentTokenYaml'
server:
  url: '$serverUrlYaml'
$socketUrlLine
runtime:
  install_dir: '$installDirYaml'
  workspace_root: '$workspaceRootYaml'
  log_dir: '$logDirYaml'
"@
    [System.IO.File]::WriteAllText($configPath, $configContent)

    $acl = Get-Acl $configPath
    $acl.SetAccessRuleProtection($true, $false)
    $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
        [System.Security.Principal.WindowsIdentity]::GetCurrent().Name,
        'FullControl',
        'Allow'
    )
    $acl.SetAccessRule($rule)
    Set-Acl -Path $configPath -AclObject $acl

    $otoBin = Join-Path $installDir 'oto.exe'

    Write-Host 'Verifying OTO binary...'
    try {
        $versionProc = Start-Process -FilePath $otoBin -ArgumentList '--version' -NoNewWindow -Wait -PassThru
        if ($versionProc.ExitCode -ne 0) {
            Write-Error 'Error: OTO binary verification failed.'
            exit 1
        }
    } catch {
        Write-Error 'Error: OTO binary verification failed.'
        exit 1
    }

    if ($background) {
        Write-Host 'Starting OTO agent in the background...'
        $outLog = Join-Path $logDir 'oto-agent.out.log'
        $errLog = Join-Path $logDir 'oto-agent.err.log'
        $proc = Start-Process -FilePath $otoBin `
            -ArgumentList "agent run --config `"$configPath`"" `
            -RedirectStandardOutput $outLog `
            -RedirectStandardError $errLog `
            -WindowStyle Hidden `
            -PassThru
        [System.IO.File]::WriteAllText($pidPath, $proc.Id.ToString())
        Write-Host "OTO agent started in the background. PID: $($proc.Id)"
        Write-Host "Log path: $outLog"
    } else {
        Write-Host 'OTO agent is configured to run in the foreground.'
        Write-Host 'To run it manually, use the following command:'
        Write-Host "  $otoBin agent run --config `"$configPath`""
    }
} finally {
    Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $tmpDir
}
