#!/usr/bin/env bash
set -Eeuo pipefail

deploy_user="labyricorn-deploy"
deploy_home="/srv/labyricorn"

if [[ "$(id -u)" -eq 0 ]]; then
  exec sudo -u "$deploy_user" env \
    HOME="$deploy_home" \
    PATH="$deploy_home/.local/bin:/usr/local/bin:/usr/bin:/bin" \
    "$0" "$@"
fi

if [[ "$(id -un)" != "$deploy_user" ]]; then
  printf 'deploy-labyricorn must run as root or %s\n' "$deploy_user" >&2
  exit 1
fi

repo="$deploy_home/repo"
releases="$deploy_home/releases"
current="$deploy_home/current"
logs="$deploy_home/logs"
project_cache="$deploy_home/project-cache"
lektor="$deploy_home/.local/bin/lektor"
lektor_python="$deploy_home/.local/pipx/venvs/lektor/bin/python"
lock_file="$logs/deploy.lock"
log_file="$logs/deploy.log"
release_dir=""

log() {
  printf '%s %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*" | tee -a "$log_file"
}

cleanup() {
  status=$?
  if [[ "$status" -ne 0 && -n "$release_dir" && -d "$release_dir" ]]; then
    rm -rf -- "$release_dir"
  fi
  if [[ "$status" -ne 0 ]]; then
    log "deployment failed status=$status"
  fi
  exit "$status"
}
trap cleanup EXIT

mkdir -p "$releases" "$logs" "$project_cache"
exec 9>"$lock_file"
if ! flock -n 9; then
  log "deployment skipped reason=lock-held"
  exit 0
fi

if [[ -n "$(git -C "$repo" status --porcelain)" ]]; then
  log "deployment refused reason=dirty-working-tree"
  exit 1
fi

log "deployment started"
git -C "$repo" fetch --prune origin
git -C "$repo" rev-parse --verify --quiet refs/remotes/origin/main >/dev/null
git -C "$repo" switch main
git -C "$repo" reset --hard origin/main

site_commit="$(git -C "$repo" rev-parse HEAD)"
release_name="$(date -u +'%Y%m%dT%H%M%SZ')-${site_commit:0:12}"
release_dir="$releases/$release_name"

if [[ ! -x "$lektor" || ! -x "$lektor_python" ]]; then
  log "deployment refused reason=missing-lektor-runtime"
  exit 1
fi

"$lektor_python" "$repo/scripts/build_with_projects.py" \
  --site-root "$repo" \
  --cache-path "$project_cache" \
  --output-path "$release_dir" \
  --lektor "$lektor"

test -f "$release_dir/index.html"
test -f "$release_dir/.labyricorn-projects.json"
printf '%s\n' "$site_commit" >"$release_dir/.labyricorn-commit"

manifest_signature() {
  "$lektor_python" -c 'import json,sys; d=json.load(open(sys.argv[1], encoding="utf-8")); p={k:{"commit":v["commit"],"metadata_digest":v["metadata_digest"]} for k,v in d.get("projects",{}).items()}; print(json.dumps({"site_commit":d.get("site_commit"),"projects":p},sort_keys=True,separators=(",",":")))' "$1"
}

if [[ -f "$current/.labyricorn-projects.json" ]]; then
  new_signature="$(manifest_signature "$release_dir/.labyricorn-projects.json")"
  current_signature="$(manifest_signature "$current/.labyricorn-projects.json")"
  if [[ "$new_signature" == "$current_signature" ]]; then
    rm -rf -- "$release_dir"
    release_dir=""
    log "deployment unchanged commit=$site_commit"
    exit 0
  fi
fi

chmod -R u=rwX,go=rX "$release_dir"
ln -sfn "releases/$release_name" "$deploy_home/current.next"
mv -Tf "$deploy_home/current.next" "$current"
release_dir=""

mapfile -t old_releases < <(
  find "$releases" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' \
    | sort -nr \
    | awk 'NR > 5 {sub(/^[^ ]+ /, ""); print}'
)
for old_release in "${old_releases[@]}"; do
  if [[ "$(readlink -f "$old_release")" != "$(readlink -f "$current")" ]]; then
    rm -rf -- "$old_release"
  fi
done

log "deployment succeeded release=$release_name commit=$site_commit"
