#!/bin/bash
# E-Ticket - Script d'import pour migration de serveur
# Restaure un bundle de migration genere par backup.sh sur un nouveau serveur.
#
# Le bundle est attendu au format e_ticket_migration_<DATE>.tar.gz et contient:
#   db.sql.gz, public_uploads.tar.gz, var_state.tar.gz, env.local, cert/, manifest.txt
#
# A la fin, le script chown automatiquement les fichiers restaures vers RESTORE_OWNER
# (defaut: bot) afin que l'utilisateur PHP du conteneur puisse y ecrire, et purge
# var/cache pour eviter les fichiers obsoletes appartenant a un autre utilisateur.
#
# Usage:
#   ./restore.sh <bundle.tar.gz> [options]
#
# Variables d'environnement supportees:
#   COMPOSE_FILE   : fichier docker-compose a utiliser  (defaut: docker-compose-prod.yml)
#   DB_SERVICE     : service Docker de la base          (defaut: db-master)
#   DB_USER        : utilisateur PostgreSQL             (defaut: e-ticket)
#   DB_NAME        : base de donnees a restaurer        (defaut: e-ticket)
#   RESTORE_OWNER  : user:group pour le chown final     (defaut: bot:bot)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="${COMPOSE_FILE:-${SCRIPT_DIR}/docker-compose-prod.yml}"
DB_SERVICE="${DB_SERVICE:-db-master}"
DB_USER="${DB_USER:-e-ticket}"
DB_NAME="${DB_NAME:-e-ticket}"
RESTORE_OWNER="${RESTORE_OWNER:-bot:bot}"

BUNDLE=""
ASSUME_YES="false"
SKIP_DB="false"
SKIP_UPLOADS="false"
SKIP_VAR="false"
SKIP_SECRETS="false"
SKIP_CHOWN="false"

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

err() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERREUR: $*" >&2
}

usage() {
    cat <<'EOF'
Usage:
  sudo ./restore.sh <bundle.tar.gz> [options]

Options:
  --yes, -y         Mode non interactif (pas de confirmation)
  --skip-db         Ne pas restaurer la base de donnees
  --skip-uploads    Ne pas restaurer public/uploads
  --skip-var        Ne pas restaurer var/ (payouts, billets, invoices, unsubscribed.json)
  --skip-secrets    Ne pas restaurer .env.local et config/cert/
  --skip-chown      Ne pas faire le chown final (deconseille)
  --owner USER:GRP  Utilisateur:groupe pour le chown final (defaut: bot:bot)
  -h, --help        Affiche cette aide

Variables d'environnement (alternatives aux flags):
  RESTORE_OWNER     Equivalent de --owner

ATTENTION: La restauration ECRASE les donnees existantes !
ATTENTION: Le chown final necessite root (sudo).
EOF
}

# --- Parsing des arguments ---
while [ $# -gt 0 ]; do
    case "$1" in
        --yes|-y)        ASSUME_YES="true"; shift ;;
        --skip-db)       SKIP_DB="true"; shift ;;
        --skip-uploads)  SKIP_UPLOADS="true"; shift ;;
        --skip-var)      SKIP_VAR="true"; shift ;;
        --skip-secrets)  SKIP_SECRETS="true"; shift ;;
        --skip-chown)    SKIP_CHOWN="true"; shift ;;
        --owner)
            if [ $# -lt 2 ]; then
                err "--owner requiert une valeur (ex: --owner bot:bot)"
                exit 1
            fi
            RESTORE_OWNER="$2"
            shift 2
            ;;
        -h|--help)       usage; exit 0 ;;
        --*)             err "Option inconnue: $1"; usage; exit 1 ;;
        *)
            if [ -z "${BUNDLE}" ]; then
                BUNDLE="$1"
            else
                err "Argument en trop: $1"
                usage
                exit 1
            fi
            shift
            ;;
    esac
done

