@echo off
setlocal EnableExtensions

set "APP_DIR=%~dp0"
if "%APP_DIR:~-1%"=="\" set "APP_DIR=%APP_DIR:~0,-1%"

set "UPDATER_EXE=%APP_DIR%\bin\updater.exe"
set "ENTRY_EXE=Openclaw Windows.exe"
set "DOWNLOAD_DIR=%TEMP%\openclaw-manual-update"
set "ZIP_INFO=%TEMP%\openclaw-manual-update-zip-path.txt"
set "PS_SCRIPT=%TEMP%\openclaw-manual-auto-update.ps1"
set "PACKAGED_SERVER_URL=https://xiake.yun"
set "FALLBACK_SERVER_URLS=https://openclaw.ikidi.top;https://xiake.yun"
set "APP_VARIANT=xk"

if defined OPENCLAW_UPDATE_VARIANT set "APP_VARIANT=%OPENCLAW_UPDATE_VARIANT%"
if /I "%APP_VARIANT%"=="auto" (
  echo %APP_DIR% | findstr /I /C:"-kd" >nul
  if not errorlevel 1 set "APP_VARIANT=kd"
)
if /I "%APP_VARIANT%"=="auto" (
  echo %APP_DIR% | findstr /I /C:"-xk" >nul
  if not errorlevel 1 set "APP_VARIANT=xk"
)
if /I "%APP_VARIANT%"=="kd" set "FALLBACK_SERVER_URLS=https://openclaw.ikidi.top;https://xiake.yun"
if /I "%APP_VARIANT%"=="xk" set "FALLBACK_SERVER_URLS=https://xiake.yun;https://openclaw.ikidi.top"

if not exist "%UPDATER_EXE%" (
  echo updater.exe was not found:
  echo %UPDATER_EXE%
  echo Put this bat file in the old Openclaw root folder and run it again.
  pause
  exit /b 1
)

if not exist "%APP_DIR%\%ENTRY_EXE%" (
  for %%F in ("%APP_DIR%\*.exe") do (
    if /I not "%%~nxF"=="updater.exe" if /I not "%%~nxF"=="server.exe" (
      set "ENTRY_EXE=%%~nxF"
      goto :entry_found
    )
  )
)

:entry_found

if exist "%ZIP_INFO%" del /f /q "%ZIP_INFO%" >nul 2>nul

set "OPENCLAW_MANUAL_APP_DIR=%APP_DIR%"
set "OPENCLAW_MANUAL_PACKAGED_SERVER=%PACKAGED_SERVER_URL%"
set "OPENCLAW_MANUAL_FALLBACK_SERVERS=%FALLBACK_SERVER_URLS%"
set "OPENCLAW_MANUAL_VARIANT=%APP_VARIANT%"
set "OPENCLAW_MANUAL_DOWNLOAD_DIR=%DOWNLOAD_DIR%"
set "OPENCLAW_MANUAL_ZIP_INFO=%ZIP_INFO%"

