#!/usr/bin/env sh
# ════════════════════════════════════════════════════════════════
#  git-nostr-sign  ·  bootstrap installer
#
#  curl -fsSL https://nostr-svrn.codeberg.page/install.sh | sh
#  (Forks: same pattern — HTTPS host must match your NIP-05 / .well-known/nostr.json)
#
#  Flags:
#    --npub  npub1...               publisher pubkey (or NIP-05 name@domain)
#    --relay wss://relay.damus.io   override default relay
#    --npub=… / --relay=…           same (safe if next token is another flag)
#    --help                         show usage and exit
#
#  Env overrides:
#    GNS_NPUB   GNS_RELAY
#
#  Trust model:
#    The PUBLISHER_NPUB below is the trust anchor — baked in at release time.
#    All package metadata comes from a Nostr event signed by that key.
#    The tarball is verified against the SHA-256 in that signed event.
#    No npm registry. No GitHub releases. No CDN.
# ════════════════════════════════════════════════════════════════
set -e

PUBLISHER_NPUB="${GNS_NPUB:-git-nostr-sign@nostr-svrn.codeberg.page}"
RELAY="${GNS_RELAY:-wss://relay.damus.io}"

# ── Colors ────────────────────────────────────────────────────────────────────
BOLD="\033[1m"; GREEN="\033[32m"; YELLOW="\033[33m"; RED="\033[31m"; RESET="\033[0m"
ok()   { printf "  ${GREEN}✓${RESET}  %s\n" "$1"; }
warn() { printf "  ${YELLOW}⚠${RESET}  %s\n" "$1"; }
die()  { printf "  ${RED}✗${RESET}  %s\n" "$1" >&2; exit 1; }
hdr()  { printf "\n${BOLD}%s${RESET}\n" "$1"; }

# ── Args ──────────────────────────────────────────────────────────────────────
usage() {
  printf '%s\n' "Usage: sh install.sh [options]" "" \
    "  --npub <npub1…|name@domain>   Publisher (default: baked-in NIP-05)" \
    "  --relay <wss://…>             Relay (default: wss://relay.damus.io)" \
    "  --npub=… / --relay=…          Same, avoids ambiguity with the next flag" \
    "" \
    "Env: GNS_NPUB  GNS_RELAY" \
    "Try: curl -fsSL …/install.sh | sh -s -- --help"
}

while [ $# -gt 0 ]; do
  case "$1" in
    --help|-h)
      usage
      exit 0
      ;;
    --npub=*)
      PUBLISHER_NPUB="${1#*=}"
      [ -n "$PUBLISHER_NPUB" ] || die "--npub value is empty"
      shift
      ;;
    --relay=*)
      RELAY="${1#*=}"
      [ -n "$RELAY" ] || die "--relay value is empty"
      shift
      ;;
    --npub)
      [ $# -lt 2 ] && die "--npub requires a value (npub1… or name@domain), or use --npub=…"
      case "$2" in
        -*) die "--npub requires a value before other flags (use: --npub=npub1… or --npub 'name@domain')" ;;
      esac
      PUBLISHER_NPUB="$2"
      shift 2
      ;;
    --relay)
      [ $# -lt 2 ] && die "--relay requires a value (wss://…), or use --relay=…"
      case "$2" in
        -*) die "--relay requires a value before other flags (use: --relay=wss://…)" ;;
      esac
      RELAY="$2"
      shift 2
      ;;
    *)
      die "Unknown option: $1 (try sh install.sh --help)"
      ;;
  esac
done

printf "\n${BOLD}╔══════════════════════════════════════════╗${RESET}\n"
printf "${BOLD}║      git-nostr-sign  ·  installer        ║${RESET}\n"
printf "${BOLD}╚══════════════════════════════════════════╝${RESET}\n\n"
printf "  Publisher: %s\n" "$PUBLISHER_NPUB"
printf "  Relay:     %s\n" "$RELAY"

