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

PROJECT_NAME="${PROJECT_NAME:-ecom01}"
BRANCH="${DEPLOY_BRANCH:-main}"
DEPLOY_ROOT="${DEPLOY_ROOT:-/var/www/stacks/ecom01}"
REPO_DIR="${REPO_DIR:-$DEPLOY_ROOT/repo}"
REPO_URL="${DEPLOY_REPO_URL:-git@gitlab.com:ecom-profit/ecom01.git}"
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}"
DEPLOY_SKIP_CHECKOUT="${DEPLOY_SKIP_CHECKOUT:-0}"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"

log() {
  printf '[%s] %s\n' "$PROJECT_NAME" "$*"
}

require_command() {
  if ! command -v "$1" >/dev/null 2>&1; then
    log "Missing required command: $1"
    exit 1
  fi
}

compose() {
  docker compose -p "$PROJECT_NAME" --env-file "$REPO_DIR/.env.production" -f "$REPO_DIR/$COMPOSE_FILE" "$@"
}

ensure_env_files() {
  local missing=0

  if [ ! -f "$REPO_DIR/.env.production" ]; then
    cp "$REPO_DIR/.env.production.example" "$REPO_DIR/.env.production"
    log "Created $REPO_DIR/.env.production from example. Fill secrets before deploying."
    missing=1
  fi

  if [ ! -f "$REPO_DIR/admin/.env" ]; then
    cp "$REPO_DIR/admin/.env.production.example" "$REPO_DIR/admin/.env"
    log "Created $REPO_DIR/admin/.env from production example. Fill secrets before deploying."
    missing=1
  fi

  if [ ! -f "$REPO_DIR/frontend/.env.production" ]; then
    cp "$REPO_DIR/frontend/.env.production.example" "$REPO_DIR/frontend/.env.production"
    log "Created $REPO_DIR/frontend/.env.production from example. Fill secrets before deploying."
    missing=1
  fi

  if grep -Eq 'change-me|APP_KEY=$' "$REPO_DIR/.env.production" "$REPO_DIR/admin/.env"; then
    log "Production env files still contain placeholder values."
    missing=1
  fi

  if [ "$missing" -ne 0 ]; then
    exit 1
  fi
}

normalize_repo_permissions() {
  log "Normalizing ecom01 repo permissions for the runner and app containers."

  find "$REPO_DIR" \
    \( -path "$REPO_DIR/admin/storage" -o -path "$REPO_DIR/admin/storage/*" -o -path "$REPO_DIR/admin/bootstrap/cache" -o -path "$REPO_DIR/admin/bootstrap/cache/*" \) -prune -o \
    -type d -exec chmod u+rwx,g+rwx,o+rx {} + 2>/dev/null || true
  find "$REPO_DIR" \
    \( -path "$REPO_DIR/admin/storage" -o -path "$REPO_DIR/admin/storage/*" -o -path "$REPO_DIR/admin/bootstrap/cache" -o -path "$REPO_DIR/admin/bootstrap/cache/*" -o -path "$REPO_DIR/.env.production" -o -path "$REPO_DIR/admin/.env" -o -path "$REPO_DIR/frontend/.env.production" \) -prune -o \
    -type f -exec chmod u+rw,g+rw,o+r {} + 2>/dev/null || true
  find "$REPO_DIR" \
    \( -path "$REPO_DIR/admin/storage" -o -path "$REPO_DIR/admin/storage/*" -o -path "$REPO_DIR/admin/bootstrap/cache" -o -path "$REPO_DIR/admin/bootstrap/cache/*" \) -prune -o \
    -type d -exec chmod g+s {} + 2>/dev/null || true

  chgrp -R docker "$REPO_DIR" 2>/dev/null || true
  chmod 660 \
    "$REPO_DIR/.env.production" \
    "$REPO_DIR/admin/.env" \
    "$REPO_DIR/frontend/.env.production" \
    2>/dev/null || true
}

record_existing_state() {
  mkdir -p "$DEPLOY_ROOT/backups"
  docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' > "$DEPLOY_ROOT/backups/docker-ps-before-$TIMESTAMP.txt"
  log "Recorded docker ps to $DEPLOY_ROOT/backups/docker-ps-before-$TIMESTAMP.txt"
}