> "%PS_SCRIPT%" echo $ErrorActionPreference = 'Stop'
>> "%PS_SCRIPT%" echo $appDir = $env:OPENCLAW_MANUAL_APP_DIR
>> "%PS_SCRIPT%" echo $envServer = $env:OPENCLAW_UPDATE_SERVER
>> "%PS_SCRIPT%" echo $packagedServer = $env:OPENCLAW_MANUAL_PACKAGED_SERVER
>> "%PS_SCRIPT%" echo $fallbackServers = ($env:OPENCLAW_MANUAL_FALLBACK_SERVERS).Split(';') ^| Where-Object { $_ }
>> "%PS_SCRIPT%" echo $variant = ($env:OPENCLAW_MANUAL_VARIANT).ToLowerInvariant()
>> "%PS_SCRIPT%" echo $downloadDir = $env:OPENCLAW_MANUAL_DOWNLOAD_DIR
>> "%PS_SCRIPT%" echo $zipInfo = $env:OPENCLAW_MANUAL_ZIP_INFO
>> "%PS_SCRIPT%" echo function Normalize-Server([string]$url) {
>> "%PS_SCRIPT%" echo   if (-not $url) { return $null }
>> "%PS_SCRIPT%" echo   $value = $url.Trim().TrimEnd('/')
>> "%PS_SCRIPT%" echo   if ($value -match '^https?://') { return $value }
>> "%PS_SCRIPT%" echo   return $null
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo function Read-PlainServerUrl([string]$path) {
>> "%PS_SCRIPT%" echo   try {
>> "%PS_SCRIPT%" echo     if (-not (Test-Path -LiteralPath $path)) { return $null }
>> "%PS_SCRIPT%" echo     $json = Get-Content -LiteralPath $path -Raw ^| ConvertFrom-Json
>> "%PS_SCRIPT%" echo     return Normalize-Server $json.serverUrl
>> "%PS_SCRIPT%" echo   } catch {
>> "%PS_SCRIPT%" echo     return $null
>> "%PS_SCRIPT%" echo   }
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo function Get-EntryText($item) {
>> "%PS_SCRIPT%" echo   if (-not $item) { return '' }
>> "%PS_SCRIPT%" echo   $values = @($item.fileName, $item.filename, $item.name, $item.downloadUrl, $item.url, $item.packageUrl, $item.portableUrl, $item.zipUrl) ^| Where-Object { $_ }
>> "%PS_SCRIPT%" echo   return ($values -join ' ').ToLowerInvariant()
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo function Get-FirstValue($values) {
>> "%PS_SCRIPT%" echo   foreach ($value in $values) {
>> "%PS_SCRIPT%" echo     if ($value) { return [string]$value }
>> "%PS_SCRIPT%" echo   }
>> "%PS_SCRIPT%" echo   return $null
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo function Select-VariantItems($items, [string]$variant) {
>> "%PS_SCRIPT%" echo   $valid = @($items ^| Where-Object { $_ })
>> "%PS_SCRIPT%" echo   if ($variant -ne 'kd' -and $variant -ne 'xk') { return $valid }
>> "%PS_SCRIPT%" echo   $own = '-' + $variant
>> "%PS_SCRIPT%" echo   $other = if ($variant -eq 'kd') { '-xk' } else { '-kd' }
>> "%PS_SCRIPT%" echo   $matched = @($valid ^| Where-Object { (Get-EntryText $_).Contains($own) })
>> "%PS_SCRIPT%" echo   if ($matched.Count -gt 0) { return $matched }
>> "%PS_SCRIPT%" echo   return @($valid ^| Where-Object { -not (Get-EntryText $_).Contains($other) })
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo if ($variant -ne 'kd' -and $variant -ne 'xk') {
>> "%PS_SCRIPT%" echo   $dir = $appDir.ToLowerInvariant()
>> "%PS_SCRIPT%" echo   if ($dir.Contains('-kd')) { $variant = 'kd' }
>> "%PS_SCRIPT%" echo   elseif ($dir.Contains('-xk')) { $variant = 'xk' }
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo $configuredServer = $null
>> "%PS_SCRIPT%" echo foreach ($path in @((Join-Path $appDir 'server-config.json'), (Join-Path $appDir 'bin\server-config.json'), (Join-Path $appDir 'bin\config\server-config.json'))) {
>> "%PS_SCRIPT%" echo   $configuredServer = Read-PlainServerUrl $path
>> "%PS_SCRIPT%" echo   if ($configuredServer) { break }
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo $servers = @()
>> "%PS_SCRIPT%" echo $servers += Normalize-Server $envServer
>> "%PS_SCRIPT%" echo $servers += $configuredServer
>> "%PS_SCRIPT%" echo $servers += Normalize-Server $packagedServer
>> "%PS_SCRIPT%" echo foreach ($server in $fallbackServers) { $servers += Normalize-Server $server }
>> "%PS_SCRIPT%" echo $servers = $servers ^| Where-Object { $_ } ^| Select-Object -Unique
>> "%PS_SCRIPT%" echo New-Item -ItemType Directory -Force -Path $downloadDir ^| Out-Null
>> "%PS_SCRIPT%" echo $candidate = $null
>> "%PS_SCRIPT%" echo $baseUrl = $null
>> "%PS_SCRIPT%" echo foreach ($server in $servers) {
>> "%PS_SCRIPT%" echo   try {
>> "%PS_SCRIPT%" echo     $url = $server.TrimEnd('/') + '/api/download/latest'
>> "%PS_SCRIPT%" echo     Write-Host ('Checking server: ' + $server)
>> "%PS_SCRIPT%" echo     $res = Invoke-RestMethod -Uri $url -TimeoutSec 20
>> "%PS_SCRIPT%" echo     if ($res.code -ne 0) { continue }
>> "%PS_SCRIPT%" echo     $data = $res.data
>> "%PS_SCRIPT%" echo     $windows = $data.windows
>> "%PS_SCRIPT%" echo     $nested = $null
>> "%PS_SCRIPT%" echo     if ($windows -and $windows.portable) { $nested = $windows.portable }
>> "%PS_SCRIPT%" echo     $items = Select-VariantItems @($data.windowsPortable, $data.portable, $nested, $windows) $variant
>> "%PS_SCRIPT%" echo     foreach ($item in $items) {
>> "%PS_SCRIPT%" echo       $itemUrl = Get-FirstValue @($item.downloadUrl, $item.url, $item.packageUrl, $item.portableUrl, $item.zipUrl)
>> "%PS_SCRIPT%" echo       if ($itemUrl) {
>> "%PS_SCRIPT%" echo         $candidate = $item
>> "%PS_SCRIPT%" echo         $baseUrl = $server.TrimEnd('/')
>> "%PS_SCRIPT%" echo         break
>> "%PS_SCRIPT%" echo       }
>> "%PS_SCRIPT%" echo     }
>> "%PS_SCRIPT%" echo     if ($candidate) { break }
>> "%PS_SCRIPT%" echo   } catch {
>> "%PS_SCRIPT%" echo   }
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo if (-not $candidate) { throw 'Could not get a matching latest package from server.' }
>> "%PS_SCRIPT%" echo $downloadUrl = Get-FirstValue @($candidate.downloadUrl, $candidate.url, $candidate.packageUrl, $candidate.portableUrl, $candidate.zipUrl)
>> "%PS_SCRIPT%" echo if (-not $downloadUrl.StartsWith('http')) { $downloadUrl = $baseUrl + '/' + $downloadUrl.TrimStart('/') }
>> "%PS_SCRIPT%" echo $fileName = Get-FirstValue @($candidate.fileName, $candidate.filename, $candidate.name)
>> "%PS_SCRIPT%" echo if (-not $fileName) { $fileName = [IO.Path]::GetFileName(([Uri]$downloadUrl).AbsolutePath) }
>> "%PS_SCRIPT%" echo if (-not $fileName) { $fileName = 'openclaw-latest.zip' }
>> "%PS_SCRIPT%" echo $zipPath = Join-Path $downloadDir $fileName
>> "%PS_SCRIPT%" echo Write-Host ('Selected variant: ' + $variant)
>> "%PS_SCRIPT%" echo Write-Host ('Download URL: ' + $downloadUrl)
>> "%PS_SCRIPT%" echo Write-Host ('Save path: ' + $zipPath)
>> "%PS_SCRIPT%" echo Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -TimeoutSec 600
>> "%PS_SCRIPT%" echo if (-not (Test-Path $zipPath)) { throw 'Download failed.' }
>> "%PS_SCRIPT%" echo $sha = Get-FirstValue @($candidate.sha256, $candidate.sha256sum, $candidate.checksum, $candidate.hash)
>> "%PS_SCRIPT%" echo if ($sha) {
>> "%PS_SCRIPT%" echo   $actual = (Get-FileHash -Algorithm SHA256 -Path $zipPath).Hash.ToLowerInvariant()
>> "%PS_SCRIPT%" echo   if ($actual -ne $sha.ToLowerInvariant()) {
>> "%PS_SCRIPT%" echo     Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue
>> "%PS_SCRIPT%" echo     throw 'SHA256 verification failed.'
>> "%PS_SCRIPT%" echo   }
>> "%PS_SCRIPT%" echo }
>> "%PS_SCRIPT%" echo [IO.File]::WriteAllText($zipInfo, $zipPath, [Text.Encoding]::Default)

echo Checking and downloading the latest version...
powershell -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%"
if errorlevel 1 (
  echo.
  echo Automatic download failed. Please check the network and try again.
  pause
  exit /b 1
)

set "ZIP_PATH="
for /f "usebackq delims=" %%I in ("%ZIP_INFO%") do set "ZIP_PATH=%%I"

if not exist "%ZIP_PATH%" (
  echo Downloaded update package was not found:
  echo %ZIP_PATH%
  pause
  exit /b 1
)

echo.
echo Starting updater...
echo App dir: %APP_DIR%
echo Update zip: %ZIP_PATH%
echo Entry exe: %ENTRY_EXE%
echo.

"%UPDATER_EXE%" --app-dir "%APP_DIR%" --zip "%ZIP_PATH%" --entry-exe "%ENTRY_EXE%"

echo Updater has been started. Please wait for the new version to open.
pause
