#!/usr/bin/env bash
# RHA Provider Vault — macOS / Linux plugin patcher
#
# LOCKSTEP: keep LOGIC_VERSION and behavior identical to Windows-Plugin-Patcher.ps1.
# Self-update safe: this script is already loaded into the shell's 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.
# No sudo, no telemetry.

set -euo pipefail

LOGIC_VERSION="1"
CONF="${HOME}/.rha-vault-install.conf"
DOWNLOAD_URL="https://rha.software/api/extension.zip"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

say() { printf '%s\n' "$*"; }
fail() { say "FAILED at $1: $2"; exit 1; }

# --- permanent install directory ------------------------------------------------
load_dir() {
  if [[ -f "$CONF" ]]; then
    # shellcheck disable=SC1090
    # conf is a single KEY=value line we own
    INST_DIR="$(grep -E '^INSTALL_DIR=' "$CONF" 2>/dev/null | head -1 | cut -d= -f2- || true)"
  else
    INST_DIR=""
  fi
}

save_dir() {
  printf 'INSTALL_DIR=%s\n' "$1" > "$CONF"
  say "Saved permanent plugin directory → $1 (in $CONF)"
}

ensure_dir() {
  load_dir
  if [[ -n "${INST_DIR:-}" && -d "$INST_DIR" ]]; then
    say "Permanent plugin directory: $INST_DIR"
    printf 'Keep this directory? [Y/n] '
    read -r ans || true
    if [[ "${ans:-Y}" =~ ^[Nn] ]]; then
      INST_DIR=""
    fi
  fi
  if [[ -z "${INST_DIR:-}" ]]; then
    say "First run — choose the PERMANENT plugin directory (the unpacked extension folder)."
    say "Example: $HOME/rha-provider-vault"
    printf 'Path: '
    read -r INST_DIR
    [[ -n "$INST_DIR" ]] || fail "choose-dir" "empty path"
    INST_DIR="${INST_DIR/#\~/$HOME}"
    mkdir -p "$INST_DIR"
    save_dir "$INST_DIR"
  fi
}

