$ErrorActionPreference = 'Stop'

$manifestUrl = 'https://3.onj.me/programs/.sendto.json'

function Find-7Zip {
    $cmd = Get-Command 7z.exe -ErrorAction SilentlyContinue
    if ($cmd) { return $cmd.Source }

    $candidates = @(
        (Join-Path $env:ProgramFiles '7-Zip\7z.exe'),
        (Join-Path ${env:ProgramFiles(x86)} '7-Zip\7z.exe')
    )

    foreach ($candidate in $candidates) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) { return $candidate }
    }

    return $null
}

function Ensure-7Zip {
    $sevenZip = Find-7Zip
    if ($sevenZip) { return $sevenZip }

    Write-Host ''
    Write-Host '7-Zip is required to extract the SendTo package, but it was not found.'
    $winget = Get-Command winget.exe -ErrorAction SilentlyContinue
    if (-not $winget) {
        throw '7-Zip is required to install this .7z update, and winget was not found. Install 7-Zip, then run this updater again.'
    }

    $answer = Read-Host 'Install 7-Zip now using winget? Type Y to continue'
    if ($answer -notmatch '^(?i:y(?:es)?)$') {
        throw '7-Zip installation was cancelled. Install 7-Zip, then run this updater again.'
    }

    Write-Host 'Installing 7-Zip with winget...'
    & $winget.Source install --id 7zip.7zip --exact --accept-package-agreements --accept-source-agreements
    if ($LASTEXITCODE -ne 0) {
        throw "winget could not install 7-Zip. Exit code: $LASTEXITCODE."
    }

    $sevenZip = Find-7Zip
    if (-not $sevenZip) {
        throw '7-Zip was installed, but 7z.exe was still not found. Open a new terminal or install 7-Zip manually, then run this updater again.'
    }

    return $sevenZip
}

function Read-JsonFile {
    param([Parameter(Mandatory)][string]$Path)
    if (-not (Test-Path -LiteralPath $Path)) { return $null }
    return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}

function Get-Sha256 {
    param([Parameter(Mandatory)][string]$Path)

    $stream = [IO.File]::OpenRead($Path)
    $sha256 = [Security.Cryptography.SHA256]::Create()
    try {
        return ([BitConverter]::ToString($sha256.ComputeHash($stream))).Replace('-', '').ToLowerInvariant()
    }
    finally {
        $sha256.Dispose()
        $stream.Dispose()
    }
}

$targetEncoders = Join-Path $env:USERPROFILE 'encoders'
$localManifestPath = Join-Path $targetEncoders '.sendto.json'

Write-Host 'SendTo bootstrap updater'
Write-Host "Target encoder folder: $targetEncoders"
Write-Host "Manifest: $manifestUrl"
Write-Host ''

$remote = Invoke-RestMethod -Uri $manifestUrl -UseBasicParsing
$local = Read-JsonFile -Path $localManifestPath

$localVersion = if ($local -and $local.version) { [string]$local.version } else { 'unknown or pre-updater install' }
$remoteVersion = [string]$remote.version
$localSha = if ($local -and $local.sha256) { [string]$local.sha256 } else { '' }
$remoteSha = if ($remote.sha256) { [string]$remote.sha256 } else { '' }

Write-Host "Installed version: $localVersion"
Write-Host "Available version: $remoteVersion"

if ($localVersion -eq $remoteVersion -and (-not $localSha -or -not $remoteSha -or $localSha -eq $remoteSha)) {
    Write-Host 'SendTo is already up to date.'
    exit 0
}

Write-Host ''
Write-Host 'An install or update is available.'
if ($remote.notes) {
    Write-Host ''
    Write-Host 'Notes:'
    foreach ($note in $remote.notes) { Write-Host " - $note" }
}

$answer = Read-Host 'Download and install the current SendTo package now? Type Y to continue'
if ($answer -notmatch '^(?i:y(?:es)?)$') {
    Write-Host 'Update cancelled.'
    exit 0
}

$sevenZip = Ensure-7Zip

$tempRoot = Join-Path ([IO.Path]::GetTempPath()) ('SendToBootstrap-' + [guid]::NewGuid().ToString('N'))
$archivePath = Join-Path $tempRoot $remote.archiveName
$extractPath = Join-Path $tempRoot 'extracted'
New-Item -ItemType Directory -Force -Path $tempRoot, $extractPath | Out-Null

try {
    Write-Host "Downloading $($remote.archiveUrl)"
    Invoke-WebRequest -Uri $remote.archiveUrl -OutFile $archivePath -UseBasicParsing

    $actualSize = (Get-Item -LiteralPath $archivePath).Length
    if ($remote.size -and [int64]$remote.size -ne $actualSize) {
        throw "Downloaded size mismatch. Expected $($remote.size), got $actualSize."
    }

    if (-not $remoteSha) {
        throw 'The server manifest did not include a SHA-256 hash, so the update cannot be verified.'
    }

    $actualSha = Get-Sha256 -Path $archivePath
    if ($actualSha -ne $remoteSha.ToLowerInvariant()) {
        throw "Downloaded hash mismatch. Expected $remoteSha, got $actualSha."
    }

    Write-Host 'Extracting update...'
    & $sevenZip x '-y' "-o$extractPath" $archivePath | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "7-Zip extraction failed with exit code $LASTEXITCODE." }

    $installer = Join-Path $extractPath 'install-sendto.ps1'
    if (-not (Test-Path -LiteralPath $installer)) {
        $installer = Get-ChildItem -LiteralPath $extractPath -Recurse -Filter 'install-sendto.ps1' | Select-Object -First 1 -ExpandProperty FullName
    }
    if (-not $installer -or -not (Test-Path -LiteralPath $installer)) {
        throw 'Could not find install-sendto.ps1 in the downloaded package.'
    }

    Write-Host 'Installing current SendTo package...'
    & powershell -NoProfile -ExecutionPolicy Bypass -File $installer
    if ($LASTEXITCODE -ne 0) { throw "Installer failed with exit code $LASTEXITCODE." }

    New-Item -ItemType Directory -Force -Path $targetEncoders | Out-Null
    $remote | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $localManifestPath -Encoding UTF8

    Write-Host ''
    Write-Host 'SendTo install/update completed successfully.'
}
finally {
    Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
}
