<#
    AccessKit updater -- checks www.gigasoundproject.com for a newer build, and applies it.

    ============================== HOW THIS WORKS ==============================
    There is no server. `version.json` is a static file served by GitHub Pages from the
    same site that already hosts the download, so there is nothing to run, patch or
    attack. This script fetches it, compares against a stamp file written at install
    time, and if a newer build exists: downloads the zip, VERIFIES ITS SHA-256, extracts
    it to a temp folder and runs that package's own install.ps1.

    *The hash check is the one part that is not optional.* An updater downloads and
    installs code; that is the single place where a mistake actually matters. If the
    hash does not match what version.json says, this refuses to install and says so.

    ============================== PER-GAME CONFIG =============================
    This file is COPIED per game rather than shared, exactly like the mods themselves --
    see "do not merge the readers". Only the CONFIG block below differs between copies,
    so a change to one game can never break the other. Port fixes across by hand.
#>

[CmdletBinding()]
param(
    # Skip the "an update is available, install it?" prompt. For unattended re-runs.
    [switch] $Yes,
    # Report what would happen and change nothing.
    [switch] $CheckOnly,
    # Override auto-detection when the game is not in a standard Steam library.
    [string] $GamePath
)

# ------------------------------- CONFIG (MK11) -------------------------------
$GameKey      = 'mk11'
$DisplayName  = 'Mortal Kombat 11 Accessibility'
$VersionUrl   = 'https://www.gigasoundproject.com/mods/version.json'
$StampFile    = 'accesskit_version.txt'
$SteamFolder  = 'Mortal Kombat 11'
$MarkerPath   = 'Binaries\Retail\MK11.exe'      # proves we found the right folder
# -----------------------------------------------------------------------------

$ErrorActionPreference = 'Stop'

function Say([string] $m) { Write-Host $m }

<#
    Wait for the user, but never DIE waiting. Read-Host throws outright when the host is
    non-interactive (a scheduled run, a CI check, another script calling this one), which
    would turn a clean "you are up to date" into a red error. The pause is a courtesy for
    double-click users; it must not be load-bearing.
#>
function Pause-IfInteractive {
    try { Read-Host 'Press Enter to close' | Out-Null } catch { }
}

function Fail([string] $m) {
    Write-Host ''
    Write-Host "PROBLEM: $m"
    Write-Host ''
    Write-Host 'Nothing was changed. Your current install is untouched.'
    Pause-IfInteractive
    exit 1
}

function Test-GameFolder([string] $p) {
    if ([string]::IsNullOrWhiteSpace($p)) { return $false }
    return (Test-Path (Join-Path $p $MarkerPath))
}

<#
    Find the game by reading Steam's own library list. Same approach as install.ps1 --
    libraryfolders.vdf lists every library root, including ones on other drives, which
    is why we do not just guess C:\Program Files (x86)\Steam.
