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

remote="${MAILHUB_DEPLOY_REMOTE:-}"
remote_dir="${MAILHUB_DEPLOY_DIR:-}"
branch="${MAILHUB_DEPLOY_BRANCH:-$(git branch --show-current)}"
git_url="${MAILHUB_DEPLOY_GIT_URL:-$(git remote get-url origin)}"
stash_remote="${MAILHUB_DEPLOY_STASH_REMOTE:-0}"

if [[ -z "${remote}" || -z "${remote_dir}" ]]; then
  echo "Set MAILHUB_DEPLOY_REMOTE and MAILHUB_DEPLOY_DIR before deploying." >&2
  echo "Example: MAILHUB_DEPLOY_REMOTE=deploy@example.com MAILHUB_DEPLOY_DIR=/opt/mailhub npm run deploy:remote" >&2
  exit 1
fi

if [[ -z "${branch}" ]]; then
  echo "Unable to detect current git branch." >&2
  exit 1
fi

git fetch origin "${branch}"

local_head="$(git rev-parse HEAD)"
remote_head="$(git rev-parse "origin/${branch}")"
if [[ "${local_head}" != "${remote_head}" ]]; then
  echo "Local HEAD is not pushed to origin/${branch}." >&2
  echo "Commit and push first, then run this deploy script." >&2
  exit 1
fi

ssh -o ServerAliveInterval=15 -o ServerAliveCountMax=4 "${remote}" \
  'bash -s' -- "${remote_dir}" "${branch}" "${git_url}" "${stash_remote}" <<'REMOTE'
set -euo pipefail

remote_dir="$1"
branch="$2"
git_url="$3"
stash_remote="$4"

cd "${remote_dir}"

wait_for_compose_health() {
  local timeout="${MAILHUB_DEPLOY_HEALTH_TIMEOUT:-180}"
  local deadline=$((SECONDS + timeout))
  local service container_id snapshot state health all_ready

  while (( SECONDS < deadline )); do
    all_ready=1
    for service in "$@"; do
      container_id="$(docker compose ps --all --quiet "${service}" 2>/dev/null | tail -n 1 || true)"
      if [[ -z "${container_id}" ]]; then
        all_ready=0
        continue
      fi
      snapshot="$(docker inspect \
        --format '{{.State.Status}} {{if .State.Health}}{{.State.Health.Status}}{{else}}missing{{end}}' \
        "${container_id}" 2>/dev/null || true)"
      state="${snapshot%% *}"
      health="${snapshot#* }"
      if [[ "${state}" == "exited" || "${state}" == "dead" ]]; then
        docker compose logs --tail=100 "${service}" >&2 || true
        return 1
      fi
      [[ "${state}" == "running" && "${health}" == "healthy" ]] || all_ready=0
    done
    [[ "${all_ready}" == "1" ]] && return 0
    sleep 2
  done

  docker compose ps >&2 || true
  docker compose logs --tail=100 "$@" >&2 || true
  return 1
}

if ! git remote get-url origin >/dev/null 2>&1; then
  git remote add origin "${git_url}"
fi

if [[ -n "$(git status --porcelain)" ]]; then
  if [[ "${stash_remote}" == "1" ]]; then
    git stash push -u -m "pre-deploy-$(date -u +%Y%m%d-%H%M%S)"
  else
    echo "Remote working tree is dirty. Set MAILHUB_DEPLOY_STASH_REMOTE=1 to stash it before pulling." >&2
    git status --short >&2
    exit 1
  fi
fi

previous_revision="$(git rev-parse HEAD)"
on_deploy_exit() {
  local status=$?
  trap - EXIT
  if [[ "${status}" != "0" ]]; then
    echo "Deployment failed. Previous revision was ${previous_revision}; inspect the running containers before rollback." >&2
    docker compose ps >&2 || true
  fi
  exit "${status}"
}
trap on_deploy_exit EXIT

git fetch origin "${branch}"
git checkout "${branch}"
git pull --ff-only origin "${branch}"
docker compose up -d --build
wait_for_compose_health app postfix
MAILHUB_CERT_RESTART=1 ./scripts/sync-tls-certificate.sh
wait_for_compose_health app postfix
docker compose ps
trap - EXIT
REMOTE
