#!/usr/bin/env bash
#
# Gera o `.env.e2e` — o ambiente da suíte Playwright, apontado para o Supabase
# LOCAL.
#
# ═══ POR QUE ESTE ARQUIVO EXISTE ═══
#
# O `.env.local` de um checkout de trabalho aponta para o Supabase de PRODUÇÃO
# (é com ele que se desenvolve). O `playwright.config.ts` sobe o app com
# `next start`, que carrega `.env.local`. Resultado, medido em 2026-08-06:
# `pnpm test:e2e` escrevia organizações, usuários e agentes de teste **no banco
# real** — o teste passava, e o estrago era invisível.
#
# A saída não é "lembrar de trocar o .env.local antes de testar": é exatamente o
# tipo de disciplina que falha uma vez e ninguém percebe.
#
# ⚠️ ISTO SOZINHO NÃO BASTA. Os scripts de seed liam `.env.local` DIRETO do
# disco, ignorando `process.env` — então nem o env do webServer os alcançava.
# O conserto do outro lado é `scripts/lib/env-de-teste.ts`, que faz `process.env`
# vencer. Os dois juntos é que fecham o caminho.
#
# Uso:
#   pnpm e2e:env                      # (re)cria o .env.e2e
#   pnpm e2e:build && pnpm test:e2e   # build embute NEXT_PUBLIC_*, ver e2e-build.sh
set -uo pipefail
set -e
CHAVE_CPF=""; CHAVE_WAHA=""; CHAVE_AI=""

cd "$(dirname "$0")/.."

# `supabase` do PATH quando existe (é o que o CI instala, via supabase/setup-cli),
# `npx supabase` como plano B para a máquina do dev. Insistir no `npx` custaria um
# download do registry a cada uma das três chamadas abaixo — a CLI não é
# dependência deste projeto.
SUPABASE="supabase"
command -v supabase >/dev/null 2>&1 || SUPABASE="npx supabase"

if ! $SUPABASE status >/dev/null 2>&1; then
  echo "==> O Supabase local não está de pé. Rode 'npx supabase start' antes." >&2
  exit 1
fi

# Chaves de cifra de 32 bytes. Reaproveita as do .env.e2e atual quando ele já
# existe: regenerá-las a cada chamada tornaria ilegível toda credencial que o
# banco de teste já guardou.
if [ -f .env.e2e ]; then
  CHAVE_CPF="$(grep -E '^CPF_ENCRYPTION_KEY=' .env.e2e | cut -d= -f2-)"
  CHAVE_WAHA="$(grep -E '^WAHA_BYO_ENCRYPTION_KEY=' .env.e2e | cut -d= -f2-)"
  CHAVE_AI="$(grep -E '^AI_CRED_AES_KEY=' .env.e2e | cut -d= -f2-)"
fi
[ "${#CHAVE_CPF}" -ge 44 ] || CHAVE_CPF="$(openssl rand -base64 32)"
[ "${#CHAVE_WAHA}" -ge 44 ] || CHAVE_WAHA="$(openssl rand -base64 32)"
[ "${#CHAVE_AI}" -ge 44 ] || CHAVE_AI="$(openssl rand -base64 32)"
# A chave de cifra `nuvemshop_oauth_key` NÃO é variável de ambiente: ela mora em
# `private.app_secrets`, no BANCO, e é de lá que `fn_encrypt_oauth` a lê. O nome
# esconde o alcance — ela cifra também o segredo HMAC de uma fonte de captação
# (`lib/webhooks/secrets.ts`) e a credencial do WAHA.
#
# O `install.sh` a grava em TODA instalação (`_common.sh`), e o `local-stack.sh`
# faz o mesmo na stack local. O rig do e2e não fazia, e por isso media um
# produto que não existe: "Gerar segredo" respondia 422 `encryption_unavailable`
# aqui e funcionava na VPS do cliente. Medido em 2026-09-30, pela tela.
#
# A chave em vigor é lida do próprio banco, não do `.env.e2e`: o banco é a fonte
# da verdade, e regenerá-la tornaria ilegível todo segredo que o banco de teste
# já guarda.
psql_no_banco() {
  if command -v psql >/dev/null 2>&1; then
    psql "$1" -v ON_ERROR_STOP=1 -q -At -c "$2" 2>/dev/null
  elif command -v docker >/dev/null 2>&1; then
    docker run --rm --network host postgres:15-alpine \
      psql "$1" -v ON_ERROR_STOP=1 -q -At -c "$2" 2>/dev/null
  else
    return 1
  fi
}

