# RHA Provider Vault — Windows plugin patcher
#
# LOCKSTEP: keep LOGIC_VERSION and behavior identical to MacOS-Linux-Plugin-Patcher.sh.
# Self-update safe: PowerShell has already read this script into memory; writing a
# newer copy of this file (from the zip) into the permanent dir is fine.
#
# Writes only: the chosen plugin directory (+ .new staging), and ~/.rha-vault-install.conf
# (under the user profile). No elevation, no telemetry.

$ErrorActionPreference = 'Stop'
$LOGIC_VERSION = '1'
$ConfPath = Join-Path $HOME '.rha-vault-install.conf'
$DownloadUrl = 'https://rha.software/api/extension.zip'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path

function Say([string]$msg) { Write-Host $msg }
function Fail([string]$step, [string]$reason) {
  Say "FAILED at ${step}: $reason"
  exit 1
}

function Get-InstallDir {
  if (Test-Path -LiteralPath $ConfPath) {
    $line = Get-Content -LiteralPath $ConfPath | Where-Object { $_ -match '^INSTALL_DIR=' } | Select-Object -First 1
    if ($line) { return $line.Substring('INSTALL_DIR='.Length) }
  }
  return $null
}

function Save-InstallDir([string]$dir) {
  Set-Content -LiteralPath $ConfPath -Value "INSTALL_DIR=$dir" -Encoding utf8
  Say "Saved permanent plugin directory → $dir (in $ConfPath)"
}

function Ensure-Dir {
  $script:InstDir = Get-InstallDir
  if ($InstDir -and (Test-Path -LiteralPath $InstDir -PathType Container)) {
    Say "Permanent plugin directory: $InstDir"
    $ans = Read-Host 'Keep this directory? [Y/n]'
    if ($ans -match '^[Nn]') { $script:InstDir = $null }
  }
  if (-not $InstDir) {
    Say "First run — choose the PERMANENT plugin directory (the unpacked extension folder)."
    Say "Example: $HOME\rha-provider-vault"
    $script:InstDir = Read-Host 'Path'
    if (-not $InstDir) { Fail 'choose-dir' 'empty path' }
    New-Item -ItemType Directory -Force -Path $InstDir | Out-Null
    Save-InstallDir $InstDir
  }
}

function Find-Candidates {
  $names = @('rha-provider-vault*.zip', 'rhawrapper*.zip', 'extension.zip')
  $dirs = @($ScriptDir, (Join-Path $HOME 'Downloads'))
  $found = @()
  foreach ($d in $dirs) {
    if (-not (Test-Path -LiteralPath $d)) { continue }
    foreach ($pat in $names) {
      $found += Get-ChildItem -LiteralPath $d -Filter $pat -File -ErrorAction SilentlyContinue
    }
  }
  return @($found | Sort-Object LastWriteTime -Descending | Select-Object -Unique)
}