#>
function Find-Game {
    $roots = @()
    foreach ($base in @("${env:ProgramFiles(x86)}\Steam", "$env:ProgramFiles\Steam", 'C:\Steam')) {
        $vdf = Join-Path $base 'steamapps\libraryfolders.vdf'
        if (-not (Test-Path $vdf)) { continue }
        $roots += $base
        foreach ($line in (Get-Content $vdf)) {
            if ($line -match '"path"\s+"([^"]+)"') {
                $roots += ($matches[1] -replace '\\\\', '\')
            }
        }
    }
    foreach ($r in ($roots | Select-Object -Unique)) {
        $candidate = Join-Path $r "steamapps\common\$SteamFolder"
        if (Test-GameFolder $candidate) { return $candidate }
    }
    return $null
}

function Get-InstalledVersion([string] $gameDir) {
    $stamp = Join-Path $gameDir $StampFile
    if (-not (Test-Path $stamp)) { return $null }
    try {
        $raw = (Get-Content $stamp -Raw).Trim()
        if ($raw.Length -gt 0) { return $raw }
    } catch { }
    return $null
}

<#
    Compare two dotted version strings numerically. [version] handles 0.6.0 vs 0.10.0
    correctly, where a string compare would say 0.6.0 is newer. Falls back to a string
    inequality test if either side is not parseable, which at worst offers an update
    that is not needed rather than hiding one that is.
#>
function Test-IsNewer([string] $candidate, [string] $current) {
    if ([string]::IsNullOrWhiteSpace($current)) { return $true }
    try {
        return ([version] $candidate) -gt ([version] $current)
    } catch {
        return ($candidate -ne $current)
    }
}

# ============================== 1. locate ====================================

Say ''
Say "$DisplayName -- check for updates"
Say ''

if ([string]::IsNullOrWhiteSpace($GamePath)) { $GamePath = Find-Game }
if (-not (Test-GameFolder $GamePath)) {
    Fail ("Could not find the game. Run this again with the folder, for example:`r`n" +
          "  update.ps1 -GamePath ""D:\Steam\steamapps\common\$SteamFolder""")
}
Say "Game folder     : $GamePath"

$installed = Get-InstalledVersion $GamePath
if ($null -eq $installed) {
    Say 'Installed       : unknown (no version stamp -- an older build, probably)'
} else {
    Say "Installed       : $installed"
}

# ============================== 2. ask the site ==============================

Say 'Checking for a newer version...'
try {
    # TLS 1.2 is not the default on stock PowerShell 5.1 and GitHub Pages requires it.
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $resp = Invoke-WebRequest -Uri $VersionUrl -UseBasicParsing -TimeoutSec 30

    # .Content is a string when the server sends a text Content-Type and a byte[] when it
    # does not. Do not assume: decode explicitly, and strip a leading byte-order mark,
    # which ConvertFrom-Json will not accept.
    $raw = $resp.Content
    if ($raw -is [byte[]]) { $raw = [Text.Encoding]::UTF8.GetString($raw) }
    $raw = $raw -replace '^ï»¿', ''
    $raw = $raw.TrimStart([char]0xFEFF, [char]0x200B).Trim()

    $all = $raw | ConvertFrom-Json
} catch {
    Fail ("Could not reach $VersionUrl`r`n" +
          "  $($_.Exception.Message)`r`n" +
          '  Check your internet connection and try again.')
}

$info = $all.$GameKey
if ($null -eq $info) { Fail "version.json has no entry for '$GameKey'." }
foreach ($field in @('version', 'url', 'sha256')) {
    if ([string]::IsNullOrWhiteSpace($info.$field)) { Fail "version.json entry for '$GameKey' is missing '$field'." }
}

Say "Latest          : $($info.version)"
if ($info.notes) { Say "What is new     : $($info.notes)" }
Say ''

if (-not (Test-IsNewer $info.version $installed)) {
    Say 'You already have the latest version. Nothing to do.'
    Pause-IfInteractive
    exit 0
}

Say "An update is available: $($info.version)"
if ($CheckOnly) { Say '(check only -- not installing)'; Pause-IfInteractive; exit 0 }

if (-not $Yes) {
    $answer = ''
    try { $answer = Read-Host 'Download and install it now? (Y/N)' }
    catch { Say 'Cannot prompt here. Re-run with -Yes to install without asking.'; exit 0 }
    if ($answer -notmatch '^[Yy]') { Say 'Cancelled. Nothing was changed.'; exit 0 }
}

# ============================== 3. download ==================================

# Refuse to install over a running game -- the DLLs are locked and a half-applied
# update is far worse than a postponed one.
if (Get-Process -Name 'MK11', 'MK11_DX12' -ErrorAction SilentlyContinue) {
    Fail 'The game is running. Close it completely, then run this again.'
}

$work = Join-Path ([IO.Path]::GetTempPath()) ("accesskit_" + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $work -Force | Out-Null
$zipPath = Join-Path $work 'update.zip'

try {
    Say 'Downloading...'
    Invoke-WebRequest -Uri $info.url -OutFile $zipPath -UseBasicParsing -TimeoutSec 600

    # ---------------------- 4. VERIFY BEFORE TRUSTING ------------------------
    Say 'Checking the download...'
    $actual = (Get-FileHash -Path $zipPath -Algorithm SHA256).Hash
    $expect = $info.sha256.Trim()
    if ($actual -ne $expect.ToUpperInvariant() -and $actual -ne $expect) {
        Fail ("The downloaded file does not match what the site says it should be.`r`n" +
              "  expected $expect`r`n" +
              "  actual   $actual`r`n" +
              '  NOTHING WAS INSTALLED. Try again; if it keeps happening, say so.')
    }
    Say 'Download verified.'

    if ($info.size -and ((Get-Item $zipPath).Length -ne [int64] $info.size)) {
        Say "  (note: size differs from version.json, but the hash matched -- continuing)"
    }

    # ---------------------- 5. hand off to the real installer ----------------
    Say 'Extracting...'
    $unpack = Join-Path $work 'unpacked'
    Expand-Archive -Path $zipPath -DestinationPath $unpack -Force

    $installer = Get-ChildItem -Path $unpack -Filter 'install.ps1' -Recurse | Select-Object -First 1
    if ($null -eq $installer) { Fail 'The downloaded package has no install.ps1 in it.' }

    Say 'Installing...'
    & $installer.FullName -GamePath $GamePath
    if ($LASTEXITCODE -ne 0 -and $null -ne $LASTEXITCODE) {
        Fail "The installer reported a problem (exit code $LASTEXITCODE)."
    }

    # ---------------------- 6. record what we installed ----------------------
    Set-Content -Path (Join-Path $GamePath $StampFile) -Value $info.version -Encoding utf8
    Say ''
    Say "Updated to $($info.version)."
    Say 'Start the game as usual.'
}
finally {
    try { Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue } catch { }
}

Pause-IfInteractive