# --- zip discovery --------------------------------------------------------------
find_candidates() {
  local -a found=()
  local d f
  for d in "$SCRIPT_DIR" "$HOME/Downloads"; do
    [[ -d "$d" ]] || continue
    while IFS= read -r -d '' f; do
      found+=("$f")
    done < <(find "$d" -maxdepth 1 -type f \( -iname 'rha-provider-vault*.zip' -o -iname 'rhawrapper*.zip' -o -iname 'extension.zip' \) -print0 2>/dev/null || true)
  done
  if ((${#found[@]} == 0)); then
    return 1
  fi
  # newest mtime first
  printf '%s\n' "${found[@]}" | xargs ls -t 2>/dev/null || printf '%s\n' "${found[@]}"
}

pick_local_zip() {
  say "Scanning $SCRIPT_DIR and ~/Downloads for rha-provider-vault*.zip / rhawrapper*.zip …"
  local list
  list="$(find_candidates)" || fail "scan-zips" "no candidate zip found"
  local -a arr=()
  while IFS= read -r line; do
    [[ -n "$line" ]] && arr+=("$line")
  done <<< "$list"
  say "Candidates (newest first):"
  local i=1
  for f in "${arr[@]}"; do
    local mt
    mt="$(date -r "$f" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || stat -c '%y' "$f" 2>/dev/null || echo '?')"
    say "  [$i] $f  ($mt)"
    i=$((i + 1))
  done
  local newest="${arr[0]}"
  say "Auto-selecting newest: $newest"
  printf 'Press Enter to accept, or type a number / full path: '
  read -r choice || true
  if [[ -z "${choice:-}" ]]; then
    ZIP_PATH="$newest"
  elif [[ "$choice" =~ ^[0-9]+$ ]]; then
    local idx=$((choice - 1))
    [[ $idx -ge 0 && $idx -lt ${#arr[@]} ]] || fail "pick-zip" "index out of range"
    ZIP_PATH="${arr[$idx]}"
  else
    ZIP_PATH="${choice/#\~/$HOME}"
    [[ -f "$ZIP_PATH" ]] || fail "pick-zip" "not a file: $ZIP_PATH"
  fi
  say "Chosen zip: $ZIP_PATH"
}

download_fresh() {
  local tmp
  tmp="$(mktemp -t rha-ext.XXXXXX.zip)"
  say "Downloading $DOWNLOAD_URL → $tmp"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$DOWNLOAD_URL" -o "$tmp" || fail "download" "curl failed"
  elif command -v wget >/dev/null 2>&1; then
    wget -qO "$tmp" "$DOWNLOAD_URL" || fail "download" "wget failed"
  else
    fail "download" "need curl or wget"
  fi
  ZIP_PATH="$tmp"
  CLEANUP_ZIP="$tmp"
  say "Download complete ($(wc -c < "$ZIP_PATH" | tr -d ' ') bytes)"
}

# --- atomic patch ---------------------------------------------------------------
read_manifest_version() {
  local dir="$1"
  local m="$dir/manifest.json"
  [[ -f "$m" ]] || { echo ""; return; }
  # Prefer python/node for JSON; fall back to a crude grep.
  if command -v python3 >/dev/null 2>&1; then
    python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("version",""))' "$m" 2>/dev/null || true
  elif command -v node >/dev/null 2>&1; then
    node -e 'console.log(JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).version||"")' "$m" 2>/dev/null || true
  else
    grep -oE '"version"[[:space:]]*:[[:space:]]*"[^"]+"' "$m" | head -1 | sed -E 's/.*"([^"]+)"/\1/' || true
  fi
}

atomic_patch() {
  local zip="$1"
  local dest="$INST_DIR"
  local staging="${dest}.new"
  local old_ver new_ver

  say "Preparing staging directory: $staging"
  rm -rf "$staging"
  mkdir -p "$staging"

  say "Extracting zip into staging…"
  if command -v unzip >/dev/null 2>&1; then
    unzip -q "$zip" -d "$staging" || fail "extract" "unzip failed"
  else
    fail "extract" "unzip not found"
  fi

  # Zip may contain a single top-level folder — flatten if so.
  local entries
  entries="$(find "$staging" -mindepth 1 -maxdepth 1 | wc -l | tr -d ' ')"
  if [[ "$entries" == "1" && -d "$(find "$staging" -mindepth 1 -maxdepth 1)" ]]; then
    local only
    only="$(find "$staging" -mindepth 1 -maxdepth 1)"
    if [[ ! -f "$staging/manifest.json" && -f "$only/manifest.json" ]]; then
      say "Flattening single top-level folder: $(basename "$only")"
      # move contents up
      shopt -s dotglob
      mv "$only"/* "$staging"/
      rmdir "$only" 2>/dev/null || rm -rf "$only"
      shopt -u dotglob
    fi
  fi

  [[ -f "$staging/manifest.json" ]] || fail "verify" "manifest.json missing after extract"

  old_ver="$(read_manifest_version "$dest")"
  new_ver="$(read_manifest_version "$staging")"
  [[ -n "$new_ver" ]] || fail "verify" "could not read new manifest version"
  say "Version: ${old_ver:-'(none)'} → $new_ver"

  # Copy patcher scripts into permanent dir (self-update; running script already in memory).
  if [[ -f "$staging/MacOS-Linux-Plugin-Patcher.sh" ]]; then
    say "Refreshing patcher scripts beside the plugin dir…"
    cp -f "$staging/MacOS-Linux-Plugin-Patcher.sh" "$dest/../MacOS-Linux-Plugin-Patcher.sh" 2>/dev/null || \
      cp -f "$staging/MacOS-Linux-Plugin-Patcher.sh" "$SCRIPT_DIR/MacOS-Linux-Plugin-Patcher.sh" 2>/dev/null || true
    cp -f "$staging/MacOS-Linux-Plugin-Patcher.sh" "$staging/MacOS-Linux-Plugin-Patcher.sh" 2>/dev/null || true
  fi
  # Also ensure both patchers land inside the permanent plugin directory itself.
  for s in MacOS-Linux-Plugin-Patcher.sh Windows-Plugin-Patcher.ps1; do
    if [[ -f "$staging/$s" ]]; then
      say "  keeping $s inside plugin tree"
    fi
  done

  say "Atomic swap: $staging → $dest"
  local backup="${dest}.old.$$"
  if [[ -d "$dest" ]]; then
    mv "$dest" "$backup" || fail "swap" "could not move old dir aside"
  fi
  mv "$staging" "$dest" || {
    # try restore
    [[ -d "$backup" ]] && mv "$backup" "$dest"
    fail "swap" "could not move staging into place"
  }
  if [[ -d "$backup" ]]; then
    say "Removing previous install backup…"
    rm -rf "$backup"
  fi

  # Drop copies of both patchers next to (and inside) the permanent dir for next time.
  for s in MacOS-Linux-Plugin-Patcher.sh Windows-Plugin-Patcher.ps1; do
    if [[ -f "$dest/$s" ]]; then
      cp -f "$dest/$s" "$(dirname "$dest")/$s" 2>/dev/null || true
    fi
  done

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

# --- main menu ------------------------------------------------------------------
say "RHA Provider Vault patcher (macOS/Linux) — logic v$LOGIC_VERSION"
ensure_dir

say ""
say "Menu:"
say "  1) Patch from a local zip"
say "  2) Download fresh and patch"
printf 'Choice [1/2]: '
read -r choice || true

CLEANUP_ZIP=""
case "${choice:-1}" in
  2) download_fresh ;;
  1|*) pick_local_zip ;;
esac

atomic_patch "$ZIP_PATH"

if [[ -n "${CLEANUP_ZIP:-}" && -f "$CLEANUP_ZIP" ]]; then
  rm -f "$CLEANUP_ZIP"
fi

exit 0
