#!/usr/bin/env bash
# One-line installer for Gaupa shop stack on Raspberry Pi (Linux arm64).
# Usage: curl -fsSL https://install.gaupa.network | bash
# Idempotent: each step skips gracefully if already done.
#
# IMPORTANT: install.gaupa.network MUST serve this full file (steps 1–14).
# If the one-liner stops after Step 4, run from the repo instead:
#   git clone https://github.com/GaupaLabs/gaupa-shop-package.git && cd gaupa-shop-package && ./scripts/bootstrap.sh
set -euo pipefail

# ── Config (override with env) ─────────────────────────────────────────────
CHIRPSTACK_DIR="${CHIRPSTACK_DIR:-$HOME/chirpstack-docker}"
REPO_ROOT="${REPO_ROOT:-$HOME/gaupa-shop-package}"
GAUPA_RELEASES_BASE="${GAUPA_RELEASES_BASE:-https://releases.gaupa.network}"
GAUPA_REPO_URL="https://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@github.com/GaupaLabs/gaupa-shop-package.git"
GAUPA_BRIDGE_REPO="${GAUPA_BRIDGE_REPO:-https://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@github.com/GaupaLabs/gaupa-bridge-service.git}"
GAUPA_DASHBOARD_REPO="${GAUPA_DASHBOARD_REPO:-https://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@github.com/GaupaLabs/gaupa-dashboard.git}"
TARGET_HOSTNAME="${TARGET_HOSTNAME:-gaupa}"
NODE_HOME_DEFAULT="$HOME/.gaupa-networkd"
CHIRPSTACK_DEFAULT_USER="admin"
CHIRPSTACK_DEFAULT_PASS="admin"

# ── Helpers ────────────────────────────────────────────────────────────────
log()  { echo "▶ $*"; }
ok()   { echo "  ✓ $*"; }
warn() { echo "  ⚠ $*"; }

# Run docker with sudo fallback (group membership not active in same session)
docker_cmd() {
  if docker info &>/dev/null 2>&1; then
    docker "$@"
  else
    sudo docker "$@"
  fi
}
dc() { docker_cmd compose "$@"; }

wait_for_url() {
  local url="$1" attempts="${2:-15}" interval="${3:-2}"
  for _ in $(seq 1 "$attempts"); do
    if curl -sf -o /dev/null "$url" 2>/dev/null; then return 0; fi
    sleep "$interval"
  done
  return 1
}

# ── Step 1: Prerequisites ──────────────────────────────────────────────────
log "Step 1: Prerequisites..."
[ "$(uname -s)" = "Linux" ] || { echo "Linux only." >&2; exit 1; }
ARCH=$(uname -m)
[ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ] || { echo "arm64 required, got: $ARCH" >&2; exit 1; }
command -v sudo  &>/dev/null || { echo "sudo required." >&2; exit 1; }
command -v curl  &>/dev/null || sudo apt-get install -y curl
command -v git   &>/dev/null || sudo apt-get install -y git
command -v jq    &>/dev/null || sudo apt-get install -y jq
command -v envsubst &>/dev/null || sudo apt-get install -y gettext-base
ok "Prerequisites satisfied"

# ── Step 2: Docker ─────────────────────────────────────────────────────────
log "Step 2: Docker..."
if ! command -v docker &>/dev/null; then
  curl -fsSL https://get.docker.com | sh
  ok "Docker installed"
else
  ok "Docker already installed"
fi
# Ensure current user can run docker without sudo (new group applies after re-login or: newgrp docker)
sudo usermod -aG docker "$USER" || true

# ── Step 3: Hostname + mDNS ────────────────────────────────────────────────
log "Step 3: Hostname..."
if [ "$(hostname -s 2>/dev/null)" != "$TARGET_HOSTNAME" ]; then
  sudo hostnamectl set-hostname "$TARGET_HOSTNAME"
  ok "Hostname set to $TARGET_HOSTNAME"
else
  ok "Hostname already $TARGET_HOSTNAME"
fi
if ! grep -qE "127\.0\.0\.1\s+$TARGET_HOSTNAME(\s|$)" /etc/hosts 2>/dev/null; then
  echo "127.0.0.1   $TARGET_HOSTNAME" | sudo tee -a /etc/hosts >/dev/null
fi
dpkg -l avahi-daemon &>/dev/null 2>&1 || sudo apt-get install -y avahi-daemon
sudo systemctl enable --now avahi-daemon 2>/dev/null || true
ok "$TARGET_HOSTNAME.local resolvable on local network"