# ── Dependencies ─────────────────────────────────────────────────────────────
hdr "Checking dependencies"
command -v node >/dev/null 2>&1 || die "Node.js 18+ required: https://nodejs.org"
if command -v pnpm >/dev/null 2>&1; then
  PKG_MGR=pnpm
elif command -v npm >/dev/null 2>&1; then
  PKG_MGR=npm
else
  die "pnpm or npm required (install Node.js from https://nodejs.org)"
fi
NODE_MAJOR=$(node -e "process.stdout.write(process.versions.node.split('.')[0])")
[ "$NODE_MAJOR" -lt 18 ] && die "Node.js 18+ required (you have $(node --version))"
ok "Node.js $(node --version)"
ok "Package manager: $PKG_MGR"

if command -v sha256sum >/dev/null 2>&1; then
  SHA_CMD="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
  SHA_CMD="shasum -a 256"
else
  die "sha256sum or shasum required"
fi
ok "SHA-256 available"

# ── Temp dir + npm deps (must run before NIP-05: dynamic import needs node_modules) ─
TMP_DIR=$(mktemp -d) || die "mktemp -d failed"
TMP_PKG="${TMP_DIR}/pkg-fetch"
mkdir -p "$TMP_PKG"
printf "  Installing temporary packages (nostr-tools, ws)…\n"
if [ "$PKG_MGR" = "pnpm" ]; then
  (cd "$TMP_PKG" && pnpm add nostr-tools ws >/dev/null 2>&1) || die "pnpm add nostr-tools ws failed"
else
  (cd "$TMP_PKG" && npm init -y >/dev/null 2>&1 && npm install nostr-tools ws >/dev/null 2>&1) || die "npm install nostr-tools ws failed"
fi
ok "Installer dependencies ready"
NIP19_ESM="${TMP_PKG}/node_modules/nostr-tools/lib/esm/nip19.js"

