#!/bin/bash
# batch-tenant-pull.sh — Propaga un push del repo cd-system a N tenants específicos.
#
# IMPORTANTE: este archivo VIVE en VPS1 como /root/scripts/batch-tenant-pull.sh
# Versionado en repo bajo scripts/ para auditoría. Deploy:
#   scp scripts/batch-tenant-pull.sh vps1-claude:/root/scripts/batch-tenant-pull.sh
#   ssh vps1-claude 'chmod +x /root/scripts/batch-tenant-pull.sh && bash -n /root/scripts/batch-tenant-pull.sh'
#
# Documentación: docs/bewpro2.0/infrastructure/07-tenant-clones-architecture.md
#
# Política (founder 2026-05-05): propagación caso a caso, NO masiva.
# Este script ejecuta en una lista EXPLÍCITA de tenants, NO hace broadcast.
#
# Uso:
#   batch-tenant-pull.sh --users=user1,user2,user3 [opciones]
#
# Opciones:
#   --users=u1,u2,...        Lista comma-separated de cpanel_users (REQUERIDO)
#   --users-file=path        Alternativa: archivo con un user por línea
#   --branch=cd-system       Branch a pullear (default: cd-system)
#   --dry-run                Solo simula, no toca nada
#   --skip-if-uptodate       Salta tenants ya en HEAD remoto (default: pull igual)
#   --export-log=path        Path para CSV de resultados
#   --no-cache-clear         No correr artisan *:clear post-pull
#   --no-confirm             Salta el confirm interactivo (no recomendado prod)
#
# Por cada tenant:
#   1. Auto-detect VPS (VPS1 local / VPS2 / VPS3 via `id <user>`)
#   2. Auto-detect panel (HestiaCP / cPanel) probando paths
#   3. Pre-check: HEAD local vs origin → skip si --skip-if-uptodate
#   4. Stash cambios locales (preserva data/project-data/*.json etc)
#   5. git fetch + pull --rebase
#   6. Stash pop (restaurar local)
#   7. php artisan config:clear + view:clear + route:clear
#   8. Verificar HEAD nuevo
#   9. Log resultado en CSV
#
# Auto-recovery: si pop falla por conflict → reporta y sigue con siguiente tenant.

set -uo pipefail

# ── Constantes routing VPS ──────────────────────────────────────────────
VPS2_IP="179.43.124.219"; VPS2_PORT="5633"
VPS3_IP="179.43.120.113"; VPS3_PORT="5651"
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=30"
PHP_BIN="${PHP_BIN:-/opt/cpanel/ea-php82/root/usr/bin/php}"

# ── Parsear argumentos ──────────────────────────────────────────────────
USERS_RAW=""
USERS_FILE=""
BRANCH="cd-system"
DRY_RUN=false
SKIP_IF_UPTODATE=false
EXPORT_LOG=""
SKIP_CACHE_CLEAR=false
NO_CONFIRM=false

for arg in "$@"; do
  case "$arg" in
    --users=*)         USERS_RAW="${arg#*=}" ;;
    --users-file=*)    USERS_FILE="${arg#*=}" ;;
    --branch=*)        BRANCH="${arg#*=}" ;;
    --dry-run)         DRY_RUN=true ;;
    --skip-if-uptodate) SKIP_IF_UPTODATE=true ;;
    --export-log=*)    EXPORT_LOG="${arg#*=}" ;;
    --no-cache-clear)  SKIP_CACHE_CLEAR=true ;;
    --no-confirm)      NO_CONFIRM=true ;;
    --help|-h)
      sed -n '2,40p' "$0" | sed 's/^# //;s/^#//'
      exit 0
      ;;
    *)
      echo "ERROR: argumento desconocido: $arg" >&2
      echo "Usage: $0 --users=u1,u2,... [--dry-run] [--skip-if-uptodate] [--export-log=path]" >&2
      exit 2
      ;;
  esac
done

# Resolver lista de users
USERS=()
if [[ -n "$USERS_FILE" ]]; then
  [[ -f "$USERS_FILE" ]] || { echo "ERROR: users-file no existe: $USERS_FILE" >&2; exit 2; }
  while IFS= read -r line; do
    line="${line%%#*}"  # strip comments
    line=$(echo "$line" | xargs)  # trim
    [[ -n "$line" ]] && USERS+=("$line")
  done < "$USERS_FILE"
elif [[ -n "$USERS_RAW" ]]; then
  IFS=',' read -ra USERS <<< "$USERS_RAW"
else
  echo "ERROR: requerido --users=u1,u2,... o --users-file=path" >&2
  exit 2
fi

