#!/usr/bin/env bash
#
# Gera, NA SUA MÁQUINA, a cópia que o ambiente de teste público
# (sistema.franquiabv.xyz) carrega. Ver docs/ambiente-teste.md.
#
#     bash scripts/exportar-base-teste.sh                  # de franquiabv_dev para base-teste.sql.gz
#     bash scripts/exportar-base-teste.sh ORIGEM SAIDA.sql.gz
#
# A ORIGEM SÓ É LIDA. O trabalho acontece numa cópia, `franquiabv_exportacao_<pid>`,
# no MySQL do docker compose local, e a cópia é apagada no fim:
#
#   1. a origem é copiada inteira para a cópia;
#   2. a cópia passa pelo anonimizador (scripts/anonimizar.sh) -- que, desde
#      25/09/2026, refaz também as tags da busca: antes, 241.683 clientes da
#      cópia local tinham ali o nome, o telefone e o e-mail reais;
#   3. e pelo endurecimento do teste público (scripts/endurecer-base-teste.sql):
#      ids do Asaas, chaves, hashes previsíveis, nomes em texto, texto livre;
#   4. a régua (scripts/conferir-base-teste.sql) tem de dar a linha exata --
#      senão, nada é exportado;
#   5. só então o dump sai, comprimido -- para um .parcial, que só ganha o nome
#      final completo e íntegro.
#
# Só o artefato conferido atravessa a rede (docs/ambiente-homologacao.md, "A
# ordem importa"): o dump cru nunca sai daqui.
# ------------------------------------------------------------------------------

set -euo pipefail

# O EXPORTADOR RODA DE UMA CÓPIA DE SI MESMO, como o deploy: o bash lê o arquivo
# aos poucos, e trocar de ramo no meio de uma exportação de uma hora fazia o
# resto sair lido do arquivo novo (quarta revisão do deploy).
if [ -z "${EXPORTAR_RAIZ:-}" ]; then
    EXPORTAR_RAIZ="$(cd "$(dirname "$0")/.." && pwd)"
    EXPORTAR_COPIA="$(mktemp)"
    export EXPORTAR_RAIZ EXPORTAR_COPIA
    cp "$0" "$EXPORTAR_COPIA"
    exec bash "$EXPORTAR_COPIA" "$@"
fi
cd "$EXPORTAR_RAIZ"
# A cópia de si sai também quando o script para antes da limpeza de verdade.
trap 'rm -f "$EXPORTAR_COPIA"' EXIT
ANONIMIZADOR=""
MODELO=""
ENDURECIMENTO=""
REGUA=""

ORIGEM="${1:-franquiabv_dev}"
SAIDA="${2:-base-teste.sql.gz}"
# NOME PRÓPRIO POR EXECUÇÃO: duas exportações ao mesmo tempo apagavam a cópia
# uma da outra.
TRABALHO="franquiabv_exportacao_$$"
PARCIAL="$SAIDA.parcial.$$"

falhar() { printf '\nABORTADO: %s\n' "$*" >&2; exit 1; }
passo()  { printf '\n==> %s\n' "$*"; }

case "$ORIGEM" in
    *produc*|*prod) falhar "a origem '$ORIGEM' tem nome de produção. A exportação parte da base LOCAL." ;;
esac
case "$ORIGEM" in
    franquiabv_exportacao*) falhar "a origem não pode ser uma cópia de trabalho da exportação." ;;
esac
case "$SAIDA" in
    *.sql.gz) ;;
    *) falhar "a saída precisa terminar em .sql.gz (é o que o deploy aceita)." ;;
esac
[ ! -e "$SAIDA" ] || falhar "$SAIDA já existe; apague ou escolha outro nome."

# O SELO DA CÓPIA: o sha256 dos arquivos que lhe dão forma, sem os CR. Vai na
# primeira linha do dump, e o `deploy-teste.sh base` só carrega a cópia cujo
# selo é o da árvore de lá -- a cópia antiga, que fica no $HOME com o mesmo
# nome, não entra mais por engano (terceira revisão do deploy).
#
# É O MESMO CÁLCULO de scripts/deploy-teste.sh (ARQUIVOS_DA_COPIA e
# selo_da_copia). Se os dois divergirem, nenhuma cópia entra: a falha é fechada.
ARQUIVOS_DA_COPIA="scripts/anonimizar.sql.modelo scripts/anonimizar.sh scripts/exportar-base-teste.sh scripts/endurecer-base-teste.sql scripts/conferir-base-teste.sql"
# Com "congelados", o selo é o das cópias que rodam (ver abaixo), e não o da árvore.
selo_da_copia() {
    local f arq
    for f in $ARQUIVOS_DA_COPIA; do
        arq="$f"
        [ "${1:-}" != congelados ] || arq="$(congelado "$f")"
        printf '%s %s\n' "$f" "$(tr -d '\r' < "$arq" | sha256sum | cut -c1-64)"
    done | sha256sum | cut -c1-64
}
# A cópia congelada de cada arquivo do selo. Um arquivo novo na lista, sem cópia
# aqui, lê um caminho que não existe: o selo sai outro, e nada é exportado.
congelado() {
    case "$1" in
        scripts/anonimizar.sql.modelo)   printf '%s' "$MODELO" ;;
        scripts/anonimizar.sh)           printf '%s' "$ANONIMIZADOR" ;;
        scripts/exportar-base-teste.sh)  printf '%s' "$EXPORTAR_COPIA" ;;
        scripts/endurecer-base-teste.sql) printf '%s' "$ENDURECIMENTO" ;;
        scripts/conferir-base-teste.sql) printf '%s' "$REGUA" ;;
        *) printf '%s' "/sem-copia-congelada/$1" ;;
    esac
}

