param(
    [switch]$Check,
    [switch]$Yes
)

$ErrorActionPreference = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$Root = $PSScriptRoot
$Binary = Join-Path $Root 'zfile\zfile-pro.exe'
$MetadataUrl = if ($env:ZFILE_UPDATE_METADATA_URL) {
    $env:ZFILE_UPDATE_METADATA_URL
} else {
    'https://docs.zfile.vip/update/latest.json'
}
$PackageUrl = if ($env:ZFILE_UPDATE_PACKAGE_URL) {
    $env:ZFILE_UPDATE_PACKAGE_URL
} else {
    'https://c.jun6.net/ZFILE-PRO/zfile-pro-release_windows_amd64.zip'
}

function Parse-SemVer {
    param([string]$Value)

    $match = [regex]::Match(
        $Value.Trim(),
        '^[vV]?(?<core>\d+(?:\.\d+)*)(?:-(?<pre>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$'
    )
    if (-not $match.Success) {
        throw "无效版本号: $Value"
    }
    [pscustomobject]@{
        Core = $match.Groups['core'].Value.Split('.')
        Pre  = $match.Groups['pre'].Value.Split('.', [System.StringSplitOptions]::RemoveEmptyEntries)
    }
}

function Compare-PreRelease {
    param([string[]]$Left, [string[]]$Right)

    $length = [Math]::Max($Left.Length, $Right.Length)
    for ($i = 0; $i -lt $length; $i++) {
        if ($i -ge $Left.Length) { return -1 }
        if ($i -ge $Right.Length) { return 1 }
        if ($Left[$i] -ceq $Right[$i]) { continue }

        $leftNumber = [System.Numerics.BigInteger]::Zero
        $rightNumber = [System.Numerics.BigInteger]::Zero
        $leftNumeric = [System.Numerics.BigInteger]::TryParse($Left[$i], [ref]$leftNumber)
        $rightNumeric = [System.Numerics.BigInteger]::TryParse($Right[$i], [ref]$rightNumber)
        if ($leftNumeric -and $rightNumeric) { return $leftNumber.CompareTo($rightNumber) }
        if ($leftNumeric) { return -1 }
        if ($rightNumeric) { return 1 }
        return [string]::CompareOrdinal($Left[$i], $Right[$i])
    }
    return 0
}

function Compare-SemVer {
    param([string]$Left, [string]$Right)

    $leftVersion = Parse-SemVer $Left
    $rightVersion = Parse-SemVer $Right
    $length = [Math]::Max($leftVersion.Core.Length, $rightVersion.Core.Length)
    for ($i = 0; $i -lt $length; $i++) {
        $leftPart = if ($i -lt $leftVersion.Core.Length) {
            [System.Numerics.BigInteger]::Parse($leftVersion.Core[$i])
        } else {
            [System.Numerics.BigInteger]::Zero
        }
        $rightPart = if ($i -lt $rightVersion.Core.Length) {
            [System.Numerics.BigInteger]::Parse($rightVersion.Core[$i])
        } else {
            [System.Numerics.BigInteger]::Zero
        }
        $compared = $leftPart.CompareTo($rightPart)
        if ($compared -ne 0) { return $compared }
    }

    if ($leftVersion.Pre.Length -eq 0 -and $rightVersion.Pre.Length -eq 0) { return 0 }
    if ($leftVersion.Pre.Length -eq 0) { return 1 }
    if ($rightVersion.Pre.Length -eq 0) { return -1 }
    return Compare-PreRelease $leftVersion.Pre $rightVersion.Pre
}

function Get-ZFileProcesses {
    Get-CimInstance Win32_Process -Filter "Name='zfile-pro.exe'" -ErrorAction SilentlyContinue |
        Where-Object { $_.ExecutablePath -and ([IO.Path]::GetFullPath($_.ExecutablePath) -eq [IO.Path]::GetFullPath($Binary)) }
}