[[ ${#USERS[@]} -eq 0 ]] && { echo "ERROR: lista de users vacía" >&2; exit 2; }

# ── Banner ──────────────────────────────────────────────────────────────
echo "═══════════════════════════════════════════════════════════════════"
echo "  batch-tenant-pull — Propagación selectiva a tenants"
echo "═══════════════════════════════════════════════════════════════════"
echo "  Branch:               $BRANCH"
echo "  Tenants:              ${#USERS[@]}"
echo "  Dry-run:              $DRY_RUN"
echo "  Skip if up-to-date:   $SKIP_IF_UPTODATE"
echo "  Cache clear:          $(! $SKIP_CACHE_CLEAR && echo yes || echo NO)"
[[ -n "$EXPORT_LOG" ]] && echo "  Export log:           $EXPORT_LOG"
echo
echo "  Users:"
for u in "${USERS[@]}"; do echo "    · $u"; done
echo

# ── Confirmación interactiva ────────────────────────────────────────────
if ! $DRY_RUN && ! $NO_CONFIRM; then
  read -p "  ¿Continuar? (yes/no): " confirm
  [[ "$confirm" != "yes" ]] && { echo "Cancelado."; exit 0; }
  echo
fi

# ── Helpers VPS detection ───────────────────────────────────────────────
detect_location() {
  local user="$1"
  if id "$user" &>/dev/null; then
    echo "local"
  elif ssh -p "$VPS2_PORT" $SSH_OPTS root@"$VPS2_IP" "id $user" &>/dev/null; then
    echo "vps2"
  elif ssh -p "$VPS3_PORT" $SSH_OPTS root@"$VPS3_IP" "id $user" &>/dev/null; then
    echo "vps3"
  else
    echo "not_found"
  fi
}

remote_exec_as_user() {
  local loc="$1"; local user="$2"; local cmd="$3"
  case "$loc" in
    local) sudo -u "$user" bash -c "$cmd" ;;
    vps2)  ssh -p "$VPS2_PORT" $SSH_OPTS root@"$VPS2_IP" "su -s /bin/bash $user -c \"$cmd\"" ;;
    vps3)  ssh -p "$VPS3_PORT" $SSH_OPTS root@"$VPS3_IP" "su -s /bin/bash $user -c \"$cmd\"" ;;
    *) return 1 ;;
  esac
}

remote_exec_root() {
  local loc="$1"; local cmd="$2"
  case "$loc" in
    local) bash -c "$cmd" ;;
    vps2)  ssh -p "$VPS2_PORT" $SSH_OPTS root@"$VPS2_IP" "$cmd" ;;
    vps3)  ssh -p "$VPS3_PORT" $SSH_OPTS root@"$VPS3_IP" "$cmd" ;;
    *) return 1 ;;
  esac
}

detect_app_dir() {
  local loc="$1"; local user="$2"
  # cPanel:   /home/{user}/public_html/git-files/{user}/
  # HestiaCP: /home/{user}/web/{domain}/public_html/git-files/{user}/ (domain varía)
  local cpanel="/home/${user}/public_html/git-files/${user}"
  if remote_exec_root "$loc" "[[ -f '${cpanel}/artisan' ]]" 2>/dev/null; then
    echo "$cpanel"
    return 0
  fi
  # Para HestiaCP: probamos a encontrar cualquier domain bajo /home/{user}/web/
  local hestia_dir
  hestia_dir=$(remote_exec_root "$loc" "find /home/${user}/web -maxdepth 4 -name artisan -path '*git-files/${user}/artisan' 2>/dev/null | head -1 | sed 's|/artisan\$||'")
  if [[ -n "$hestia_dir" ]]; then
    echo "$hestia_dir"
    return 0
  fi
  return 1
}