# DO CÓDIGO COMMITADO: com mudança local nestes arquivos, o selo não seria o de
# commit nenhum, e o servidor recusaria a cópia -- depois de uma hora de
# exportação.
# shellcheck disable=SC2086
[ -z "$(git status --porcelain -- $ARQUIVOS_DA_COPIA)" ] \
    || falhar "há mudança não commitada no que dá forma à cópia: $(git status --porcelain -- $ARQUIVOS_DA_COPIA | tr '\n' ' '). Commite (e leve ao servidor) antes de exportar."
SELO="$(selo_da_copia)"
[[ "$SELO" =~ ^[0-9a-f]{64}$ ]] || falhar "não consegui calcular o selo da cópia."
# O COMMIT DO SELO, para a mensagem do fim: o HEAD de lá podia ser outro.
COMMIT="$(git rev-parse --short HEAD)"

docker compose ps --status running --services 2>/dev/null | grep -q '^mysql$' \
    || falhar "o serviço mysql do docker compose local não está no ar. Rode antes: docker compose up -d"

# Dentro do contêiner, com MYSQL_PWD: gigabytes não passam pelo cano do host, e
# a senha não vai para a linha de comando.
no_mysql() { docker compose exec -T -e MYSQL_PWD=root mysql "$@"; }

no_mysql mysql -uroot -N -B -e "SELECT COUNT(*) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = '$ORIGEM'" \
    | grep -qx 1 || falhar "a base de origem '$ORIGEM' não existe no MySQL local."

# CÓPIAS DE EXECUÇÕES MORTAS: a limpeza não roda num kill -9. Não as apago --
# outra exportação pode estar no meio --, mas digo que existem: cada uma é uma
# cópia inteira da base, e até o anonimizador passar ela tem o dado real.
velhas="$(no_mysql mysql -uroot -N -B -e "SELECT GROUP_CONCAT(SCHEMA_NAME) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME LIKE 'franquiabv\_exportacao\_%'")"
[ "$velhas" = "NULL" ] || printf '    !!  cópias de trabalho de outras exportações: %s -- se nenhuma estiver rodando, apague com DROP DATABASE.\n' "$velhas" >&2

# A CÓPIA DE TRABALHO SAI SEMPRE, deu certo ou não: ela tem, até o passo 2, o
# que a origem tem.
limpar() {
    no_mysql mysql -uroot -e "DROP DATABASE IF EXISTS \`$TRABALHO\`" >/dev/null 2>&1 || true
    rm -f "$PARCIAL" ${ANONIMIZADOR:+"$ANONIMIZADOR"} ${MODELO:+"$MODELO"} \
        ${ENDURECIMENTO:+"$ENDURECIMENTO"} ${REGUA:+"$REGUA"} "$EXPORTAR_COPIA"
}
trap limpar EXIT

# TUDO O QUE DÁ FORMA À CÓPIA, DESTE INSTANTE, é o que roda: o anonimizador e o
# modelo dele, o endurecimento e a régua saem de cópias tiradas agora (este
# script já roda de uma). Lidos da árvore na hora, um checkout no meio da
# exportação rodava os de OUTRO código, e o dump saía com o selo deste -- o
# endurecimento e a régua desde a quarta revisão; o anonimizador desde a
# quinta, em que trocar de ramo e voltar antes do fim passava pela conferência.
ANONIMIZADOR="$(mktemp)"
MODELO="$(mktemp)"
ENDURECIMENTO="$(mktemp)"
REGUA="$(mktemp)"
cp scripts/anonimizar.sh "$ANONIMIZADOR"
cp scripts/anonimizar.sql.modelo "$MODELO"
cp scripts/endurecer-base-teste.sql "$ENDURECIMENTO"
cp scripts/conferir-base-teste.sql "$REGUA"
# O SELO É O DAS CÓPIAS, e tem de ser o da árvore commitada lida acima.
[ "$(selo_da_copia congelados)" = "$SELO" ] || falhar "a árvore mudou enquanto eu a lia. Nada foi exportado."

