#!/bin/bash

set -Eeuo pipefail

MODE=${1:-deploy}
if [ "$MODE" = "--check" ] || [ "$MODE" = "check" ] || [ "$MODE" = "preflight" ]; then
  PREFLIGHT_ONLY=1
  shift || true
else
  PREFLIGHT_ONLY=0
fi

BRANCH=${1:-main}
SERVICE_NAME=${2:-fpm}
APP_PATH=${APP_PATH:-/var/www/html/farmacia}
COMPOSE_DIR=${COMPOSE_DIR:-/var/www/farmacia}
COMPOSE_FILE=${COMPOSE_FILE:-$COMPOSE_DIR/docker-compose.yml}
HEALTHCHECK_URL=${HEALTHCHECK_URL:-https://demo.sysfarma.pe/login}
LOCK_FILE=${LOCK_FILE:-/var/lock/enterfarmaplus-deploy.lock}
STATE_DIR=${STATE_DIR:-/var/lib/enterfarmaplus-deploy}
LOG_FILE=${LOG_FILE:-/var/log/enterfarmaplus-deploy.log}
DOCKER_APP_SCRIPT=${DOCKER_APP_SCRIPT:-$APP_PATH/script-docker-app.sh}
# auto = solo migra si el commit a desplegar cambia archivos de migracion.
# force = migra siempre. skip = nunca migra (uso excepcional y explicito).
MIGRATION_MODE=${MIGRATION_MODE:-auto}

if [ "$(id -u)" -ne 0 ]; then
  echo "Este deploy debe ejecutarse como root en el host Docker."
  exit 1
fi

for command_name in git docker flock curl; do
  if ! command -v "$command_name" >/dev/null 2>&1; then
    echo "Falta el comando requerido: $command_name"
    exit 1
  fi
done

if [ ! -d "$APP_PATH/.git" ]; then
  echo "No existe un repositorio git en $APP_PATH"
  exit 1
fi

if [ ! -x "$DOCKER_APP_SCRIPT" ]; then
  echo "No existe o no es ejecutable el helper Docker: $DOCKER_APP_SCRIPT"
  exit 1
fi

print_deploy_help() {
  cat <<'EOF'
Solucion rapida:
  1. Revisa el estado: git status --short --branch
  2. Si hay cambios que quieres publicar, haz commit y push.
  3. Si hay cambios que no deben ir al deploy, descartalos o movelos fuera del repo.
  4. Vuelve a correr el deploy.

Reglas:
  - El deploy solo acepta main limpio.
  - origin/main debe ser un fast-forward desde el commit local.
  - No uses git reset --hard como rutina de deploy.
EOF
}

fail_dirty_tree() {
  echo "El arbol de produccion no esta limpio; deploy cancelado."
  git status --short --branch
  print_deploy_help
  exit 1
}

fail_non_ff() {
  echo "origin/$BRANCH no es avance fast-forward desde el HEAD actual."
  echo "HEAD local:    $(git rev-parse --short HEAD)"
  echo "HEAD remoto:   $(git rev-parse --short FETCH_HEAD)"
  echo
  echo "Cambios pendientes en origin/$BRANCH:"
  git log --oneline --decorate --graph --max-count=10 HEAD..FETCH_HEAD || true
  print_deploy_help
  exit 1
}

mkdir -p "$STATE_DIR"
touch "$LOG_FILE"
chmod 750 "$STATE_DIR"
chmod 640 "$LOG_FILE"

exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  echo "Ya hay otro deploy de EnterFarmaPlus en ejecucion."
  exit 1
fi

exec > >(tee -a "$LOG_FILE") 2>&1

started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
previous_commit=""
target_commit=""

on_error() {
  exit_code=$?
  echo "DEPLOY_ERROR timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) previous=${previous_commit:-unknown} target=${target_commit:-unknown} exit=$exit_code"
  echo "No se hace rollback automatico: las migraciones pueden no ser reversibles."
  exit "$exit_code"
}
trap on_error ERR

docker_app() {
  SERVICE_NAME="$SERVICE_NAME" \
  APP_PATH="$APP_PATH" \
  COMPOSE_DIR="$COMPOSE_DIR" \
  COMPOSE_FILE="$COMPOSE_FILE" \
  "$DOCKER_APP_SCRIPT" "$@"
}

echo "DEPLOY_START timestamp=$started_at branch=$BRANCH"
cd "$APP_PATH"

current_branch=$(git branch --show-current)
if [ "$current_branch" != "$BRANCH" ]; then
  echo "Rama inesperada: actual=$current_branch esperada=$BRANCH"
  print_deploy_help
  exit 1
fi

if [ -n "$(git status --porcelain)" ]; then
  fail_dirty_tree
fi

previous_commit=$(git rev-parse HEAD)
git fetch --prune origin "$BRANCH"
target_commit=$(git rev-parse FETCH_HEAD)

if ! git merge-base --is-ancestor "$previous_commit" "$target_commit"; then
  fail_non_ff
fi

if [ "$PREFLIGHT_ONLY" -eq 1 ]; then
  echo "PREFLIGHT_OK branch=$BRANCH head=$(git rev-parse --short HEAD) remote=$(git rev-parse --short FETCH_HEAD)"
  exit 0
fi

case "$MIGRATION_MODE" in
  auto|force|skip)
    ;;
  *)
    echo "MIGRATION_MODE invalido: $MIGRATION_MODE (usa auto, force o skip)"
    exit 1
    ;;