# ── Step 4: Clone gaupa-shop-package ───────────────────────────────────────
# Clone first so .env exists before ChirpStack; user adds API key/app ID after Step 5.
log "Step 4: gaupa-shop-package..."
if [ -d "$REPO_ROOT/.git" ]; then
  (cd "$REPO_ROOT" && git pull --rebase 2>/dev/null || true)
  ok "Pulled latest"
else
  # If REPO_ROOT exists with only .env (user exited at step 6), preserve .env when cloning
  if [ -d "$REPO_ROOT" ] && [ -f "$REPO_ROOT/.env" ]; then
    cp "$REPO_ROOT/.env" /tmp/gaupa-shop-env.bak
    rm -rf "$REPO_ROOT"
  fi
  git clone --depth 1 "$GAUPA_REPO_URL" "$REPO_ROOT"
  if [ -f /tmp/gaupa-shop-env.bak ]; then
    mv /tmp/gaupa-shop-env.bak "$REPO_ROOT/.env"
  fi
  ok "Cloned gaupa-shop-package"
fi
# Ensure .env exists before ChirpStack so user can add API key/app ID when prompted
[ -f "$REPO_ROOT/.env" ] || cp "$REPO_ROOT/.env.example" "$REPO_ROOT/.env"
ok ".env ready"

# ── Step 5: ChirpStack ─────────────────────────────────────────────────────
log "Step 5: ChirpStack..."
if [ ! -d "$CHIRPSTACK_DIR/.git" ]; then
  git clone --depth 1 https://github.com/chirpstack/chirpstack-docker.git "$CHIRPSTACK_DIR"
  ok "Cloned chirpstack-docker"
else
  ok "chirpstack-docker already present"
fi
cd "$CHIRPSTACK_DIR"
dc up -d
ok "ChirpStack running"

# ── Step 6: ChirpStack API key + application ───────────────────────────────
# Credentials must be created in the ChirpStack UI and set in .env before continuing.
log "Step 6: ChirpStack API key + application..."
_chirpstack_key_valid() { [ -n "$1" ] && ! echo "$1" | grep -qE 'timeout|error|failed|No such file|docker_cmd'; }
_chirpstack_app_id_valid() { [ -n "$1" ] && echo "$1" | grep -qE '^[0-9a-fA-F-]{30,}$'; }

CHIRPSTACK_API_KEY=""
CHIRPSTACK_APPLICATION_ID=""
if [ -f "$REPO_ROOT/.env" ]; then
  _env_key=$(grep -E '^CHIRPSTACK_API_KEY=.+' "$REPO_ROOT/.env" 2>/dev/null | cut -d= -f2- | head -1) || true
  _env_app=$(grep -E '^CHIRPSTACK_APPLICATION_ID=.+' "$REPO_ROOT/.env" 2>/dev/null | cut -d= -f2- | head -1) || true
  if _chirpstack_key_valid "$_env_key" && _chirpstack_app_id_valid "$_env_app"; then
    CHIRPSTACK_API_KEY="$_env_key"
    CHIRPSTACK_APPLICATION_ID="$_env_app"
    ok "ChirpStack API key and application ID found in .env"
  fi
fi

if [ -z "$CHIRPSTACK_API_KEY" ] || [ -z "$CHIRPSTACK_APPLICATION_ID" ]; then
  echo ""
  echo "  ┌─ ChirpStack: create credentials in the UI ─────────────────────────────"
  echo "  │"
  echo "  │  Add your ChirpStack API key and application ID to .env, then re-run."
  echo "  │"
  echo "  │  1. Open ChirpStack UI: http://$TARGET_HOSTNAME.local:8080  (or http://<this-machine-ip>:8080)"
  echo "  │     Log in with admin / admin."
  echo "  │"
  echo "  │  2. Create an API key: user menu (top right) → API keys → Add API key."
  echo "  │     Name it e.g. gaupa-bridge, create, then copy the token (shown once)."
  echo "  │"
  echo "  │  3. Create an application: Applications → Add application."
  echo "  │     Name it e.g. gaupa-sensors, create, then copy the application ID."
  echo "  │"
  echo "  │  4. Put them in .env and re-run:"
  echo "  │     echo 'CHIRPSTACK_API_KEY=<token>' >> $REPO_ROOT/.env"
  echo "  │     echo 'CHIRPSTACK_APPLICATION_ID=<application-id>' >> $REPO_ROOT/.env"
  echo "  │     cd $REPO_ROOT && ./scripts/bootstrap.sh   # or: curl -fsSL https://install.gaupa.network | bash"
  echo "  │"
  echo "  └────────────────────────────────────────────────────────────────────"
  echo ""
  exit 0