# ── Resolve NIP-05 if needed ─────────────────────────────────────────────────
if echo "$PUBLISHER_NPUB" | grep -q '@'; then
  hdr "Resolving NIP-05 address"
  NIP05_NAME=$(echo "$PUBLISHER_NPUB" | cut -d@ -f1)
  NIP05_DOMAIN=$(echo "$PUBLISHER_NPUB" | cut -d@ -f2)
  NIP05_URL="https://${NIP05_DOMAIN}/.well-known/nostr.json?name=${NIP05_NAME}"
  printf "  Fetching %s\n" "$NIP05_URL"
  NIP05_JSON=$(curl -fsSL "$NIP05_URL") || die "NIP-05 fetch failed"
  # JSON often has newlines; env vars cannot carry it reliably to Node on sh/bash → use a temp file
  NIP05_TMP=$(mktemp) || die "mktemp failed"
  printf '%s' "$NIP05_JSON" > "$NIP05_TMP" || die "NIP-05 temp write failed"
  PUBLISHER_NPUB=$(node -e "
const fs = require('fs');
const j = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
const name = process.argv[2];
const hex = j.names?.[name];
if (!hex || typeof hex !== 'string') { process.stderr.write('Name not found\\n'); process.exit(1); }
const raw = hex.trim().replace(/^0x/i, '');
if (!/^[0-9a-fA-F]{64}$/.test(raw)) { process.stderr.write('Invalid pubkey hex in nostr.json\\n'); process.exit(1); }
import('file://${NIP19_ESM}').then(m => { process.stdout.write(m.npubEncode(raw)); });
  " "$NIP05_TMP" "$NIP05_NAME") || die "Could not resolve NIP-05 name"
  rm -f "$NIP05_TMP"
  ok "Resolved to $PUBLISHER_NPUB"
fi

# ── Fetch latest kind 1063 release (NIP-94) ───────────────────────────────────
hdr "Fetching latest release from Nostr"

FETCH_RESULT=$(node --input-type=module <<EOF
import { decode }   from '${TMP_PKG}/node_modules/nostr-tools/lib/esm/nip19.js';
import WS           from '${TMP_PKG}/node_modules/ws/index.js';

const npub  = '${PUBLISHER_NPUB}';
const relay = '${RELAY}';

let d;
try {
  d = decode(npub);
} catch (e) {
  const msg = e && e.message ? String(e.message) : String(e);
  process.stderr.write(
    'Invalid publisher: expected npub1… (bech32) or use NIP-05 (user@domain) with --npub.\n' +
    'Details: ' + msg + '\n'
  );
  process.exit(1);
}
if (d.type !== 'npub') { process.stderr.write('Not an npub (got ' + d.type + ')\n'); process.exit(1); }
const pubkey = d.data;

const events = await new Promise((res, rej) => {
  const ws    = new WS(relay);
  const evts  = [];
  const t     = setTimeout(() => { ws.close(); res(evts); }, 8000);
  ws.on('open',    () => ws.send(JSON.stringify(['REQ','p',{kinds:[1063],authors:[pubkey],'#t':['git-nostr-sign'],limit:3}])));
  ws.on('message', d => {
    const m = JSON.parse(d.toString());
    if (m[0]==='EVENT') evts.push(m[2]);
    if (m[0]==='EOSE') { clearTimeout(t); ws.close(); res(evts); }
  });
  ws.on('error', rej);
});

if (!events.length) { process.stderr.write('No releases found\n'); process.exit(1); }
events.sort((a,b) => b.created_at - a.created_at);
const ev   = events[0];
const tag  = n => ev.tags.find(t=>t[0]===n)?.[1] ?? '';
const urls = ev.tags.filter(t=>t[0]==='url').map(t=>t[1]);
process.stdout.write([tag('version'), tag('x'), urls.join(','), ev.id].join('\t') + '\n');
EOF
)

rm -rf "$TMP_PKG"

VERSION=$(echo "$FETCH_RESULT" | cut -f1)
HASH=$(   echo "$FETCH_RESULT" | cut -f2)
URLS=$(   echo "$FETCH_RESULT" | cut -f3)
EVENT_ID=$(echo "$FETCH_RESULT" | cut -f4)

[ -z "$VERSION" ] && die "Could not parse release from Nostr"
ok "Version:  v${VERSION}"
ok "SHA-256:  ${HASH}"
ok "Event:    ${EVENT_ID}"

# ── Download from Blossom ─────────────────────────────────────────────────────
hdr "Downloading from Blossom"
TARBALL="${TMP_DIR}/git-nostr-sign.tgz"
downloaded=0

# POSIX field split (Alpine/BusyBox sh has no read -a / <<<)
_save_ifs=$IFS
IFS=,
for URL in $URLS; do
  printf "  Trying %s ... " "$URL"
  if curl -fsSL --max-time 30 -o "$TARBALL" "$URL" 2>/dev/null; then
    printf "ok\n"; downloaded=1; break
  else printf "failed\n"; fi
done
IFS=$_save_ifs
[ "$downloaded" = "1" ] || die "All mirrors failed"

# ── Verify SHA-256 ────────────────────────────────────────────────────────────
hdr "Verifying integrity"
ACTUAL=$($SHA_CMD "$TARBALL" | awk '{print $1}')
if [ "$ACTUAL" = "$HASH" ]; then
  ok "SHA-256 verified — matches signed Nostr event"
else
  rm -f "$TARBALL"
  die "HASH MISMATCH — download may be tampered!\n  Expected: $HASH\n  Got:      $ACTUAL"
fi

# ── Install ───────────────────────────────────────────────────────────────────
hdr "Installing"
if [ "$PKG_MGR" = "pnpm" ]; then
  pnpm add -g "$TARBALL"
else
  npm install -g "$TARBALL"
fi
rm -rf "$TMP_DIR"
ok "git-nostr-sign v${VERSION} installed"

# ── Done ──────────────────────────────────────────────────────────────────────
printf "\n${BOLD}══════════════════════════════════════════${RESET}\n"
printf "  ✅  Installed v%s\n" "$VERSION"
printf "  Verified via Nostr event: %s\n" "$EVENT_ID"
printf "\n  Running setup wizard...\n"
printf "${BOLD}══════════════════════════════════════════${RESET}\n\n"
git-nostr-setup