function Pick-LocalZip {
  Say "Scanning $ScriptDir and ~/Downloads for rha-provider-vault*.zip / rhawrapper*.zip …"
  $arr = @(Find-Candidates)
  if ($arr.Count -eq 0) { Fail 'scan-zips' 'no candidate zip found' }
  Say 'Candidates (newest first):'
  for ($i = 0; $i -lt $arr.Count; $i++) {
    $f = $arr[$i]
    Say ("  [{0}] {1}  ({2})" -f ($i + 1), $f.FullName, $f.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss'))
  }
  $newest = $arr[0].FullName
  Say "Auto-selecting newest: $newest"
  $choice = Read-Host 'Press Enter to accept, or type a number / full path'
  if ([string]::IsNullOrWhiteSpace($choice)) {
    $script:ZipPath = $newest
  } elseif ($choice -match '^\d+$') {
    $idx = [int]$choice - 1
    if ($idx -lt 0 -or $idx -ge $arr.Count) { Fail 'pick-zip' 'index out of range' }
    $script:ZipPath = $arr[$idx].FullName
  } else {
    if (-not (Test-Path -LiteralPath $choice -PathType Leaf)) { Fail 'pick-zip' "not a file: $choice" }
    $script:ZipPath = $choice
  }
  Say "Chosen zip: $ZipPath"
}

function Download-Fresh {
  $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("rha-ext-{0}.zip" -f [guid]::NewGuid().ToString('N').Substring(0, 8))
  Say "Downloading $DownloadUrl → $tmp"
  try {
    Invoke-WebRequest -Uri $DownloadUrl -OutFile $tmp -UseBasicParsing
  } catch {
    Fail 'download' $_.Exception.Message
  }
  $script:ZipPath = $tmp
  $script:CleanupZip = $tmp
  $bytes = (Get-Item -LiteralPath $tmp).Length
  Say "Download complete ($bytes bytes)"
}

function Read-ManifestVersion([string]$dir) {
  $m = Join-Path $dir 'manifest.json'
  if (-not (Test-Path -LiteralPath $m)) { return '' }
  try {
    $j = Get-Content -LiteralPath $m -Raw | ConvertFrom-Json
    return [string]$j.version
  } catch {
    return ''
  }
}

function Expand-ZipFlat([string]$zip, [string]$staging) {
  if (Test-Path -LiteralPath $staging) { Remove-Item -LiteralPath $staging -Recurse -Force }
  New-Item -ItemType Directory -Force -Path $staging | Out-Null
  Say "Extracting zip into staging…"
  Expand-Archive -LiteralPath $zip -DestinationPath $staging -Force
  $children = @(Get-ChildItem -LiteralPath $staging -Force)
  if ($children.Count -eq 1 -and $children[0].PSIsContainer) {
    $only = $children[0].FullName
    $topManifest = Join-Path $staging 'manifest.json'
    $innerManifest = Join-Path $only 'manifest.json'
    if (-not (Test-Path -LiteralPath $topManifest) -and (Test-Path -LiteralPath $innerManifest)) {
      Say ("Flattening single top-level folder: {0}" -f $children[0].Name)
      Get-ChildItem -LiteralPath $only -Force | ForEach-Object {
        Move-Item -LiteralPath $_.FullName -Destination $staging -Force
      }
      Remove-Item -LiteralPath $only -Recurse -Force
    }
  }
}

function Atomic-Patch([string]$zip) {
  $dest = $InstDir
  $staging = "$dest.new"
  Say "Preparing staging directory: $staging"
  Expand-ZipFlat $zip $staging

  if (-not (Test-Path -LiteralPath (Join-Path $staging 'manifest.json'))) {
    Fail 'verify' 'manifest.json missing after extract'
  }

  $oldVer = Read-ManifestVersion $dest
  $newVer = Read-ManifestVersion $staging
  if (-not $newVer) { Fail 'verify' 'could not read new manifest version' }
  $oldLabel = if ($oldVer) { $oldVer } else { '(none)' }
  Say "Version: $oldLabel → $newVer"

  foreach ($s in @('MacOS-Linux-Plugin-Patcher.sh', 'Windows-Plugin-Patcher.ps1')) {
    $p = Join-Path $staging $s
    if (Test-Path -LiteralPath $p) { Say "  keeping $s inside plugin tree" }
  }

  Say "Atomic swap: $staging → $dest"
  $backup = "$dest.old.$PID"
  if (Test-Path -LiteralPath $dest) {
    try {
      Move-Item -LiteralPath $dest -Destination $backup -Force
    } catch {
      Fail 'swap' "could not move old dir aside: $($_.Exception.Message)"
    }
  }
  try {
    Move-Item -LiteralPath $staging -Destination $dest -Force
  } catch {
    if (Test-Path -LiteralPath $backup) { Move-Item -LiteralPath $backup -Destination $dest -Force }
    Fail 'swap' "could not move staging into place: $($_.Exception.Message)"
  }
  if (Test-Path -LiteralPath $backup) {
    Say 'Removing previous install backup…'
    Remove-Item -LiteralPath $backup -Recurse -Force
  }

  $parent = Split-Path -Parent $dest
  foreach ($s in @('MacOS-Linux-Plugin-Patcher.sh', 'Windows-Plugin-Patcher.ps1')) {
    $src = Join-Path $dest $s
    if (Test-Path -LiteralPath $src) {
      try { Copy-Item -LiteralPath $src -Destination (Join-Path $parent $s) -Force } catch { }
    }
  }

  Say 'SUCCESS — reload the extension at chrome://extensions (⟳). Never reload mid-run.'
}

# --- main -----------------------------------------------------------------------
Say "RHA Provider Vault patcher (Windows) — logic v$LOGIC_VERSION"
Ensure-Dir

Say ''
Say 'Menu:'
Say '  1) Patch from a local zip'
Say '  2) Download fresh and patch'
$choice = Read-Host 'Choice [1/2]'

$script:CleanupZip = $null
if ($choice -eq '2') {
  Download-Fresh
} else {
  Pick-LocalZip
}

Atomic-Patch $ZipPath

if ($CleanupZip -and (Test-Path -LiteralPath $CleanupZip)) {
  Remove-Item -LiteralPath $CleanupZip -Force
}

exit 0