ENVOUT="$($SUPABASE status -o env 2>/dev/null)"
# O `|| true` no fim não é decoração: sob `set -e` + `pipefail`, um `grep` sem
# casamento (chave que o stack não devolveu) derruba o script AQUI, calado, antes
# das guardas abaixo. Quem não tem a chave precisa chegar na recusa explicada.
ler() { printf '%s\n' "$ENVOUT" | grep "^$1=" | cut -d= -f2- | tr -d '"' || true; }

API_URL="$(ler API_URL)"
ANON="$(ler ANON_KEY)"
SERVICE="$(ler SERVICE_ROLE_KEY)"
# A URL do Postgres sai do MESMO status do stack que está de pé — host E porta.
# Até a #1091 ela era um literal com a porta padrão do Supabase local: com dois
# stacks no ar (cada checkout tem o próprio `project_id` e a própria faixa de
# portas), os seeds que abrem conexão DIRETA escreviam no banco da OUTRA sessão —
# conexão válida, schema idêntico, suíte verde, estrago invisível. É a mesma
# família do `.env.local` de produção que este arquivo existe para impedir.
DB_URL="$(ler DB_URL)"

if [ -z "$API_URL" ] || [ -z "$ANON" ] || [ -z "$SERVICE" ]; then
  echo "==> Não consegui ler as chaves do stack local (API_URL/ANON_KEY/SERVICE_ROLE_KEY)." >&2
  exit 1
fi

# Guarda contra o erro que este arquivo existe para impedir. Se o `supabase
# status` devolver um host remoto (config apontada para um projeto linkado, por
# exemplo), falhar aqui é melhor do que gerar um `.env.e2e` que manda a suíte
# para a nuvem — o modo de falha silencioso é o caro.
case "$API_URL" in
  http://127.0.0.1:*|http://localhost:*) ;;
  *)
    echo "==> RECUSADO: o stack local respondeu com uma URL que não é local: $API_URL" >&2
    exit 1
    ;;
esac

# Mesmo raciocínio do guard acima, agora para o Postgres. Sem `DB_URL` no status
# não há como saber a porta DESTE stack, e gravar a padrão é exatamente o defeito
# da #1091 — então aqui é recusa declarada, não chute. O modo de falha silencioso
# é o caro: um `.env.e2e` plausível apontando para o banco errado não dá erro
# nenhum.
if [ -z "$DB_URL" ]; then
  echo "==> Não consegui ler a DB_URL do stack local (o 'supabase status -o env' não trouxe DB_URL)." >&2
  echo "    Sem ela, a alternativa seria chutar a porta padrão e semear o banco de outro stack." >&2
  exit 1
fi

# O valor é do stack local ou não serve. A URL é impressa sem a credencial: este
# arquivo não põe senha de banco em log nem em saída de terminal.
case "$DB_URL" in
  *@127.0.0.1:*|*@localhost:*|*@\[::1\]:*) ;;
  *)
    echo "==> RECUSADO: o Postgres do stack local respondeu com um host que não é local: $(printf '%s' "$DB_URL" | sed -E 's#://[^@/]*@#://[REDACTED]@#')" >&2
    exit 1
    ;;
esac

# Mesmo default do `playwright.config.ts` (PORT = process.env.E2E_PORT ??
# "3001"). NEXT_PUBLIC_APP_URL é uma NEXT_PUBLIC_* — o Next a embute no bundle
# (server E client) em BUILD time, não runtime; por isso ela precisa entrar
# aqui, no arquivo que `e2e-build.sh` exporta antes do `next build`, e não
# bastaria setá-la só no `next start`. Sem isto, app/auth/confirm/route.ts
# monta o redirect contra o default do schema (http://localhost:3000) — porta
# onde não há NADA escutando durante o teste — e o browser, que SEMPRE segue
# 3xx (diferente de `curl` sem `-L`), estoura ERR_CONNECTION_REFUSED em vez de
# chegar em /login/reset ou /onboarding/welcome. Medido: as 3 specs do fluxo de
# auth por e-mail (password-recovery, reset-password-mfa, signup-journey)
# reprovam assim mesmo com token_hash válido — o defeito independe de PKCE.
E2E_PORT="${E2E_PORT:-3001}"