# --- Verification root pour le chown final ---
if [ "${SKIP_CHOWN}" = "false" ] && [ "$(id -u)" != "0" ]; then
    err "Ce script doit etre execute en root (sudo) pour le chown final vers ${RESTORE_OWNER}"
    err "Soit relancer avec sudo, soit ajouter --skip-chown (deconseille)"
    exit 1
fi

# --- Verification que le user/group cible existe ---
if [ "${SKIP_CHOWN}" = "false" ]; then
    OWNER_USER="${RESTORE_OWNER%%:*}"
    OWNER_GROUP="${RESTORE_OWNER##*:}"
    if ! id -u "${OWNER_USER}" >/dev/null 2>&1; then
        err "Utilisateur '${OWNER_USER}' introuvable sur le systeme"
        exit 1
    fi
    if ! getent group "${OWNER_GROUP}" >/dev/null 2>&1; then
        err "Groupe '${OWNER_GROUP}' introuvable sur le systeme"
        exit 1
    fi
fi

if [ -z "${BUNDLE}" ]; then
    err "Aucun bundle specifie"
    usage
    exit 1
fi

if [ ! -f "${BUNDLE}" ]; then
    err "Bundle introuvable: ${BUNDLE}"
    exit 1
fi

# --- Extraction du bundle dans un dossier temporaire ---
TMP_DIR="$(mktemp -d -t e_ticket_restore_XXXXXX)"
cleanup() {
    rm -rf "${TMP_DIR}"
}
trap cleanup EXIT

log "=== Restauration du bundle de migration E-Ticket ==="
log "Bundle: ${BUNDLE}"
log "Extraction dans ${TMP_DIR}..."

if ! tar -xzf "${BUNDLE}" -C "${TMP_DIR}"; then
    err "Echec d'extraction du bundle"
    exit 1
fi

# Trouve le dossier extrait (e_ticket_migration_<DATE>)
EXTRACTED_DIR="$(find "${TMP_DIR}" -maxdepth 1 -mindepth 1 -type d -name 'e_ticket_migration_*' | head -n1)"
if [ -z "${EXTRACTED_DIR}" ] || [ ! -d "${EXTRACTED_DIR}" ]; then
    err "Bundle invalide: dossier e_ticket_migration_* introuvable"
    exit 1
fi

# --- Verification du manifest et des checksums ---
if [ ! -f "${EXTRACTED_DIR}/manifest.txt" ]; then
    err "Manifest manquant dans le bundle"
    exit 1
fi

log "Verification des checksums..."
(cd "${EXTRACTED_DIR}" && grep -E '^[a-f0-9]{64} ' manifest.txt | sha256sum -c --quiet) || {
    err "Checksums invalides - bundle corrompu"
    exit 1
}
log "    OK - bundle integre"

echo
echo "================================================================"
echo " Manifest du bundle:"
echo "================================================================"
grep -E '^(bundle_name|created_at|source_host|git_commit|git_branch|db_name|skip_secrets):' "${EXTRACTED_DIR}/manifest.txt" || true
echo "================================================================"

# --- Verification des composants disponibles ---
HAS_DB="false"
HAS_UPLOADS="false"
HAS_VAR="false"
HAS_ENV="false"
HAS_CERT="false"
[ -f "${EXTRACTED_DIR}/db.sql.gz" ]            && HAS_DB="true"
[ -f "${EXTRACTED_DIR}/public_uploads.tar.gz" ] && HAS_UPLOADS="true"
[ -f "${EXTRACTED_DIR}/var_state.tar.gz" ]     && HAS_VAR="true"
[ -f "${EXTRACTED_DIR}/env.local" ]            && HAS_ENV="true"
[ -d "${EXTRACTED_DIR}/cert" ]                 && HAS_CERT="true"