# ── Loop principal ──────────────────────────────────────────────────────
declare -a RESULTS=()
TOTAL=${#USERS[@]}
OK_COUNT=0; SKIP_COUNT=0; ERROR_COUNT=0
i=0

for user in "${USERS[@]}"; do
  i=$((i+1))
  echo "── [$i/$TOTAL] $user ──────────────────────────────────────"

  LOC=$(detect_location "$user")
  if [[ "$LOC" == "not_found" ]]; then
    echo "  ✗ user no encontrado en VPS1/VPS2/VPS3"
    RESULTS+=("$user,not_found,,,,user not found")
    ERROR_COUNT=$((ERROR_COUNT+1))
    continue
  fi
  echo "  Location: $LOC"

  APP_DIR=$(detect_app_dir "$LOC" "$user")
  if [[ -z "$APP_DIR" ]]; then
    echo "  ✗ TENANT_APP_DIR no encontrado (probé cPanel + HestiaCP)"
    RESULTS+=("$user,$LOC,,,,app_dir not found")
    ERROR_COUNT=$((ERROR_COUNT+1))
    continue
  fi
  echo "  Path: $APP_DIR"

  HEAD_BEFORE=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git log --oneline -1 2>/dev/null | awk '{print \$1}'" 2>/dev/null | head -c 12)
  echo "  HEAD antes: $HEAD_BEFORE"

  # Pre-check: ya está al día?
  ORIGIN_HEAD=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git fetch origin $BRANCH 2>&1 >/dev/null; git rev-parse origin/$BRANCH 2>/dev/null | head -c 12")
  echo "  origin/$BRANCH: $ORIGIN_HEAD"

  if [[ "$HEAD_BEFORE" == "$ORIGIN_HEAD" ]]; then
    if $SKIP_IF_UPTODATE; then
      echo "  ↷ ya está en HEAD remoto — skip"
      RESULTS+=("$user,$LOC,$APP_DIR,$HEAD_BEFORE,$HEAD_BEFORE,uptodate_skipped")
      SKIP_COUNT=$((SKIP_COUNT+1))
      continue
    else
      echo "  ↷ ya está en HEAD remoto pero continúo (sin --skip-if-uptodate)"
    fi
  fi

  if $DRY_RUN; then
    echo "  🟡 DRY-RUN — no se ejecuta pull"
    RESULTS+=("$user,$LOC,$APP_DIR,$HEAD_BEFORE,$ORIGIN_HEAD,dry_run")
    continue
  fi

  # ── Stash cambios locales (preserva data/project-data/*.json) ─────────
  STASH_TAG="pre-pull-$(date +%s)-$user"
  CHANGES=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git status --short | wc -l" 2>/dev/null | tr -d ' ')
  if [[ "$CHANGES" -gt 0 ]]; then
    echo "  ⚠ $CHANGES cambios locales — stash"
    remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git stash push -m '$STASH_TAG' --include-untracked 2>&1 | tail -1"
  fi

  # ── git pull --rebase ─────────────────────────────────────────────────
  PULL_OUT=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git pull --rebase origin $BRANCH 2>&1" || echo "PULL_FAILED")
  echo "$PULL_OUT" | tail -2 | sed 's/^/    /'

  if echo "$PULL_OUT" | grep -qE "(CONFLICT|error:|fatal:|PULL_FAILED)"; then
    echo "  ✗ pull falló"
    # Intentar abortar rebase
    remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git rebase --abort 2>/dev/null; true"
    # Pop el stash si existía
    [[ "$CHANGES" -gt 0 ]] && remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git stash pop 2>&1 | tail -1" || true
    RESULTS+=("$user,$LOC,$APP_DIR,$HEAD_BEFORE,$HEAD_BEFORE,pull_failed")
    ERROR_COUNT=$((ERROR_COUNT+1))
    continue
  fi

  # ── Pop stash (con manejo de conflicto) ───────────────────────────────
  if [[ "$CHANGES" -gt 0 ]]; then
    POP_OUT=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git stash list 2>/dev/null | grep -q '$STASH_TAG' && git stash pop 2>&1 || echo NO_STASH" 2>/dev/null)
    if echo "$POP_OUT" | grep -qE "(CONFLICT|error)"; then
      echo "  ⚠ stash pop CONFLICT — cambios locales en stash, requiere review manual"
      RESULTS+=("$user,$LOC,$APP_DIR,$HEAD_BEFORE,?,stash_pop_conflict")
      ERROR_COUNT=$((ERROR_COUNT+1))
      continue
    fi
  fi

  # ── Cache clear ───────────────────────────────────────────────────────
  if ! $SKIP_CACHE_CLEAR; then
    remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && $PHP_BIN artisan config:clear 2>&1 | tail -1; $PHP_BIN artisan view:clear 2>&1 | tail -1; $PHP_BIN artisan route:clear 2>&1 | tail -1" 2>/dev/null | sed 's/^/    /'
  fi

  # ── Verificar HEAD nuevo ──────────────────────────────────────────────
  HEAD_AFTER=$(remote_exec_as_user "$LOC" "$user" "cd $APP_DIR && git log --oneline -1 2>/dev/null | awk '{print \$1}'" 2>/dev/null | head -c 12)
  echo "  ✓ HEAD ahora: $HEAD_AFTER"
  RESULTS+=("$user,$LOC,$APP_DIR,$HEAD_BEFORE,$HEAD_AFTER,ok")
  OK_COUNT=$((OK_COUNT+1))
done

# ── Reporte final ───────────────────────────────────────────────────────
echo
echo "═══════════════════════════════════════════════════════════════════"
echo "  Resumen"
echo "═══════════════════════════════════════════════════════════════════"
echo "  Total:      $TOTAL"
echo "  ✓ OK:       $OK_COUNT"
echo "  ↷ Skipped:  $SKIP_COUNT"
echo "  ✗ Errores:  $ERROR_COUNT"
echo

# Export CSV
if [[ -n "$EXPORT_LOG" ]]; then
  {
    echo "user,location,app_dir,head_before,head_after,status"
    for row in "${RESULTS[@]}"; do echo "$row"; done
  } > "$EXPORT_LOG"
  echo "  Log CSV: $EXPORT_LOG"
fi

[[ $ERROR_COUNT -gt 0 ]] && exit 1
exit 0
