[CmdletBinding()]
param(
    [string]$InstallDir = (Join-Path $env:ProgramFiles 'KIM Quest Remote'),
    [string]$ManifestUrl = 'https://kim-quest-remote.pages.dev/windows-update.json',
    [string]$ManifestPath,
    [switch]$VerifyManifestOnly,
    [switch]$Show
)

$ErrorActionPreference = 'Stop'
$InstallDir = [IO.Path]::GetFullPath($InstallDir)
$HostExe = Join-Path $InstallDir 'KIMQuestRemoteHost.exe'
$LogDir = Join-Path $env:LOCALAPPDATA 'KIMQuestRemote'
$LogPath = Join-Path $LogDir 'patcher.log'
$PublicModulus = '0UezD6sH8RKRSnBpMQf9NQMhN1XKuIbuRAVun155Z3vKLl9YxjeGz52YP+b2TKcGLZYjaKwzqM+9UCfFXXNPHO5aADdOtORutSgi26Red2fm8DxQlZxgkRyN8yFTCVowm06gxuj+SMjDouAfILg04Ugl8SVegTNEgAYKs1Q+OirCZi4bjyYfnMxaMPZYVDQL8TMWrp/ynVYM1W8iNeuKEXfsezhONSPYon1qND9Mm0xjIdoK8tJEX4TKSQeGgu53W20TGPtzpdxHt2lpfuNA7hQ4kL0tqMYK1tywVZn5ZQUd1jaMtAtFzg6kR19VF3Ng/M740iUV569f6Z5t6+Lkqu8NtRlzi7twl4nQGFwQae8wLwnRAw+1eQDKyFah7kGpmqZTrvNQS7WzvQPgEMqk/mHrTe5R54jNOouR4o7w2XaqLDUDfrgAc58zw2TCKK8iH0aMnFhnCO72Aflj/qNhEQYfKHsVA8AA1i7n9j8LIHo29xivrS/TuSUFJILq63E3'
$PublicExponent = 'AQAB'

New-Item -ItemType Directory -Path $LogDir -Force | Out-Null

function Write-PatchLog {
    param([string]$Message)
    Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format o) $Message"
}

function Start-KIMHost {
    if (-not (Test-Path -LiteralPath $HostExe)) {
        throw "Host executable is missing: $HostExe"
    }
    $Arguments = if ($Show) { '--show' } else { '' }
    if ($Arguments) {
        Start-Process -FilePath $HostExe -ArgumentList $Arguments -WorkingDirectory $InstallDir
    } else {
        Start-Process -FilePath $HostExe -WorkingDirectory $InstallDir
    }
}

function Get-InstalledVersion {
    if (-not (Test-Path -LiteralPath $HostExe)) { return [version]'0.0.0' }
    try {
        return [version]((& $HostExe --version | Select-Object -First 1).Trim())
    } catch {
        Write-PatchLog "Could not read installed version: $($_.Exception.Message)"
        return [version]'0.0.0'
    }
}

function Test-ManifestSignature {
    param([pscustomobject]$Manifest)
    $Canonical = 'KQRW1|{0}|{1}|{2}|{3}' -f `
        [string]$Manifest.version, `
        [string]$Manifest.size, `
        ([string]$Manifest.sha256).ToUpperInvariant(), `
        [string]$Manifest.url
    $Parameters = New-Object Security.Cryptography.RSAParameters
    $Parameters.Modulus = [Convert]::FromBase64String($PublicModulus)
    $Parameters.Exponent = [Convert]::FromBase64String($PublicExponent)
    $Rsa = New-Object Security.Cryptography.RSACryptoServiceProvider
    try {
        $Rsa.ImportParameters($Parameters)
        return $Rsa.VerifyData(
            [Text.Encoding]::UTF8.GetBytes($Canonical),
            'SHA256',
            [Convert]::FromBase64String([string]$Manifest.signature)
        )
    } finally {
        $Rsa.Dispose()
    }
}

