#!/usr/bin/env bash
# Накатывает миграции всех сервисов и создаёт бакет MinIO.
#
# Для установленного prod-стека предпочтительно:
#   ush platform migrate --dir /opt/unishift
# Этот скрипт остаётся для git-checkout devops/prod (и devops/dev — копия).
#
# Почему не `docker compose --profile migrate up`:
# профили в compose ДОБАВЛЯЮТСЯ к обычному набору сервисов, поэтому такая
# команда поднимает заодно весь стек и мешает в одной сессии долгоживущие
# контейнеры с одноразовыми. Здесь каждый прогон идёт через `run --rm`,
# который сам за собой убирает.
#
#   ./devops/prod/migrate.sh

set -uo pipefail
# Compose-файл, .env и монтируемые конфиги лежат рядом со скриптом, а пути
# внутри compose заданы относительно этой папки — поэтому работаем из неё,
# откуда бы скрипт ни запустили.
cd "$(dirname "$0")"

# gateway, nats-bridge и client баз не используют.
SERVICES=(
  auth spaces documents command-manager scheduler
  storage env-manager flow-manager spark-grid ai-router
  task-tracker notifications mcp-gateway data-tables messaging collatio concierge guard notebooks proxy-manager
)

# Ждём docker health=healthy. На холодном старте postgres гоняет init
# (CREATE EXTENSION + десяток CREATE DATABASE) — это легко > 60s на слабом VPS.
wait_healthy() {
  local svc="$1"
  local seconds="${2:-180}"
  local health=""
  echo "Жду готовности $svc (до ${seconds}s)…"
  for _ in $(seq "$seconds"); do
    health=$(docker compose ps "$svc" --format '{{.Health}}' 2>/dev/null || true)
    # Compose иногда отдаёт пусто, пока healthcheck ещё не отработал.
    if [ "$health" = "healthy" ]; then
      echo "  $svc: healthy"
      return 0
    fi
    sleep 1
  done
  echo "Ошибка: $svc не стал healthy за ${seconds}s (сейчас: ${health:-unknown})" >&2
  echo "--- docker compose ps ---" >&2
  docker compose ps "$svc" >&2 || true
  echo "--- последние логи ---" >&2
  docker compose logs --tail 80 "$svc" >&2 || true
  echo "--- health inspect ---" >&2
  local cid
  cid=$(docker compose ps -q "$svc" 2>/dev/null || true)
  if [ -n "$cid" ]; then
    docker inspect --format '{{json .State.Health}}' "$cid" >&2 || true
  fi
  echo >&2
  echo "Если том инициализировался с ошибкой: docker compose down -v && ./migrate.sh" >&2
  return 1
}

echo "Поднимаю postgres и minio…"
docker compose up -d postgres minio >/dev/null || exit 1

wait_healthy postgres 180 || exit 1
wait_healthy minio 120 || exit 1

# На уже инициализированном томе init-скрипты postgres не перезапускаются.
# При добавлении нового сервиса (например notifications) создаём БД явно.
ensure_database() {
  local db="$1"
  docker compose exec -T postgres \
    sh -c "psql -U \"\$POSTGRES_USER\" -d postgres -tc \"SELECT 1 FROM pg_database WHERE datname='$db'\" | grep -q 1 \
      || psql -U \"\$POSTGRES_USER\" -d postgres -c \"CREATE DATABASE $db ENCODING 'UTF8';\"" \
    >/dev/null
}

echo "Проверяю наличие баз…"
for db in \
  auth_db spaces_db documents_db command_manager_db scheduler_db \
  storage_db env_manager_db flow_manager_db spark_grid ai_router_db \
  tasks_db notifications_db mcp_gateway_db data_tables_db datasets_db messaging_db collatio_db concierge_db guard_db notebooks proxy_manager_db
do
  ensure_database "$db" || {
    echo "Ошибка: не удалось создать/проверить базу $db" >&2
    exit 1
  }
done

echo "Создаю бакет…"
# -T и </dev/null: миграции неинтерактивны, а главное — `run` иначе читает
# stdin. Под `curl … install.sh | bash` stdin это сам скрипт install.sh, и
# `run` проглотил бы его остаток (шаги up и вывод), оборвав установку молча.
docker compose --profile migrate run --rm -T minio-init </dev/null 2>&1 | tail -1

failed=()
for svc in "${SERVICES[@]}"; do
  # Судим по КОДУ ВОЗВРАТА контейнера, а не по тексту: миграционный бинарник
  # выходит с 0 при успехе и ненулём при сбое. Грепать вывод ненадёжно —
  # посторонние нефатальные строки (в т.ч. про кэш) маскировали настоящую
  # ошибку, а при сбое усечённый греп её прятал.
  out=$(docker compose --profile migrate run --rm -T "${svc}-migrate" </dev/null 2>&1)
  code=$?
  if [ "$code" -eq 0 ]; then
    printf '  %-18s OK (%s)\n' "$svc" "$(grep -c 'Applied migration' <<<"$out")"
  else
    printf '  %-18s СБОЙ (exit %s)\n' "$svc" "$code"
    # Полный вывод контейнера — чтобы причина была видна сразу.
    sed 's/^/      /' <<<"$out"
    failed+=("$svc")
  fi
done

if [ ${#failed[@]} -gt 0 ]; then
  echo
  echo "Не прошли: ${failed[*]}"
  exit 1
fi

echo
echo "Все миграции применены. Дальше: docker compose up -d"