function Start-ZFile {
    Start-Process -FilePath $Binary `
        -ArgumentList '-Dfile.encoding=UTF-8', '-Dsun.stdout.encoding=UTF-8', '-Dsun.stderr.encoding=UTF-8' `
        -WorkingDirectory $Root `
        -WindowStyle Hidden
    Start-Sleep -Seconds 2
    if (-not (Get-ZFileProcesses)) {
        throw '程序启动后立即退出，请查看日志确认原因'
    }
}

function Stop-ZFile {
    $processes = @(Get-ZFileProcesses)
    foreach ($process in $processes) {
        Stop-Process -Id $process.ProcessId -Force
    }
    if ($processes.Count -gt 0) {
        Start-Sleep -Seconds 1
    }
}

function Copy-BackupItem {
    param([string]$BackupDir, [string]$Name)

    $source = Join-Path $Root $Name
    if (Test-Path $source) {
        Copy-Item $source -Destination $BackupDir -Recurse -Force
    }
}

function Restore-ZFile {
    param([string]$BackupDir, [bool]$WasRunning)

    Write-Warning '更新失败，正在恢复旧程序...'
    Stop-ZFile
    foreach ($name in @('zfile', 'static')) {
        $target = Join-Path $Root $name
        Remove-Item $target -Recurse -Force -ErrorAction SilentlyContinue
        $backupItem = Join-Path $BackupDir $name
        if (Test-Path $backupItem) {
            Copy-Item $backupItem -Destination $Root -Recurse -Force
        }
    }
    Get-ChildItem $BackupDir -File -ErrorAction SilentlyContinue |
        Where-Object { $_.Extension -in @('.bat', '.ps1') -or $_.Name -like 'application.properties*' } |
        Copy-Item -Destination $Root -Force
    if ($WasRunning) {
        try {
            Start-ZFile
        } catch {
            Write-Warning "旧程序已恢复，但重新启动失败: $($_.Exception.Message)"
        }
    }
}

if (-not (Test-Path $Binary)) {
    Write-Error "找不到程序文件: $Binary"
    exit 1
}

$currentVersion = ((& $Binary --version 2>$null | Select-Object -First 1) -as [string]).Trim()
if (-not $currentVersion -or $currentVersion -eq 'unknown') {
    Write-Error '无法读取当前程序版本，当前二进制可能是旧版本或文件不完整'
    exit 1
}

Write-Host '正在检测更新...'
try {
    $metadata = Invoke-RestMethod -Uri $MetadataUrl -UseBasicParsing
    $latestVersion = ($metadata.latestVersion -as [string]).Trim()
    if (-not $latestVersion) { throw '更新信息缺少 latestVersion' }
    $comparison = Compare-SemVer $latestVersion $currentVersion
} catch {
    Write-Error "检测更新失败: $($_.Exception.Message)"
    exit 1
}

Write-Host "当前版本: $currentVersion"
Write-Host "最新版本: $latestVersion"
if ($comparison -lt 0) {
    Write-Host '当前程序版本高于已发布版本，无需更新'
    exit 0
}
if ($comparison -eq 0) {
    Write-Host '当前已是最新版本'
    exit 0
}

Write-Warning "检测到新版本: $currentVersion -> $latestVersion"
if ($Check) { exit 2 }
if (-not $Yes) {
    $answer = Read-Host '确认下载并更新吗？更新前会备份旧程序 [y/N]'
    if ($answer -notmatch '^(?i:y|yes)$') {
        Write-Host '已取消更新'
        exit 0
    }
}

$workDir = Join-Path ([IO.Path]::GetTempPath()) ("zfile-update-" + [guid]::NewGuid().ToString('N'))
$archive = Join-Path $workDir 'update.zip'
$staging = Join-Path $workDir 'staging'
New-Item $staging -ItemType Directory -Force | Out-Null

try {
    Write-Host "正在下载更新包: $PackageUrl"
    Invoke-WebRequest -Uri $PackageUrl -UseBasicParsing -OutFile $archive
    Expand-Archive -Path $archive -DestinationPath $staging -Force
    $packageBinary = Get-ChildItem $staging -Recurse -File -Filter 'zfile-pro.exe' |
        Where-Object { $_.Directory.Name -eq 'zfile' } |
        Select-Object -First 1
    if (-not $packageBinary) { throw '更新包缺少 zfile\zfile-pro.exe' }
    $packageRoot = $packageBinary.Directory.Parent.FullName
    foreach ($required in @(
        'zfile\zfile-pro.exe',
        '双击我启动.bat',
        '后台启动.bat',
        '更新程序.bat',
        'update.ps1'
    )) {
        if (-not (Test-Path (Join-Path $packageRoot $required))) {
            throw "更新包缺少必要文件: $required"
        }
    }

    $wasRunning = @(Get-ZFileProcesses).Count -gt 0
    if ($wasRunning) { Stop-ZFile }

    $safeVersion = $currentVersion -replace '[^0-9A-Za-z._-]', '_'
    $backupDir = Join-Path $Root ("backup\{0}_{1}" -f $safeVersion, (Get-Date -Format 'yyyyMMdd_HHmmss'))
    New-Item $backupDir -ItemType Directory -Force | Out-Null
    try {
        foreach ($name in @('zfile', 'static', 'application.properties', 'application.properties.example')) {
            Copy-BackupItem $backupDir $name
        }
        Get-ChildItem $Root -File |
            Where-Object { $_.Extension -in @('.bat', '.ps1') } |
            Copy-Item -Destination $backupDir -Force
    } catch {
        if ($wasRunning) { Start-ZFile }
        throw "备份旧程序失败: $($_.Exception.Message)"
    }
    Write-Host "旧程序已备份到: $backupDir"

    try {
        foreach ($name in @('zfile', 'static')) {
            $target = Join-Path $Root $name
            Remove-Item $target -Recurse -Force -ErrorAction SilentlyContinue
            $source = Join-Path $packageRoot $name
            if (Test-Path $source) {
                Copy-Item $source -Destination $Root -Recurse -Force
            }
        }
        $packageScripts = @(Get-ChildItem $packageRoot -File |
            Where-Object { $_.Extension -in @('.bat', '.ps1') })
        $packageScriptNames = @($packageScripts | ForEach-Object { $_.Name })
        Get-ChildItem $Root -File |
            Where-Object {
                $_.Extension -in @('.bat', '.ps1') -and
                $_.Name -notin $packageScriptNames
            } |
            Remove-Item -Force
        $packageScripts | Copy-Item -Destination $Root -Force

        $packagePropertiesExample = Join-Path $packageRoot 'application.properties.example'
        if (Test-Path $packagePropertiesExample) {
            Copy-Item $packagePropertiesExample -Destination $Root -Force
        }

        $installedVersion = ((& $Binary --version 2>$null | Select-Object -First 1) -as [string]).Trim()
        if ($installedVersion -ne $latestVersion) {
            throw "更新包版本校验失败: 期望=$latestVersion，实际=$installedVersion"
        }
        if ($wasRunning) { Start-ZFile }
    } catch {
        $reason = $_.Exception.Message
        Restore-ZFile $backupDir $wasRunning
        throw $reason
    }

    Write-Host "更新完成: $currentVersion -> $latestVersion"
    Write-Host "备份目录: $backupDir"
    if (-not $wasRunning) { Write-Host '更新前服务未运行，当前保持停止状态' }
} catch {
    Write-Error "更新失败: $($_.Exception.Message)"
    exit 1
} finally {
    Remove-Item $workDir -Recurse -Force -ErrorAction SilentlyContinue
}
