#!/usr/bin/env bash

set -Eeuo pipefail

readonly SCRIPT_NAME="$(basename "$0")"
readonly PROJECT_DIR="${1:-}"
readonly HEALTH_URL="${2:-}"
readonly MAX_ATTEMPTS="${MAX_ATTEMPTS:-12}"
readonly RETRY_SECONDS="${RETRY_SECONDS:-5}"

log() {
  printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}

fail() {
  printf '[%s] ERROR: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >&2
  exit 1
}

usage() {
  cat <<EOF
Usage:
  $SCRIPT_NAME /path/to/project [https://app.example.com/health]

Environment variables:
  MAX_ATTEMPTS   Health-check attempts (default: 12)
  RETRY_SECONDS  Seconds between attempts (default: 5)
EOF
}

on_error() {
  local exit_code=$?
  log "Deployment stopped with exit code ${exit_code}. Recent container logs:"
  docker compose logs --tail=80 2>/dev/null || true
  exit "$exit_code"
}

trap on_error ERR

[[ -n "$PROJECT_DIR" ]] || { usage; fail "Project directory is required."; }
[[ -d "$PROJECT_DIR" ]] || fail "Directory does not exist: $PROJECT_DIR"
command -v docker >/dev/null 2>&1 || fail "Docker is not installed or not available in PATH."
docker compose version >/dev/null 2>&1 || fail "Docker Compose v2 is required."

cd "$PROJECT_DIR"
[[ -f compose.yaml || -f compose.yml || -f docker-compose.yml || -f docker-compose.yaml ]] \
  || fail "No Compose file was found in $PROJECT_DIR"

log "Validating Docker Compose configuration"
docker compose config --quiet

log "Pulling the images declared by the project"
docker compose pull

log "Starting the new service version"
docker compose up -d --remove-orphans

log "Current container state"
docker compose ps

if [[ -n "$HEALTH_URL" ]]; then
  command -v curl >/dev/null 2>&1 || fail "curl is required when a health URL is provided."
  log "Checking application health at $HEALTH_URL"

  for ((attempt = 1; attempt <= MAX_ATTEMPTS; attempt++)); do
    if curl --fail --silent --show-error --max-time 10 "$HEALTH_URL" >/dev/null; then
      log "Health check passed on attempt ${attempt}/${MAX_ATTEMPTS}"
      log "Deployment completed successfully"
      exit 0
    fi

    if (( attempt < MAX_ATTEMPTS )); then
      log "Health check ${attempt}/${MAX_ATTEMPTS} failed; retrying in ${RETRY_SECONDS}s"
      sleep "$RETRY_SECONDS"
    fi
  done

  fail "The application did not become healthy after $MAX_ATTEMPTS attempts."
fi

log "No health URL was provided; deployment completed without an HTTP check"
