#!/bin/sh
# Issue #82: the bootstrap installer. Meant to be fetched and run in one line:
#
#   curl -fsSL https://get.verityhq.app/dl/install.sh | sh -s -- VERITY-XXXXXXXX-XXXXXXXX
#
# `sh`, not `bash`: a fresh box is not guaranteed to have bash, and nothing here needs it.
# The hub endpoint below is a placeholder: .github/workflows/get-site.yml publishes a copy per channel
# with it replaced (get.verityhq.app -> https://hub.verityhq.app, get-dev -> https://hub-dev.verityhq.app).
# Moved here from verity-license/infra/installer/install.sh (verity-license#130) so the installer and
# this script ship from one repo and can no longer disagree about which hub they talk to.
set -eu

HUB_ENDPOINT="${VERITY_HUB_ENDPOINT:-https://hub.verityhq.app}"
INSTALL_DIR="${VERITY_INSTALL_DIR:-$PWD/verity}"

CLAIM_CODE="${1:-}"

fail() {
    echo "==> $1" >&2
    [ -n "${2:-}" ] && echo "    $2" >&2
    exit 1
}

info() {
    echo "==> $1"
}

# --- Pre-flight -------------------------------------------------------------
# Every check here fails with what's wrong and how to fix it, in plain language - the installer's whole
# purpose is that a non-technical customer never has to read a stack trace to get unstuck.

[ -n "$CLAIM_CODE" ] || fail "No claim code given." \
    "Usage: curl -fsSL https://get.verityhq.app/dl/install.sh | sh -s -- VERITY-XXXXXXXX-XXXXXXXX"

case "$CLAIM_CODE" in
    VERITY-????????-????????) : ;;
    *) fail "That doesn't look like a claim code." \
        "It should look like VERITY-XXXXXXXX-XXXXXXXX. Check what you were sent and try again." ;;
esac

command -v curl >/dev/null 2>&1 || fail "curl is not installed." \
    "Install it with your system's package manager (e.g. 'apt install curl' or 'yum install curl'), then re-run this command."

if ! command -v docker >/dev/null 2>&1; then
    fail "Docker is not installed." \
        "Install Docker first: https://docs.docker.com/engine/install/ - then re-run this command."
fi

if ! docker compose version >/dev/null 2>&1; then
    fail "Docker is installed, but the 'docker compose' plugin isn't." \
        "Install it: https://docs.docker.com/compose/install/linux/ - then re-run this command."
fi

if ! docker info >/dev/null 2>&1; then
    fail "Docker is installed but not usable from here." \
        "Either start the Docker service, or add your user to the 'docker' group and log back in."
fi

# 2 GB free is comfortably above what the compose stack (app + Postgres + cloudflared) needs at rest;
# below it, Postgres can fail to start in a way that looks nothing like a disk problem.
AVAILABLE_KB=$(df -Pk "$PWD" | awk 'NR==2 {print $4}')
if [ -n "$AVAILABLE_KB" ] && [ "$AVAILABLE_KB" -lt 2097152 ]; then
    fail "Less than 2 GB of free disk space here." \
        "Free up space or run this from a different location, then re-run this command."
fi

if ! curl -fsS --max-time 10 "$HUB_ENDPOINT/health" >/dev/null 2>&1; then
    fail "Can't reach $HUB_ENDPOINT." \
        "Check your internet connection. If it's fine, this hub may be down - contact support."
fi

[ -e "$INSTALL_DIR" ] && [ ! -d "$INSTALL_DIR" ] && fail "$INSTALL_DIR exists and isn't a directory." \
    "Move it aside or set VERITY_INSTALL_DIR to a different path, then re-run this command."

# --- Redeem -------------------------------------------------------------
# Idempotent-ish: a re-run with the SAME code against a host that already has files here just
# overwrites them with the same content redemption would have produced the first time (the code is
# still valid until it's actually redeemed) - re-running after a successful install fails cleanly here
# instead, since the code is single-use and was already consumed.

info "Redeeming claim code against $HUB_ENDPOINT..."

HTTP_STATUS=$(curl -fsS -o /tmp/verity-claim-response.json -w '%{http_code}' \
    --max-time 30 \
    -H 'Content-Type: application/json' \
    -d "{\"code\":\"$CLAIM_CODE\"}" \
    "$HUB_ENDPOINT/api/license/bootstrap/claim") || HTTP_STATUS="000"

if [ "$HTTP_STATUS" != "200" ]; then
    ERROR_MESSAGE=$(command -v jq >/dev/null 2>&1 && jq -r '.error // empty' /tmp/verity-claim-response.json 2>/dev/null || true)
    rm -f /tmp/verity-claim-response.json
    fail "This claim code was rejected." \
        "${ERROR_MESSAGE:-It may have expired or already been used. Ask whoever set this up for a fresh one.}"
fi

command -v jq >/dev/null 2>&1 || fail "jq is not installed." \
    "Install it with your system's package manager (e.g. 'apt install jq' or 'yum install jq'), then re-run this command with the same claim code."

mkdir -p "$INSTALL_DIR"
jq -r '.dockerComposeYaml' /tmp/verity-claim-response.json > "$INSTALL_DIR/docker-compose.yml"
jq -r '.envFile' /tmp/verity-claim-response.json > "$INSTALL_DIR/.env"
jq -r '.setupGuideMarkdown' /tmp/verity-claim-response.json > "$INSTALL_DIR/SETUP.md"
jq -r '.backupScript' /tmp/verity-claim-response.json > "$INSTALL_DIR/backup.sh"
chmod 600 "$INSTALL_DIR/.env"
chmod 755 "$INSTALL_DIR/backup.sh"
rm -f /tmp/verity-claim-response.json

info "Files written to $INSTALL_DIR"

# --- Bring it up -------------------------------------------------------------

info "Starting your deployment (this can take a minute)..."
( cd "$INSTALL_DIR" && docker compose up -d )

info "Waiting for it to come up..."
# Only postgres declares a HEALTHCHECK in the generated compose file, so "running" (not "healthy") is
# the completion signal every service actually reports - matching the wording in SETUP.md's own
# "docker compose ps" instructions.
ATTEMPT=0
EXPECTED_SERVICES=4 # app, cloudflared, postgres, backup
while [ "$ATTEMPT" -lt 60 ]; do
    RUNNING=$(cd "$INSTALL_DIR" && docker compose ps --status running --format '{{.Name}}' 2>/dev/null | wc -l | tr -d ' ')
    if [ "$RUNNING" -ge "$EXPECTED_SERVICES" ]; then
        break
    fi
    ATTEMPT=$((ATTEMPT + 1))
    sleep 5
done

if [ "$ATTEMPT" -ge 60 ]; then
    fail "Your deployment didn't come up within 5 minutes." \
        "Check with 'docker compose ps' in $INSTALL_DIR, or run 'docker compose logs' and contact support with the output."
fi

echo ""
echo "==> Done. Your deployment is starting up and will dial in to license itself automatically."
echo "    An invitation email is on its way to the administrator address given during setup."
echo "    Setup notes: $INSTALL_DIR/SETUP.md"