checkout_code() {
  mkdir -p "$DEPLOY_ROOT"

  if [ "$DEPLOY_SKIP_CHECKOUT" = "1" ]; then
    if [ ! -f "$REPO_DIR/$COMPOSE_FILE" ]; then
      log "DEPLOY_SKIP_CHECKOUT=1, but $REPO_DIR/$COMPOSE_FILE is missing."
      exit 1
    fi
    log "Using already-synced checkout in $REPO_DIR"
    return 0
  fi

  if [ ! -d "$REPO_DIR/.git" ]; then
    log "Cloning $REPO_URL into $REPO_DIR"
    git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
  else
    log "Updating existing checkout in $REPO_DIR"
    git -C "$REPO_DIR" fetch origin "$BRANCH"
    git -C "$REPO_DIR" checkout "$BRANCH"
    git -C "$REPO_DIR" pull --ff-only origin "$BRANCH"
  fi
}

backup_existing_project_dir() {
  if [ -d "$REPO_DIR" ]; then
    mkdir -p "$DEPLOY_ROOT/backups"
    tar \
      --exclude='.git' \
      --exclude='admin/vendor' \
      --exclude='frontend/node_modules' \
      --exclude='frontend/.next' \
      -czf "$DEPLOY_ROOT/backups/repo-before-$TIMESTAMP.tar.gz" \
      -C "$DEPLOY_ROOT" repo
    log "Backed up current ecom01 repo folder."
  fi
}

backup_existing_database() {
  local db_container
  db_container="$(compose ps -q db 2>/dev/null || true)"

  if [ -n "$db_container" ] && docker inspect "$db_container" >/dev/null 2>&1; then
    mkdir -p "$DEPLOY_ROOT/backups"
    log "Backing up existing production database."
    compose exec -T db sh -lc 'mysqldump --single-transaction --quick -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' \
      | gzip -c > "$DEPLOY_ROOT/backups/db-before-$TIMESTAMP.sql.gz"
  else
    log "No existing ecom01 database container found; skipping production DB backup."
  fi
}

wait_for_database() {
  log "Waiting for MySQL healthcheck."
  for _ in $(seq 1 60); do
    if compose exec -T db sh -lc 'mysqladmin ping -h localhost -uroot -p"$MYSQL_ROOT_PASSWORD" --silent' >/dev/null 2>&1; then
      return 0
    fi
    sleep 2
  done

  log "Database did not become ready in time."
  compose ps
  exit 1
}

wait_for_php() {
  log "Waiting for PHP-FPM."
  for _ in $(seq 1 60); do
    if compose exec -T php php -v >/dev/null 2>&1; then
      return 0
    fi
    sleep 2
  done

  log "PHP-FPM did not become ready in time."
  compose ps
  exit 1
}

run_laravel_tasks() {
  log "Running Laravel production tasks."
  compose exec -T php sh -lc '
    set -e
    mkdir -p storage/framework/cache/data storage/framework/sessions storage/framework/views storage/logs
    chown -R www-data:www-data storage bootstrap/cache
    composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
    php artisan migrate --force
    php artisan storage:link || true
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
    php artisan event:cache || true
    chown -R www-data:www-data storage bootstrap/cache
    chmod -R u+rwX,g+rwX,o+rX storage bootstrap/cache
  '
}

deploy_containers() {
  log "Building and starting only ecom01 app containers."
  compose up -d --build --remove-orphans db redis php frontend
  wait_for_database
  wait_for_php
  run_laravel_tasks
  log "Restarting ecom01 PHP-FPM after Laravel cache rebuild."
  compose up -d --no-deps --force-recreate php
  compose up -d --build --remove-orphans db redis frontend
  wait_for_php
  log "Reloading ecom01 Nginx with the current project config."
  compose up -d --no-deps --force-recreate nginx
}

post_deploy_status() {
  log "Current ecom01 containers:"
  compose ps
  log "Recent ecom01 logs:"
  compose logs --tail=80 php nginx frontend db redis
}

main() {
  if [ "$DEPLOY_SKIP_CHECKOUT" != "1" ]; then
    require_command git
  fi
  require_command docker

  record_existing_state
  backup_existing_project_dir
  checkout_code
  ensure_env_files
  normalize_repo_permissions
  backup_existing_database
  deploy_containers
  post_deploy_status

  log "Deploy finished."
}

main "$@"
