#!/usr/bin/env bash
#
# tools/up-dev.sh — ciclo de vida do ambiente de desenvolvimento.
#
# Premissa (mesma do deploy): **backup → build limpo → down/up → otimizar**.
#   - backup: `tools/db-backup.sh` (pulado se o banco ainda não existe);
#   - build limpo: `docker compose build --no-cache` só quando `NO_CACHE=1` (dev);
#   - down/up: `down` mantém volumes, `up` recria os containers com a imagem nova;
#   - otimizar: `optimize:clear` → `core:cache-modules` → `optimize`, abortando na 1ª falha.
#
# Validação **não** é responsabilidade do `up` (são alvos distintos): rode `make check`
# (ou `make check E2E=1`) separadamente, quando fizer sentido.
#
# Uso: tools/up-dev.sh [--dry-run]
set -Eeuo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"

DRY_RUN=0
for arg in "$@"; do
    case "$arg" in
        --dry-run) DRY_RUN=1 ;;
        -h | --help) sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
        *) echo "up-dev: flag desconhecida: $arg" >&2; exit 2 ;;
    esac
done

PROJECT="$(bash "$ROOT/tools/project-name.sh")"
# shellcheck source=tools/runtime.sh
. "$ROOT/tools/runtime.sh"
command -v "${ENGINE[0]}" >/dev/null 2>&1 || { echo "up-dev: ${ENGINE[0]} indisponível." >&2; exit 1; }
COMPOSE=("${ENGINE[@]}" compose -p "$PROJECT" --env-file .env "${COMPOSE_FILES[@]}")

step() { printf '\n== %s ==\n' "$*"; }

run() {
    printf '+'
    printf ' %q' "$@"
    printf '\n'
    [ "$DRY_RUN" = 1 ] && return 0
    "$@"
}

# 0) Ambiente determinístico: `.env` presente, porta da cópia e `APP_URL` coerentes (e, se
#    `--instance` foi usado, a stack já é a isolada).
step "0/6 ambiente (.env, porta, APP_URL)"
run bash "$ROOT/tools/dev-env.sh"

# 0.5) Identidade (ADR-055): stacks deste checkout com nome antigo são adotadas ANTES de
#      subir a nova — dois postgres no mesmo PGDATA corrompem o banco. Se o cluster está num
#      volume anônimo (layout antigo do postgres:18), ele é copiado para o DATA_PATH.
step "0.5/6 identidade da stack"
CHECKOUT_DIR="$(cd "$ROOT" && pwd -P)"
LEGACY=()
while IFS= read -r old; do
    [ -n "$old" ] || continue
    [ "$old" = "$PROJECT" ] && continue
    LEGACY+=("$old")
done < <("${ENGINE[@]}" ps -a --filter "label=com.docker.compose.project.working_dir=${CHECKOUT_DIR}" --format '{{.Label "com.docker.compose.project"}}' | sort -u)

if [ "${#LEGACY[@]}" -gt 1 ]; then
    echo "up-dev: mais de uma stack deste checkout (${LEGACY[*]}); remova as antigas com 'compose -p <nome> down'." >&2
    exit 1
fi

# A stack a inspecionar: a antiga (se houver) ou a atual com containers (instalação pinada
# ou deploy anterior). `start` não reescreve o mount antigo; um `up -d` trocaria o PGDATA.
CHECK_PROJECT="${LEGACY[0]:-}"
if [ -z "$CHECK_PROJECT" ] && [ -n "$("${ENGINE[@]}" compose -p "$PROJECT" --env-file .env "${COMPOSE_FILES[@]}" ps -aq 2>/dev/null)" ]; then
    CHECK_PROJECT="$PROJECT"
fi
if [ -n "$CHECK_PROJECT" ] && [ "$DRY_RUN" = 0 ]; then
    run "${ENGINE[@]}" compose -p "$CHECK_PROJECT" --env-file .env "${COMPOSE_FILES[@]}" start
    adopt_rc=0
    bash "$ROOT/tools/pgdata-adopt.sh" --project "$CHECK_PROJECT" --check || adopt_rc=$?
    case "$adopt_rc" in
        0) : ;;
        10) run bash "$ROOT/tools/pgdata-adopt.sh" --project "$CHECK_PROJECT" --copy ;;
        11)
            echo "up-dev: o bind do DATA_PATH já tem um cluster e o container usa um volume anônimo; resolva manualmente (ADR-055)." >&2
            exit 1
            ;;
        12)
            echo "up-dev: o PGDATA não está no bind nem no volume (bind substituído em runtime); restaure o backup manualmente (ADR-055)." >&2
            exit 1
            ;;
        *)
            echo "up-dev: não foi possível verificar o PGDATA (rc=${adopt_rc})." >&2
            exit 1
            ;;
    esac
fi

if [ "${#LEGACY[@]}" -gt 0 ]; then
    for old in "${LEGACY[@]}"; do
        run "${ENGINE[@]}" compose -p "$old" --env-file .env "${COMPOSE_FILES[@]}" down --remove-orphans
    done
fi

# 1) Stack atual precisa estar de pé para o backup.
step "1/6 subir o stack atual"
run "${COMPOSE[@]}" up -d --remove-orphans

# 2) Backup antes de qualquer mudança (primeiro run é pulado pelo próprio script).
step "2/6 backup do banco"
run bash "$ROOT/tools/db-backup.sh"

# 3) Build limpo só sob demanda em dev (prod sempre usa --no-cache).
step "3/6 build da imagem"
if [ "${NO_CACHE:-0}" = "1" ]; then
    run "${COMPOSE[@]}" build --no-cache php-fpm queue
else
    run "${COMPOSE[@]}" build php-fpm queue
fi

# 4) Troca dos containers com a imagem recém-construída.
step "4/6 down/up"
run "${COMPOSE[@]}" down --remove-orphans
run "${COMPOSE[@]}" up -d --remove-orphans

# 5) Otimização completa e sequencial (fail-fast).
step "5/6 otimizar"
run "${COMPOSE[@]}" exec -T php-fpm sh -c \
    'php artisan optimize:clear && php artisan core:cache-modules && php artisan optimize'

printf '\nup-dev: OK (%s)\n' "$([ "$DRY_RUN" = 1 ] && echo 'dry-run' || echo 'ambiente atualizado')"