esac

migrations_changed=0
migration_reason="no-migration-files-changed"
if [ "$MIGRATION_MODE" = "auto" ]; then
  while IFS= read -r changed_file; do
    case "$changed_file" in
      database/migrations/*|modules/*/Database/Migrations/*|modules/*/Database/migrations/*)
        migrations_changed=1
        migration_reason="migration-files-changed"
        break
        ;;
    esac
  done < <(git diff --name-only "$previous_commit" "$target_commit")
elif [ "$MIGRATION_MODE" = "force" ]; then
  migrations_changed=1
  migration_reason="explicit-force"
elif [ "$MIGRATION_MODE" = "skip" ]; then
  migration_reason="explicit-skip"
fi

if [ "$previous_commit" != "$target_commit" ]; then
  git merge --ff-only "$target_commit"
else
  echo "Codigo ya alineado en $(git rev-parse --short HEAD)."
fi

# Los assets se compilan y versionan antes del push. Nunca se ejecuta npm aqui.
docker_app composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
docker_app artisan storage:link --force
if [ "$migrations_changed" -eq 1 ] && [ "$MIGRATION_MODE" != "skip" ]; then
  echo "MIGRATIONS_RUN mode=$MIGRATION_MODE reason=changed-or-forced"
  docker_app artisan migrate --force --no-interaction
  docker_app artisan tenancy:migrate --no-interaction
else
  echo "MIGRATIONS_SKIPPED mode=$MIGRATION_MODE reason=$migration_reason"
fi
docker_app artisan optimize:clear
docker_app artisan config:cache || {
  echo "config:cache fallo; se deja la configuracion sin cachear."
  docker_app artisan config:clear
}
docker_app artisan route:clear
docker_app artisan view:clear
docker_app artisan queue:restart

# PHP solo necesita escritura en los directorios de runtime.
RUN_USER=root docker_app shell \
  "chown -R www-data:www-data storage bootstrap/cache && find storage bootstrap/cache -type d -exec chmod 775 {} + && find storage bootstrap/cache -type f -exec chmod 664 {} +"

if [ -n "$(git status --porcelain)" ]; then
  echo "El deploy dejo cambios en el arbol Git; se considera fallido."
  git status --short
  exit 1
fi

curl --fail --silent --show-error --location --max-time 20 \
  --output /dev/null "$HEALTHCHECK_URL"

deployed_commit=$(git rev-parse HEAD)
printf '%s\n' "$previous_commit" > "$STATE_DIR/previous-commit"
printf '%s\n' "$deployed_commit" > "$STATE_DIR/current-commit"
printf '%s\t%s\t%s\t%s\n' "$started_at" "$BRANCH" "$previous_commit" "$deployed_commit" \
  >> "$STATE_DIR/history.tsv"

echo "DEPLOY_OK timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) head=$(git rev-parse --short HEAD) healthcheck=$HEALTHCHECK_URL"
