#!/usr/bin/env bash
#
# Manual deploy from a workstation, for use until the Gitea runner is up.
# Mirrors .gitea/workflows/deploy.yml: same build, same rsync, same guards.
#
#   ./scripts/deploy.sh               build, preview the changes, confirm, deploy
#   ./scripts/deploy.sh --dry-run     stop after the preview
#   ./scripts/deploy.sh --yes         skip the confirmation prompt
#   ./scripts/deploy.sh --skip-build  deploy whatever is already in public/
#
# Configuration is read from scripts/deploy.env (gitignored); see
# scripts/deploy.env.example. Environment variables take precedence.

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"

DRY_RUN=0
ASSUME_YES=0
SKIP_BUILD=0
for arg in "$@"; do
  case "$arg" in
    --dry-run)    DRY_RUN=1 ;;
    --yes|-y)     ASSUME_YES=1 ;;
    --skip-build) SKIP_BUILD=1 ;;
    -h|--help)    sed -n '3,13p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) echo "unknown option: $arg (try --help)" >&2; exit 2 ;;
  esac
done

die() { echo "error: $*" >&2; exit 1; }

# ---------------------------------------------------------------- config ---

CONFIG="${DEPLOY_ENV_FILE:-scripts/deploy.env}"
if [ -f "$CONFIG" ]; then
  # shellcheck disable=SC1090
  . "$CONFIG"
elif [ -z "${DEPLOY_HOST:-}" ]; then
  die "no $CONFIG and no DEPLOY_HOST in the environment.
       Copy scripts/deploy.env.example to $CONFIG and fill it in."
fi

: "${DEPLOY_HOST:?set DEPLOY_HOST}"
: "${DEPLOY_USER:?set DEPLOY_USER}"
: "${DEPLOY_PATH:?set DEPLOY_PATH}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/mcg-deploy}"

# rsync runs with --delete, so the destination becomes an exact mirror of
# public/. Refuse to run against an unset, root or shallow path.
case "$DEPLOY_PATH" in
  ""|"/"|"/root"|"/home"|"/var"|"/etc"|"/usr"|"/srv"|"/opt")
    die "DEPLOY_PATH is unsafe: '$DEPLOY_PATH'" ;;
esac
if [ "$(printf '%s' "$DEPLOY_PATH" | tr -cd '/' | wc -c)" -lt 2 ]; then
  die "DEPLOY_PATH '$DEPLOY_PATH' is too shallow to delete into"
fi

# --------------------------------------------------------- dependencies ---

for cmd in rsync ssh; do
  command -v "$cmd" >/dev/null 2>&1 || die "'$cmd' is not installed.
       Run this script from WSL or another Linux shell — Git Bash and
       PowerShell do not ship rsync."
done

# ------------------------------------------------------------- ssh key ---

[ -f "$SSH_KEY" ] || die "ssh key not found: $SSH_KEY"

case "$SSH_KEY" in
  /mnt/*)
    die "the ssh key is on the Windows filesystem ($SSH_KEY).
       WSL cannot hold 0600 there, so ssh will reject the key. Copy it into
       the WSL home directory instead:
           cp '$SSH_KEY' ~/.ssh/mcg-deploy && chmod 600 ~/.ssh/mcg-deploy" ;;
esac

perms="$(stat -c '%a' "$SSH_KEY" 2>/dev/null || echo '')"
if [ -n "$perms" ] && [ "$perms" != "600" ] && [ "$perms" != "400" ]; then
  echo "==> Tightening permissions on $SSH_KEY (was $perms)"
  chmod 600 "$SSH_KEY"
fi

# --------------------------------------------------------------- build ---

if [ "$SKIP_BUILD" -eq 0 ]; then
  command -v hugo >/dev/null 2>&1 || die "hugo is not on PATH.
       Either install Hugo 0.164.0 in WSL, or build on the Windows side and
       re-run this script with --skip-build."
  echo "==> Building"
  rm -rf public
  hugo --gc --minify
fi

[ -f public/index.html ] || \
  die "public/index.html missing - refusing to deploy an empty build"

# -------------------------------------------------------------- deploy ---

# No StrictHostKeyChecking override: ssh falls back to its default (ask), so it
# uses your personal ~/.ssh/known_hosts and prompts once on first connect. CI
# pins it to yes instead, because it has no prompt and a known_hosts secret.
SSH_CMD="ssh -i $SSH_KEY -o IdentitiesOnly=yes"
TARGET="${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/"
RSYNC_OPTS=(-az --delete --checksum)

preview="$(mktemp)"
trap 'rm -f "$preview"' EXIT

echo "==> Previewing changes against $TARGET"
rsync "${RSYNC_OPTS[@]}" --itemize-changes --dry-run \
  -e "$SSH_CMD" public/ "$TARGET" | tee "$preview"

deletions="$(grep -c '^\*deleting' "$preview" || true)"
updates="$(grep -cv '^\*deleting' "$preview" || true)"
echo
echo "==> $updates file(s) to send, $deletions to delete on the server"

if [ "$DRY_RUN" -eq 1 ]; then
  echo "==> Dry run only, nothing was uploaded."
  exit 0
fi

if [ "$ASSUME_YES" -eq 0 ]; then
  printf 'Continue? [y/N] '
  read -r reply
  case "$reply" in
    [yY]|[yY][eE][sS]) ;;
    *) echo "aborted"; exit 1 ;;
  esac
fi

echo "==> Deploying"
rsync "${RSYNC_OPTS[@]}" --human-readable --stats \
  -e "$SSH_CMD" public/ "$TARGET"

echo "==> Done"
