#!/usr/bin/env bash
# Deploy doctate-server to a remote Docker host (default: minerva.lan).
# Idempotent: re-runs preserve operator-owned config, data, logs.
#
# Usage: ./scripts/deploy-server.sh [--force|-f] [host]
#   --force, -f  build even if the working tree is dirty
#   host         default: minerva.lan
#
# See docs/deployment.md for first-time bootstrap.

set -euo pipefail

FORCE=0
HOST=""
while [[ $# -gt 0 ]]; do
    case "$1" in
        --force|-f) FORCE=1 ;;
        -h|--help)
            sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'
            exit 0
            ;;
        -*) echo "Unknown flag: $1" >&2; exit 2 ;;
        *)  HOST="$1" ;;
    esac
    shift
done
HOST="${HOST:-minerva.lan}"

REMOTE="/opt/stacks/doctate-server"
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
GIT_SHA="$(cd "$REPO_ROOT" && git rev-parse --short HEAD)"
IMAGE_LATEST="doctate-server:latest"
IMAGE_TAGGED="doctate-server:$GIT_SHA"

cd "$REPO_ROOT"

# ─── Phase 0 ─── local pre-flight
if [[ -n "$(git -C "$REPO_ROOT" status --porcelain)" ]]; then
    if (( FORCE )); then
        echo "    WARNING: working tree is dirty — building from on-disk state, not from $GIT_SHA (--force given)" >&2
    else
        echo "ERROR: working tree is dirty. Commit/stash your changes, or pass --force to deploy anyway." >&2
        echo >&2
        git -C "$REPO_ROOT" status --short >&2
        exit 1
    fi
fi

echo ">>> [0/10] cargo fmt --check (server)"
( cd server && cargo fmt --check )

# ─── Phase 1 ─── remote bootstrap (idempotent)
echo ">>> [1/10] Ensuring remote layout at $HOST:$REMOTE"
ssh "$HOST" "mkdir -p $REMOTE/config $REMOTE/data $REMOTE/logs"

# ─── Phase 2 ─── seed templates only if missing
echo ">>> [2/10] Seeding config templates (only if missing)"
for pair in \
    ".env.example:.env" \
    "settings.toml.example:settings.toml" \
    "users.toml.example:users.toml"
do
    src="server/${pair%%:*}"
    dst="${pair##*:}"
    remote_path="$REMOTE/config/$dst"
    if ssh "$HOST" "test -f $remote_path"; then
        echo "    KEEP    $remote_path"
    else
        echo "    SEED    $remote_path  <- $src"
        scp -q "$src" "$HOST:$remote_path"
    fi
done

# ─── Phase 3 ─── placeholder guard
echo ">>> [3/10] Verifying configs have been edited from defaults"
if ssh "$HOST" "grep -qE '\\\$2b\\\$12\\\$\\.\\.\\.|change-me-to-a-secure-key' $REMOTE/config/users.toml" 2>/dev/null; then
    cat >&2 <<EOF

ERROR: $REMOTE/config/users.toml still has placeholder values.

Edit it on $HOST (e.g. via nano or Dockge) and replace:
  - '\$2b\$12\$...'             with a real bcrypt hash
  - 'change-me-to-a-secure-key' with a real api_key

For the bcrypt hash, the image is already loaded on $HOST (after the
first run reaches phase 5); generate one with:
  docker run --rm -i $IMAGE_LATEST hash-password

Then re-run this script. See docs/deployment.md "First deploy" section.
EOF
    exit 1
fi

# ─── Phase 4 ─── build
echo ">>> [4/10] Building $IMAGE_TAGGED + $IMAGE_LATEST"
DOCKER_BUILDKIT=1 docker build \
    -f server/Dockerfile \
    -t "$IMAGE_TAGGED" \
    -t "$IMAGE_LATEST" \
    .

# ─── Phase 5 ─── stream image to host
echo ">>> [5/10] Transferring image to $HOST (gzip-streamed via ssh)"
docker save "$IMAGE_TAGGED" "$IMAGE_LATEST" | gzip | ssh "$HOST" 'gunzip | docker load'

# ─── Phase 6 ─── compose file sync
echo ">>> [6/10] Syncing compose file"
scp -q deploy/doctate-server.compose.yml "$HOST:$REMOTE/compose.yaml"

# ─── Phase 7 ─── restart
echo ">>> [7/10] (Re)starting container"
ssh "$HOST" "cd $REMOTE && docker compose up -d --force-recreate"

# ─── Phase 8 ─── health poll
echo ">>> [8/10] Waiting for /api/health"
for i in $(seq 1 20); do
    if curl -fsS "http://$HOST:3000/api/health" >/dev/null 2>&1; then
        echo "    OK after ${i} attempt(s)"
        # ─── Phase 9 ─── prune old image tags (keep last KEEP + :latest)
        # Containers hold images via internal IDs, not tags, so removing
        # an old <sha>-tag never affects the running container as long as
        # :latest still points to it.
        KEEP=5
        echo ">>> [9/10] Pruning old image tags on $HOST (keep last $KEEP)"
        ssh "$HOST" "
            set -e
            docker images doctate-server --format '{{.CreatedAt}}\t{{.Tag}}' | \
                grep -v -P '\\tlatest\$' | \
                sort -r | \
                awk 'NR > $KEEP { print \$NF }' | \
                xargs -I{} -r docker rmi doctate-server:{} 2>/dev/null || true
            docker image prune -f >/dev/null 2>&1 || true
        "
        # ─── Phase 10 ─── final echo
        echo ">>> [10/10] Deployed $GIT_SHA"
        echo
        echo "Rollback:"
        echo "  ssh $HOST 'docker tag doctate-server:<prev-sha> $IMAGE_LATEST && cd $REMOTE && docker compose up -d --force-recreate'"
        exit 0
    fi
    sleep 3
done

# health timeout
echo ">>> TIMEOUT (60s) — last 50 lines of container logs:" >&2
ssh "$HOST" "docker logs --tail 50 doctate-server" >&2 || true
exit 1