echo
echo " Plan de restauration:"
echo "----------------------------------------------------------------"
[ "${HAS_DB}" = "true" ]      && [ "${SKIP_DB}" = "false" ]      && echo "  [X] Base de donnees -> ${DB_NAME} (ECRASE)" || echo "  [ ] Base de donnees"
[ "${HAS_UPLOADS}" = "true" ] && [ "${SKIP_UPLOADS}" = "false" ] && echo "  [X] public/uploads/ (ECRASE, sauvegarde de securite)" || echo "  [ ] public/uploads/"
[ "${HAS_VAR}" = "true" ]     && [ "${SKIP_VAR}" = "false" ]     && echo "  [X] var/ (payouts, billets, invoices, unsubscribed.json)" || echo "  [ ] var/"
[ "${HAS_ENV}" = "true" ]     && [ "${SKIP_SECRETS}" = "false" ] && echo "  [X] .env.local (ECRASE)" || echo "  [ ] .env.local"
[ "${HAS_CERT}" = "true" ]    && [ "${SKIP_SECRETS}" = "false" ] && echo "  [X] config/cert/ (ECRASE)" || echo "  [ ] config/cert/"
echo "----------------------------------------------------------------"
echo

if [ "${ASSUME_YES}" != "true" ]; then
    read -r -p "Confirmer la restauration ? (tapez 'oui' pour continuer) : " CONFIRM
    if [ "${CONFIRM}" != "oui" ]; then
        log "Restauration annulee par l'utilisateur"
        exit 0
    fi
fi

# --- 1. Secrets (en premier pour que la stack puisse demarrer si besoin) ---
if [ "${SKIP_SECRETS}" = "false" ]; then
    if [ "${HAS_ENV}" = "true" ]; then
        log "[secrets] Restauration de .env.local..."
        if [ -f "${SCRIPT_DIR}/.env.local" ]; then
            cp -p "${SCRIPT_DIR}/.env.local" "${SCRIPT_DIR}/.env.local.before_restore_$(date +%Y%m%d_%H%M%S)"
        fi
        cp -p "${EXTRACTED_DIR}/env.local" "${SCRIPT_DIR}/.env.local"
        chmod 600 "${SCRIPT_DIR}/.env.local"
        log "    OK"
    fi
    if [ "${HAS_CERT}" = "true" ]; then
        log "[secrets] Restauration de config/cert/..."
        mkdir -p "${SCRIPT_DIR}/config/cert"
        cp -rp "${EXTRACTED_DIR}/cert/." "${SCRIPT_DIR}/config/cert/"
        chmod -R go-rwx "${SCRIPT_DIR}/config/cert"
        log "    OK"
    fi
fi

# --- 2. Base de donnees ---
if [ "${HAS_DB}" = "true" ] && [ "${SKIP_DB}" = "false" ]; then
    log "[db] Verification du service ${DB_SERVICE}..."
    if [ ! -f "${COMPOSE_FILE}" ]; then
        err "Fichier docker-compose introuvable: ${COMPOSE_FILE}"
        exit 1
    fi
    if ! docker compose -f "${COMPOSE_FILE}" ps --services --status running 2>/dev/null | grep -qx "${DB_SERVICE}"; then
        err "Le service '${DB_SERVICE}' n'est pas demarre. Lancer: make start_prod"
        exit 1
    fi

    log "[db] Restauration de la base ${DB_NAME}..."
    if ! gunzip -c "${EXTRACTED_DIR}/db.sql.gz" \
            | docker compose -f "${COMPOSE_FILE}" exec -T "${DB_SERVICE}" \
                psql -U "${DB_USER}" -d "${DB_NAME}" -v ON_ERROR_STOP=1 >/dev/null; then
        err "Echec de la restauration PostgreSQL"
        exit 1
    fi
    log "    OK"
fi

# --- 3. public/uploads ---
if [ "${HAS_UPLOADS}" = "true" ] && [ "${SKIP_UPLOADS}" = "false" ]; then
    log "[uploads] Restauration de public/uploads..."
    if [ -d "${SCRIPT_DIR}/public/uploads" ]; then
        SAFETY="${SCRIPT_DIR}/public/uploads.before_restore_$(date +%Y%m%d_%H%M%S)"
        log "    Sauvegarde de securite: ${SAFETY}"
        mv "${SCRIPT_DIR}/public/uploads" "${SAFETY}"
    fi
    mkdir -p "${SCRIPT_DIR}/public"
    if ! tar -xzf "${EXTRACTED_DIR}/public_uploads.tar.gz" -C "${SCRIPT_DIR}/public"; then
        err "Echec d'extraction de public_uploads.tar.gz"
        exit 1
    fi
    log "    OK"
