#!/usr/bin/env bash
#
# PATH shim : remplace `psql` par un appel dans le pod postgres via kubectl.
#
# Les pipelines appellent `psql ...` normalement ; run_all_pipelines.sh place ce
# répertoire en tête du PATH, donc tous les appels (y compris ceux des workers
# lancés en parallèle) passent par ici.
#
# Traduction effectuée :
#   psql -v X=1 -f fichier.sql
#     -> kubectl -n NS exec -i POD -- env PGPASSWORD=... psql -U ... -d ... -h ... -v X=1 < fichier.sql
#
# Le `-f <fichier>` est converti en redirection stdin car le fichier .sql est
# local (il n'existe pas dans le pod).
#
# ── Robustesse ────────────────────────────────────────────────────────────────
# L'API server ferme les flux `exec` sous charge ("unexpected EOF"). On distingue
# donc deux familles d'échec :
#
#   * erreur SQL   : kubectl affiche "command terminated with exit code N" et
#                    renvoie N. C'est une vraie erreur -> propagée telle quelle,
#                    JAMAIS retentée (sinon on rejouerait du DDL).
#   * panne de     : "unexpected EOF", "error sending request", timeout, etc.
#     transport      -> nouvelle tentative avec backoff.
#
# Le retry est sûr : process_one_range() tient dans une seule transaction, donc
# une coupure annule intégralement la plage (elle repasse PENDING), et les INSERT
# sont en ON CONFLICT DO NOTHING.
#
# La sortie standard est tamponnée et n'est émise qu'en cas de succès, pour que
# les workers qui parsent `| tail -n 1` ne voient jamais la sortie d'un essai raté.
#
# PSQL_MODE=local court-circuite le shim et rappelle le vrai psql.
#
set -uo pipefail

SHIM_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SHIM_DIR/../db.config.sh"

# ── Séparation des arguments : on extrait le -f pour en faire un stdin ────────
args=()
sql_file=""
while (( $# )); do
  case "$1" in
    -f)          sql_file="${2:-}"; shift 2 ;;
    --file)      sql_file="${2:-}"; shift 2 ;;
    --file=*)    sql_file="${1#--file=}"; shift ;;
    -f*)         sql_file="${1#-f}"; shift ;;
    *)           args+=("$1"); shift ;;
  esac
done

if [[ -n "$sql_file" && ! -r "$sql_file" ]]; then
  echo "psql(shim): fichier SQL introuvable : $sql_file" >&2
  exit 2
fi

# ── Mode local : on rappelle le vrai psql (PATH sans le shim) ────────────────
if [[ "${PSQL_MODE:-kubectl}" == "local" ]]; then
  PATH="$(printf '%s' "$PATH" | tr ':' '\n' | grep -vxF "$SHIM_DIR" | paste -sd: -)"
  export PATH
  if [[ -n "$sql_file" ]]; then
    exec psql "${args[@]+"${args[@]}"}" -f "$sql_file"
  else
    exec psql "${args[@]+"${args[@]}"}"
  fi
fi

# ── Construction de la commande kubectl ──────────────────────────────────────
# `-i` n'est ajouté QUE si on a un fichier à injecter. Sans lui, kubectl n'ouvre
# pas de flux stdin du tout : un flux de moins par appel, et plus d'erreur
# "read /dev/stdin: bad file descriptor" sur les appels en -c.
cmd=(kubectl)
[[ -n "${K8S_CONTEXT:-}" ]] && cmd+=(--context "$K8S_CONTEXT")
cmd+=(-n "$K8S_NAMESPACE" exec)
[[ -n "$sql_file" ]] && cmd+=(-i)
cmd+=("$K8S_POD")
[[ -n "${K8S_CONTAINER:-}" ]] && cmd+=(-c "$K8S_CONTAINER")
cmd+=(-- env "PGPASSWORD=${PGPASSWORD}" psql
      -U "$USER" -d "$DB" -h "$HOST" -p "$PORT"
      "${args[@]+"${args[@]}"}")

max_try="${KUBECTL_RETRIES:-5}"
delay="${KUBECTL_RETRY_DELAY:-5}"
tmo="${KUBECTL_TIMEOUT:-0}"   # 0 = pas de timeout (create/finalise peuvent durer des heures)

out_f="$(mktemp)"; err_f="$(mktemp)"
trap 'rm -f "$out_f" "$err_f"' EXIT

flush() { cat "$out_f"; cat "$err_f" >&2; }

attempt=0
while : ; do
  attempt=$(( attempt + 1 ))
  : > "$out_f" ; : > "$err_f"

  # Un timeout n'est pose que si KUBECTL_TIMEOUT > 0. On ne l'active que sur les
  # appels courts et rejouables (boucle de copie) : interrompre un finalise en
  # pleine reconstruction d'index le ferait recommencer indefiniment.
  runner=()
  (( tmo > 0 )) && runner=(timeout -k 10 "$tmo")

  if [[ -n "$sql_file" ]]; then
    "${runner[@]+"${runner[@]}"}" "${cmd[@]}" < "$sql_file" > "$out_f" 2> "$err_f"
  else
    "${runner[@]+"${runner[@]}"}" "${cmd[@]}" < /dev/null > "$out_f" 2> "$err_f"
  fi
  rc=$?

  # Succès
  if [[ $rc -eq 0 ]]; then
    flush
    exit 0
  fi

  # Erreur renvoyée par psql lui-même : ne jamais retenter
  if grep -q "command terminated with exit code" "$err_f"; then
    flush
    exit "$rc"
  fi

  # Panne de transport
  if (( attempt >= max_try )); then
    flush
    echo "psql(shim): transport en echec apres ${attempt} tentatives (rc=${rc})" >&2
    exit "$rc"
  fi

  echo "psql(shim): transport KO (rc=${rc}, essai ${attempt}/${max_try}), relance dans ${delay}s" >&2
  sed -n '1,3p' "$err_f" >&2
  sleep "$delay"
  delay=$(( delay * 2 ))
done