fi

# ── Step 7: Download gaupa-networkd-arm64 binary ──────────────────────────
log "Step 7: Node binary..."
BINARY_PATH="$REPO_ROOT/gaupa-networkd-arm64"
BINARY_URL="$GAUPA_RELEASES_BASE/gaupa-networkd-arm64"
SHA_URL="$GAUPA_RELEASES_BASE/gaupa-networkd-arm64.sha256"

NEED_DL=1
if [ -f "$BINARY_PATH" ]; then
  WANT=$(curl -fsSL "$SHA_URL" 2>/dev/null | awk '{print $1}' || true)
  if [ -z "$WANT" ]; then
    # No release checksum available (e.g. manual binary drop) — trust what's there
    NEED_DL=0; ok "Binary present (no remote checksum to verify against)"
  else
    GOT=$(sha256sum "$BINARY_PATH" 2>/dev/null | awk '{print $1}' || true)
    if [ "$WANT" = "$GOT" ]; then
      NEED_DL=0; ok "Binary already up to date"
    fi
  fi
fi
if [ "$NEED_DL" -eq 1 ]; then
  curl -fsSL "$BINARY_URL" -o "$BINARY_PATH"
  if curl -fsSL "$SHA_URL" -o /tmp/gaupa.sha256 2>/dev/null; then
    echo "$(awk '{print $1}' /tmp/gaupa.sha256)  $BINARY_PATH" > /tmp/gaupa-check.sha256
    sha256sum -c /tmp/gaupa-check.sha256 || { echo "sha256 mismatch — aborting." >&2; rm -f "$BINARY_PATH"; exit 1; }
  fi
  chmod +x "$BINARY_PATH"
  ok "Binary downloaded and verified"
fi
ln -sf "$BINARY_PATH" "$REPO_ROOT/gaupa-networkd" 2>/dev/null || true

# ── Step 8: Docker images ──────────────────────────────────────────────────
log "Step 8: Docker images..."
PARENT_DIR="$(dirname "$REPO_ROOT")"

build_if_missing() {
  local image="$1" repo_url="$2" repo_dir="$3"
  if docker_cmd image inspect "${image}:latest" &>/dev/null 2>&1; then
    ok "${image}:latest already present"
    return
  fi
  log "  Building ${image} from source..."
  if [ ! -d "$repo_dir/.git" ]; then
    git clone --depth 1 "$repo_url" "$repo_dir"
  fi
  # gaupa-dashboard: avoid frozen-lockfile failure when lockfile is out of sync with package.json (e.g. different Bun version)
  if [ "$image" = "gaupa-dashboard" ] && [ -f "$repo_dir/Dockerfile" ]; then
    sed -i 's/bun install --frozen-lockfile/bun install/' "$repo_dir/Dockerfile" || true
  fi
  docker_cmd build -t "${image}:latest" "$repo_dir"
  ok "${image} built"
}

build_if_missing "gaupa-bridge"    "$GAUPA_BRIDGE_REPO"    "$PARENT_DIR/gaupa-bridge-service"
build_if_missing "gaupa-dashboard" "$GAUPA_DASHBOARD_REPO" "$PARENT_DIR/gaupa-dashboard"

# ── Step 9: Write .env ─────────────────────────────────────────────────────
log "Step 9: .env..."
cd "$REPO_ROOT"
[ -f .env ] || cp .env.example .env

_env_set() {
  local key="$1" val="$2"
  if grep -q "^${key}=" .env 2>/dev/null; then
    sed -i "s|^${key}=.*|${key}=${val}|" .env
  else
    echo "${key}=${val}" >> .env
  fi
}

_env_set "NODE_HOME" "$NODE_HOME_DEFAULT"
_env_set "GAUPA_BINARY_URL" "$BINARY_URL"
# Ensure Postgres has a password (empty in .env can break container start when using sudo docker compose)
grep -qE '^POSTGRES_PASSWORD=.' .env 2>/dev/null || _env_set "POSTGRES_PASSWORD" "gaupa"
ok ".env written"