fi

# --- 4. var/ (PDFs generes, unsubscribed.json...) ---
if [ "${HAS_VAR}" = "true" ] && [ "${SKIP_VAR}" = "false" ]; then
    log "[var] Restauration de var/ (payouts, billets, invoices, unsubscribed.json)..."
    mkdir -p "${SCRIPT_DIR}/var"
    # Sauvegarde de securite des elements existants qui seront ecrases
    SAFETY_VAR="${SCRIPT_DIR}/var/.before_restore_$(date +%Y%m%d_%H%M%S)"
    mkdir -p "${SAFETY_VAR}"
    for item in payouts billets invoices unsubscribed.json; do
        if [ -e "${SCRIPT_DIR}/var/${item}" ]; then
            mv "${SCRIPT_DIR}/var/${item}" "${SAFETY_VAR}/"
        fi
    done
    # Si rien n'a ete sauvegarde, on supprime le dossier vide
    if [ -z "$(ls -A "${SAFETY_VAR}")" ]; then
        rmdir "${SAFETY_VAR}"
    else
        log "    Sauvegarde de securite: ${SAFETY_VAR}"
    fi
    if ! tar -xzf "${EXTRACTED_DIR}/var_state.tar.gz" -C "${SCRIPT_DIR}/var"; then
        err "Echec d'extraction de var_state.tar.gz"
        exit 1
    fi
    log "    OK"
fi

# --- 5. Purge du cache Symfony ---
# Indispensable: var/cache contient des fichiers compiles avec les anciens
# uid/gid et peut empecher PHP d'ecrire au demarrage suivant.
if [ -d "${SCRIPT_DIR}/var/cache" ]; then
    log "[cache] Purge de var/cache (sera reconstruit par PHP au demarrage)..."
    rm -rf "${SCRIPT_DIR}/var/cache"
    log "    OK"
fi

# --- 6. Chown final pour matcher l'utilisateur PHP du conteneur ---
if [ "${SKIP_CHOWN}" = "false" ]; then
    log "[chown] Application de '${RESTORE_OWNER}' sur les fichiers restaures..."
    CHOWN_TARGETS=()
    [ -d "${SCRIPT_DIR}/var" ]              && CHOWN_TARGETS+=("${SCRIPT_DIR}/var")
    [ -d "${SCRIPT_DIR}/public/uploads" ]   && CHOWN_TARGETS+=("${SCRIPT_DIR}/public/uploads")
    [ -d "${SCRIPT_DIR}/config/cert" ]      && CHOWN_TARGETS+=("${SCRIPT_DIR}/config/cert")
    [ -f "${SCRIPT_DIR}/.env.local" ]       && CHOWN_TARGETS+=("${SCRIPT_DIR}/.env.local")

    if [ ${#CHOWN_TARGETS[@]} -gt 0 ]; then
        chown -R "${RESTORE_OWNER}" "${CHOWN_TARGETS[@]}"
        log "    OK - cibles: ${CHOWN_TARGETS[*]}"
    else
        log "    Aucune cible a chown"
    fi
else
    log "[chown] Saut du chown final (--skip-chown)"
    log "    ATTENTION: PHP pourrait ne pas avoir les droits d'ecriture sur var/, public/uploads/..."
fi

log "=== Migration terminee avec succes ==="
log
log "Etapes recommandees post-migration:"
log "  1. make build_prod        (si l'image PHP n'a pas ete reconstruite avec le bon UID)"
log "  2. make start_prod        (si pas deja fait)"
log "  3. make migrate_prod      (appliquer les migrations Doctrine eventuelles)"
log "  4. make clear_prod        (vider le cache)"
log "  5. Verifier le bon fonctionnement de l'application"