$WorkRoot = Join-Path $env:TEMP ("KIMRemotePatch-" + [guid]::NewGuid().ToString('N'))
$StoppedForUpdate = $false
$PreviousExe = Join-Path $InstallDir 'KIMQuestRemoteHost.previous.exe'
try {
    Write-PatchLog 'Patch check started.'
    $Manifest = if ($ManifestPath) {
        Get-Content -Raw -LiteralPath $ManifestPath | ConvertFrom-Json
    } else {
        Invoke-RestMethod -Uri $ManifestUrl -Method Get -TimeoutSec 20
    }
    if (-not (Test-ManifestSignature $Manifest)) {
        throw 'Update manifest signature is invalid.'
    }
    if ($VerifyManifestOnly) {
        Write-Output 'PATCH_MANIFEST_SIGNATURE_PASS'
        exit 0
    }
    $Latest = [version][string]$Manifest.version
    $Installed = Get-InstalledVersion
    if ($Latest -le $Installed) {
        Write-PatchLog "No patch needed. Installed=$Installed Latest=$Latest"
        Start-KIMHost
        exit 0
    }

    New-Item -ItemType Directory -Path $WorkRoot -Force | Out-Null
    $Archive = Join-Path $WorkRoot 'update.zip'
    Invoke-WebRequest -Uri ([string]$Manifest.url) -OutFile $Archive -UseBasicParsing -TimeoutSec 180
    $ArchiveInfo = Get-Item -LiteralPath $Archive
    if ($ArchiveInfo.Length -ne [int64]$Manifest.size) {
        throw 'Downloaded update size does not match the signed manifest.'
    }
    $ArchiveHash = (Get-FileHash -LiteralPath $Archive -Algorithm SHA256).Hash
    if ($ArchiveHash -ne ([string]$Manifest.sha256).ToUpperInvariant()) {
        throw 'Downloaded update SHA-256 does not match the signed manifest.'
    }

    $Extract = Join-Path $WorkRoot 'extract'
    Expand-Archive -LiteralPath $Archive -DestinationPath $Extract
    $Checksums = Join-Path $Extract 'CHECKSUMS-SHA256.txt'
    if (-not (Test-Path -LiteralPath $Checksums)) {
        throw 'Update bundle has no checksum manifest.'
    }
    foreach ($Line in Get-Content -LiteralPath $Checksums) {
        if ($Line -notmatch '^([0-9A-F]{64}) \*(.+)$') {
            throw "Invalid checksum line: $Line"
        }
        $ItemPath = [IO.Path]::GetFullPath((Join-Path $Extract $Matches[2]))
        $ExtractRoot = [IO.Path]::GetFullPath($Extract) + [IO.Path]::DirectorySeparatorChar
        if (-not $ItemPath.StartsWith($ExtractRoot, [StringComparison]::OrdinalIgnoreCase)) {
            throw 'Update checksum path escaped the extraction directory.'
        }
        if ((Get-FileHash -LiteralPath $ItemPath -Algorithm SHA256).Hash -ne $Matches[1]) {
            throw "Update bundle checksum failed: $($Matches[2])"
        }
    }

    $NewExe = Join-Path $Extract 'KIMQuestRemoteHost.exe'
    $NewVersion = [version]((& $NewExe --version | Select-Object -First 1).Trim())
    if ($NewVersion -ne $Latest) {
        throw "Signed version $Latest does not match executable version $NewVersion."
    }

    Get-CimInstance Win32_Process -Filter "Name='KIMQuestRemoteHost.exe'" -ErrorAction SilentlyContinue |
        ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction Stop }
    $StoppedForUpdate = $true
    if (Test-Path -LiteralPath $HostExe) {
        Copy-Item -LiteralPath $HostExe -Destination $PreviousExe -Force
    }
    $NewTarget = "$HostExe.new"
    Copy-Item -LiteralPath $NewExe -Destination $NewTarget -Force
    if ((Get-FileHash -LiteralPath $NewTarget -Algorithm SHA256).Hash -ne
        (Get-FileHash -LiteralPath $NewExe -Algorithm SHA256).Hash) {
        throw 'Atomic host copy verification failed.'
    }
    Move-Item -LiteralPath $NewTarget -Destination $HostExe -Force

    foreach ($Name in @(
        'KIMRemotePatcher.ps1',
        'Audit-ReadOnly.ps1',
        'Verify-Install.ps1',
        'Rollback-Host.ps1',
        'Set-PairingPassword.ps1',
        'README-WINDOWS.md',
        'KIMQuestRemote.apk',
        'KIMQuestRemote-update.json'
    )) {
        $Source = Join-Path $Extract $Name
        if (Test-Path -LiteralPath $Source) {
            Copy-Item -LiteralPath $Source -Destination (Join-Path $InstallDir $Name) -Force
        }
    }
    Write-PatchLog "Patch installed successfully: $Installed -> $Latest"
    Start-KIMHost
} catch {
    Write-PatchLog "Patch check failed safely: $($_.Exception.Message)"
    if ($VerifyManifestOnly) {
        Write-Error $_.Exception.Message
        exit 1
    }
    if ($StoppedForUpdate -and (Test-Path -LiteralPath $PreviousExe)) {
        Copy-Item -LiteralPath $PreviousExe -Destination $HostExe -Force
        Write-PatchLog 'Previous host executable restored.'
    }
    if (Test-Path -LiteralPath $HostExe) {
        Start-KIMHost
    }
} finally {
    if (Test-Path -LiteralPath $WorkRoot) {
        Remove-Item -LiteralPath $WorkRoot -Recurse -Force -ErrorAction SilentlyContinue
    }
}