# ── Step 10: Init node ─────────────────────────────────────────────────────
log "Step 10: Init node..."
export NODE_HOME="$NODE_HOME_DEFAULT"
export PATH="$REPO_ROOT:$PATH"
cd "$REPO_ROOT"
[ -f .env ] && set -a && . .env && set +a
CHAIN_ID="${CHAIN_ID:-gaupa-network-1}"
GENESIS_URL="${GENESIS_URL:-https://releases.gaupa.network/genesis.json}"
if [ -d "$NODE_HOME/config" ] && [ -f "$NODE_HOME/config/genesis.json" ]; then
  ok "Node already initialized at $NODE_HOME"
else
  mkdir -p "$NODE_HOME"
  # Pass moniker as literal so we never get "accepts 1 arg(s), received 0" (no dependency on init-node.sh)
  "$REPO_ROOT/gaupa-networkd" init gaupa-shop --chain-id "$CHAIN_ID" --home "$NODE_HOME"
  curl -fsSL "$GENESIS_URL" -o "$NODE_HOME/config/genesis.json"
  ok "Node initialized"
fi
# Run init-node.sh for idempotent no-op when already initialized (keeps script in sync)
"$REPO_ROOT/scripts/init-node.sh" "gaupa-shop" 2>/dev/null || true

# ── Step 11: Configure node ────────────────────────────────────────────────
log "Step 11: Configure node..."
"$REPO_ROOT/scripts/configure-node.sh"

# ── Step 12: systemd gaupa-node ───────────────────────────────────────────
log "Step 12: systemd gaupa-node..."
export REPO_ROOT USER NODE_HOME="$NODE_HOME_DEFAULT"
envsubst '${USER} ${REPO_ROOT} ${NODE_HOME}' \
  < "$REPO_ROOT/systemd/gaupa-node.service" \
  | sudo tee /etc/systemd/system/gaupa-node.service >/dev/null
sudo systemctl daemon-reload
sudo systemctl enable --now gaupa-node
ok "gaupa-node service installed and started"

# ── Step 13: systemd gaupa-update ─────────────────────────────────────────
log "Step 13: systemd gaupa-update..."
if [ -f "$REPO_ROOT/systemd/gaupa-update.service" ]; then
  envsubst '${USER} ${REPO_ROOT}' \
    < "$REPO_ROOT/systemd/gaupa-update.service" \
    | sudo tee /etc/systemd/system/gaupa-update.service >/dev/null
  sudo systemctl daemon-reload
  sudo systemctl enable gaupa-update.service
  ok "gaupa-update service installed"
fi

# ── Step 14: Start Docker stack ───────────────────────────────────────────
log "Step 14: Docker stack..."
cd "$REPO_ROOT"
# gaupa stack expects chirpstack_default; chirpstack-docker uses its own project network.
# Create chirpstack_default and attach ChirpStack containers with aliases so dashboard/bridge
# can resolve chirpstack-rest-api and chirpstack by name.
docker_cmd network inspect chirpstack_default &>/dev/null 2>&1 || docker_cmd network create chirpstack_default
for svc in chirpstack chirpstack-rest-api; do
  cid=$(docker_cmd compose -f "$CHIRPSTACK_DIR/docker-compose.yml" --project-directory "$CHIRPSTACK_DIR" ps -q "$svc" 2>/dev/null || true)
  if [ -n "$cid" ]; then
    docker_cmd network disconnect chirpstack_default "$cid" 2>/dev/null || true
    case "$svc" in
      chirpstack-rest-api) docker_cmd network connect --alias chirpstack-rest-api chirpstack_default "$cid" ;;
      chirpstack)         docker_cmd network connect --alias chirpstack chirpstack_default "$cid" ;;
      *)                 docker_cmd network connect chirpstack_default "$cid" ;;
    esac
  fi
done
dc up -d
log "  Waiting for dashboard..."
if wait_for_url "http://127.0.0.1:3000" 20 3; then
  ok "Dashboard is up"
else
  warn "Dashboard not responding yet. If gaupa-postgres is unhealthy:"
  warn "  cd $REPO_ROOT && docker compose down -v && docker compose up -d"
  warn "  (-v removes the postgres volume so it can reinit with the default password)"
fi

# ── Done ───────────────────────────────────────────────────────────────────
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "  → Open http://$TARGET_HOSTNAME.local:3000 to complete setup"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