cat > .env.e2e <<EOF
# ── Ambiente do E2E — LOCAL, nunca a nuvem ──────────────────────────────────
# GERADO por 'pnpm e2e:env'. Não versionado (.gitignore cobre '.env*').
# Antes de rodar a suíte: pnpm e2e:build && pnpm test:e2e
NEXT_PUBLIC_SUPABASE_URL=$API_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=$ANON
SUPABASE_SERVICE_ROLE_KEY=$SERVICE
# Vem do stack que está de pé (ver o comentário do \`ler DB_URL\` acima), não de
# um literal: é isto que mantém duas sessões locais escrevendo cada uma no seu
# banco.
SUPABASE_DB_URL=$DB_URL

# Precisa bater com o baseURL real do Playwright (ver comentário acima).
NEXT_PUBLIC_APP_URL=http://localhost:$E2E_PORT

# Catálogo servido pelas provas de extensões. A exceção HTTP só é aceita
# quando o aplicativo também é local; o ambiente do produto deixa isto vazio.
EXTENSIONS_LOCAL_CATALOG_ORIGIN=http://127.0.0.1:56331

# O Jev (\`lib/ai/decisao\`) fala com o dublê \`scripts/duble-jev-e2e.mjs\`, que a
# spec \`jev-decisoes-rapidas\` sobe nesta porta — a 3996, vizinha do dublê dos
# SaaS (3997), do Redis HTTP (3998) e do WAHA (3999). Fora daquela spec nada
# escuta aqui, e não precisa: o Jev nasce desligado em toda organização.
JEV_API_BASE_URL=http://127.0.0.1:3996

# O provedor de cobranca (Stripe) fala com o duble tests/e2e/fixtures/provedor-de-cobranca.ts,
# que a spec cobranca-revendedor sobe nesta porta, vizinha da do Jev. O servidor
# sob teste precisa NASCER com ela: a URL e lida a cada chamada, mas o ambiente
# e do next start.
COBRANCA_API_BASE_URL_TESTE=http://127.0.0.1:3995

# Placeholders: 'next start' roda em NODE_ENV=production, e lib/env.ts exige
# estas vars em produção. As specs não exercitam os serviços por trás delas.
# Local e CI falham pelos mesmos motivos porque leem ESTE arquivo: o workflow
# publica o \`.env.e2e\` no ambiente do job em vez de redigitar os valores. A
# versão anterior desta linha prometia "valores iguais aos do CI" e eles não
# eram iguais (\`e2e-placeholder…\` aqui, \`ci-placeholder…\` lá) — a promessa por
# coincidência durou até a primeira divergência, que custou 8 specs em 401.
INTERNAL_SECRET=e2e-placeholder-nao-e-segredo
IMPERSONATE_COOKIE_SECRET=e2e-support-cookie-local-placeholder-32-chars
# As três abaixo são chaves de CIFRA de verdade: o app exige 32 bytes e recusa
# um rótulo. Medido — com o placeholder, criar credencial de IA devolvia 500
# ("AI_CRED_AES_KEY deve ter exatamente 32 bytes (lido: 21)") e todo run do
# agente morria em \`credential_decrypt_failed\`, o que aparecia como "o modelo
# não respondeu". Geradas na hora: são de teste, não precisam sobreviver.
CPF_ENCRYPTION_KEY=$CHAVE_CPF
WAHA_BYO_ENCRYPTION_KEY=$CHAVE_WAHA
AI_CRED_AES_KEY=$CHAVE_AI
WAHA_API_BASE_URL=http://127.0.0.1:3999
WAHA_API_KEY=e2e-placeholder-nao-e-segredo
WAHA_WEBHOOK_BASE_URL=http://127.0.0.1:3001
UPSTASH_REDIS_REST_URL=http://127.0.0.1:3998
UPSTASH_REDIS_REST_TOKEN=e2e-placeholder-nao-e-segredo