passo "copiando $ORIGEM para $TRABALHO (a origem só é lida; demora)"
no_mysql mysql -uroot -e "DROP DATABASE IF EXISTS \`$TRABALHO\`; CREATE DATABASE \`$TRABALHO\` CHARACTER SET utf8mb4"
# COM PIPEFAIL LÁ DENTRO (o sh da imagem é o bash): sem ele, o status do cano
# era o do mysql, e um mysqldump que parasse entre duas tabelas deixava a cópia
# pela metade sem erro. E o número de tabelas tem de bater com o da origem.
no_mysql sh -c "set -o pipefail; mysqldump -uroot --no-tablespaces --single-transaction --set-gtid-purged=OFF '$ORIGEM' | mysql -uroot --default-character-set=utf8mb4 '$TRABALHO'"
contar_tabelas() {
    no_mysql mysql -uroot -N -B -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$1'"
}
# NÚMERO, e maior que zero: "vazio = vazio" aprovava quando não se conseguia contar.
na_origem="$(contar_tabelas "$ORIGEM")"
na_copia="$(contar_tabelas "$TRABALHO")"
[[ "$na_origem" =~ ^[1-9][0-9]*$ ]] && [ "$na_origem" = "$na_copia" ] \
    || falhar "a cópia não tem as mesmas tabelas da origem ($na_copia de $na_origem). Nada foi exportado."

passo "anonimizando a cópia (scripts/anonimizar.sh)"
printf 'sim\n' | ANONIMIZAR_RAIZ="$EXPORTAR_RAIZ" ANONIMIZAR_MODELO="$MODELO" sh "$ANONIMIZADOR" "$TRABALHO"

passo "endurecendo a cópia (scripts/endurecer-base-teste.sql)"
no_mysql mysql -uroot --default-character-set=utf8mb4 "$TRABALHO" < "$ENDURECIMENTO"

passo "conferindo (scripts/conferir-base-teste.sql)"
# utf8mb4: a régua compara com texto acentuado. E a FORMA da linha, como o
# deploy cobra: todos zero, menos o último, que é 1.
linha="$(no_mysql mysql -uroot --default-character-set=utf8mb4 -N -B "$TRABALHO" < "$REGUA")"
forma='^(0 ){6,}1$'
[[ "$linha" =~ $forma ]] \
    || falhar "a cópia não passou: '$linha' -- todos têm de ser zero, menos o último, que é 1 (a legenda está em scripts/conferir-base-teste.sql). Nada foi exportado."
printf '    ok  %s\n' "$linha"

# AS CÓPIAS QUE RODARAM AINDA SÃO AS DO SELO, e a árvore também, sem mudança
# local. O que rodou foram as cópias; a árvore que mudou no meio quer dizer que
# alguém mexia no que dá forma à cópia durante a exportação, e na dúvida nada sai.
# shellcheck disable=SC2086
[ "$(selo_da_copia congelados)" = "$SELO" ] && [ "$(selo_da_copia)" = "$SELO" ] \
    && [ -z "$(git status --porcelain -- $ARQUIVOS_DA_COPIA)" ] \
    || falhar "o que dá forma à cópia mudou durante a exportação (troca de ramo? edição?). Nada foi exportado."

# PARA UM .parcial, E SÓ DEPOIS O NOME FINAL: um mysqldump que morresse no meio
# deixava um .gz íntegro e cortado com o nome de pronto. O '-- Dump completed'
# é a última linha que o mysqldump escreve, e o `base` também a exige.
passo "exportando para $SAIDA (selo ${SELO:0:12}…)"
{ printf -- '-- franquiabv-copia: %s\n' "$SELO"
  no_mysql mysqldump -uroot --no-tablespaces --single-transaction --set-gtid-purged=OFF "$TRABALHO"; } | gzip > "$PARCIAL"
gzip -t "$PARCIAL"
[ "$(gunzip -c "$PARCIAL" | tail -n 1 | cut -c1-17)" = "-- Dump completed" ] \
    || falhar "o dump não terminou com '-- Dump completed'. Nada foi exportado."
[ "$( (set +o pipefail; gunzip -c "$PARCIAL" | head -n 1) )" = "-- franquiabv-copia: $SELO" ] \
    || falhar "o dump saiu sem o selo na primeira linha. Nada foi exportado."
mv "$PARCIAL" "$SAIDA"

cat <<FIM

Pronto: $SAIDA ($(du -h "$SAIDA" | cut -f1)), conferido, e selado pelo código de
$COMMIT. O servidor só a carrega com esse mesmo código (ou outro com o mesmo selo).
Leve ao BVIP e carregue:

    scp $SAIDA usuario@bvip:~/
    bash scripts/deploy-teste.sh base ~/$(basename "$SAIDA")      # no BVIP, no clone

Os colaboradores saem daqui com a senha de desenvolvimento. O 'base' tira o site
do ar, troca a senha por uma aleatória, e o site só volta pelo 'publicar'.
FIM