# ── O DONO DA INSTALAÇÃO — o primeiro usuário, como o \`install.sh\` cria ────
# A \`vps-fresh-onboarding\` (parte 4 do CI) roda numa instalação onde NINGUÉM
# existe ainda: o \`install.sh\` de uma VPS recém-instalada cria o primeiro dono
# com \`scripts/bootstrap-owner.ts\`, e a spec exige isso como PRECONDIÇÃO
# (cabeçalho dela) — sem esses valores o \`beforeAll\` para em "nao achou o dono
# (dono@qa.local)", porque a spec é destrutiva e se recusa a escolher a
# organização no escuro.
#
# Ficam AQUI, e não redigitados no workflow, porque este arquivo é a fonte
# única do ambiente da suíte: o passo "Publicar o .env.e2e no ambiente do job"
# o leva inteiro para o job, então o CI e quem roda local leem o MESMO dono.
# Mesmos valores de \`docs/testing/HANDOFF-vps-qa.md\` (a receita local da
# jornada) e do cabeçalho da spec.
#
# Backtick escapado neste heredoc não é estilo: ele é \`<<EOF\` sem aspas, então
# crase crua vira SUBSTITUIÇÃO DE COMANDO — o comentário chega no arquivo
# mutilado e o shell imprime "command not found" no log do CI.
#
# ⚠️ E nenhum valor daqui pode ter ESPAÇO — são dois consumidores que não
# combinam entre si:
#   1. o \`e2e-build.sh\` carrega o arquivo com \`set -a; . ./.env.e2e\`. Valor com
#      espaço faz o shell ler o resto como COMANDO: \`OWNER_ORG_NAME=Loja QA VPS\`
#      imprime \`QA: command not found\` e o build morre — medido em 2026-09-16,
#      com as QUATRO partes do e2e vermelhas por causa desta linha;
#   2. o passo "Publicar o .env.e2e no ambiente do job" copia as linhas LITERAIS
#      para o \`\$GITHUB_ENV\`, que NÃO é shell. Então aspas não resolvem: elas
#      entrariam no valor e a organização nasceria chamada \"Loja QA VPS\", com
#      aspas no nome.
# Nome de organização aqui é um token só. A guarda que cobra isso está em
# \`tests/unit/e2e-cria-o-dono-que-a-spec-exige.test.ts\` (carrega o arquivo).
OWNER_EMAIL=dono@qa.local
OWNER_PASSWORD=QaVps!2026#Dono
OWNER_ORG_NAME=Loja-QA-VPS

NEXT_TELEMETRY_DISABLED=1
# Telemetria DESLIGADA na suíte, e não é preferência: sem isto o SDK do browser
# assume o DSN da comunidade (\`lib/sentry/dsn.ts\` → DEFAULT_SENTRY_DSN) e a suíte
# MANDA DADO para o Sentry de produção do projeto — mesma família do e2e que
# escrevia no banco de produção. E o inverso morde igual: em 2026-08-10 a
# organização do Sentry estava suspensa por cota, o ingest respondeu 429 a tudo, o
# SDK cuspiu erro de console em toda tela e \`olhar-telas-do-epico\` reprovou. A cor
# do CI não pode depender do estado de cobrança de um terceiro.
#
# Consequência aceita: com \`off\` o cliente não inicializa, então a suíte NÃO
# exercita a política do DSN da comunidade — quem a guarda é
# \`tests/unit/sentry-comunidade-so-erro.test.ts\`.
SENTRY_DSN=off
EOF

echo "==> .env.e2e gerado, apontando para $API_URL (Postgres em $(printf '%s' "$DB_URL" | sed -E 's#^.*@##'))"

# A chave de cifra vai para o BANCO, que é onde `fn_encrypt_oauth` a procura —
# o mesmo lugar em que o `install.sh` a grava numa VPS. Sem ela, toda tela que
# guarda segredo cifrado (fonte de captação, credencial do WAHA) responde 422.
#
# Não derruba o script quando o banco não responde: gerar o `.env.e2e` é útil
# antes de o stack subir, e quem precisa da chave descobre pela recusa explicada
# abaixo — não por um script que morre sem dizer o que fazia.
CHAVE_WEBHOOK="$(psql_no_banco "$DB_URL" \
  "select value from private.app_secrets where name = 'nuvemshop_oauth_key'" || true)"
if [ "${#CHAVE_WEBHOOK}" -ge 32 ]; then
  echo "==> chave de cifra já estava em private.app_secrets (nuvemshop_oauth_key) — mantida"
elif psql_no_banco "$DB_URL" \
  "insert into private.app_secrets (name, value) values ('nuvemshop_oauth_key', '$(openssl rand -hex 32)') on conflict (name) do update set value = excluded.value, updated_at = now()" \
  >/dev/null; then
  echo "==> chave de cifra gravada em private.app_secrets (nuvemshop_oauth_key)"
else
  echo "==> AVISO: não gravei a chave de cifra em private.app_secrets (banco fora do ar, ou sem psql e sem docker)." >&2
  echo "    Sem ela, guardar segredo pela tela responde 422 encryption_unavailable." >&2
fi

echo "==> Próximo: pnpm e2e:build && pnpm test:e2e"
