/**
 * A credencial de conversões da organização — lida e decifrada.
 *
 * Mora na fronteira, não no transporte, porque a TABELA é agnóstica: a mesma
 * linha serve qualquer plataforma, mudando só o slug. Fosse dentro de `meta/`, a
 * segunda plataforma copiaria a leitura — e cópia de leitura de credencial é
 * onde nasce o bug de ler a linha da organização errada.
 *
 * ⚠️ SEMPRE COM `organization_id` NO FILTRO. É a lição da #236, que custou caro
 * em `channel_sessions`: identificador de provider não é único por instalação, e
 * uma busca sem a organização casava duas linhas, o `maybeSingle()` devolvia
 * `null` com erro `PGRST116` descartado, e as duas organizações passavam a
 * operar pela conta de outra. Aqui o índice único é `(organization_id, platform)`
 * e o filtro repete a organização — banco e código dizendo a mesma coisa.
 *
 * ⚠️ EXIGE O ADMIN CLIENT. `ad_platform_connections` tem RLS ligada e ZERO
 * policies, com grants revogados de anon/authenticated (migration 0213). Pelo
 * client de sessão isto não devolve nada — nem erro, só vazio.
 */
import type { SupabaseClient } from "@supabase/supabase-js";

import { logger } from "@/lib/logger";
import { decryptWebhookSecret } from "@/lib/webhooks/secrets";
import { lerIdentidadeDaMeta } from "./meta/identidade";
import type { CredencialDeConversao, PlataformaDeAnuncio } from "./types";

/**
 * Por que cada ausência tem um nome próprio: elas pedem coisas diferentes de
 * quem lê a tela. "Nunca conectou" é um convite a conectar; "desligou" é
 * lembrete de que a pausa foi decisão de alguém; "cifra indisponível" é problema
 * de INSTALAÇÃO (a chave mestra não está no ambiente) e nenhum clique na tela da
 * organização resolve. Colapsar os três em "não configurado" mandaria o operador
 * refazer um cadastro que já está certo.
 */
export type MotivoSemCredencial =
  | "leitura_indisponivel"
  | "sem_conexao"
  | "conexao_desabilitada"
  | "credencial_incompleta"
  | "cifra_indisponivel";

export type LeituraDeCredencial =
  { ok: true; credencial: CredencialDeConversao } | { ok: false; motivo: MotivoSemCredencial };

export async function lerCredencial(
  admin: SupabaseClient,
  organizationId: string,
  plataforma: PlataformaDeAnuncio,
  /**
   * A compra usa `google_conversion_action_id`; um envio de ETAPA leva a ação
   * da própria regra (0436) e não precisa dela. Sem esta opção, a organização
   * que só configurou etapas teria todo envio recusado como incompleto.
   */
  opcoes: { exigirAcaoDeVenda?: boolean } = {},
): Promise<LeituraDeCredencial> {
  const exigirAcaoDeVenda = opcoes.exigirAcaoDeVenda ?? true;
  const { data, error } = await admin
    .from("ad_platform_connections")
    .select(
      "dataset_id, access_token_encrypted, test_event_code, enabled, google_refresh_token_encrypted, google_customer_id, google_login_customer_id, google_conversion_action_id, google_api, google_purchase_value_mode, google_send_hashed_phone",
    )
    .eq("organization_id", organizationId)
    .eq("platform", plataforma)
    .maybeSingle();

  // O erro NÃO é descartado — foi exatamente o descarte que a #236 mediu.
  if (error) {
    logger.error("[conversoes.credencial] leitura falhou", {
      organizationId,
      plataforma,
      error: error.message,
    });
    return { ok: false, motivo: "leitura_indisponivel" };
  }
  if (!data) return { ok: false, motivo: "sem_conexao" };

  const linha = data as {
    dataset_id: string | null;
    access_token_encrypted: string | null;
    test_event_code: string | null;
    enabled: boolean;
    google_refresh_token_encrypted: string | null;
    google_customer_id: string | null;
    google_login_customer_id: string | null;
    google_conversion_action_id: string | null;
    google_api: "google_ads" | "data_manager";
    google_purchase_value_mode?: "obrigatorio" | "quando_houver" | "nunca" | null;
    google_send_hashed_phone?: boolean | null;
  };

  if (!linha.enabled) return { ok: false, motivo: "conexao_desabilitada" };

  // O que conta como "completo" varia por plataforma: a Meta guarda token
  // direto em `dataset_id`/`access_token_encrypted`; o Google guarda refresh
  // token + os três identificadores nas colunas `google_*` (migration 0307).
  // Um `if` por linha, não um schema comum, porque forçar as duas formas no
  // mesmo par de colunas é o que produziria a "correção" errada no dia em que
  // uma terceira plataforma chegasse com uma forma diferente das duas.
  if (plataforma === "google_ads") {
    if (
      !linha.google_refresh_token_encrypted ||
      !linha.google_customer_id ||
      (exigirAcaoDeVenda && !linha.google_conversion_action_id)
    ) {
      return { ok: false, motivo: "credencial_incompleta" };
    }
    const refreshToken = await decryptWebhookSecret(admin, linha.google_refresh_token_encrypted);
    if (!refreshToken) return { ok: false, motivo: "cifra_indisponivel" };

    return {
      ok: true,
      credencial: {
        datasetId: linha.google_customer_id,
        // Vazio de propósito: o access token do Google expira em ~1h e é
        // derivado a cada envio pelo PRÓPRIO transporte, a partir do refresh
        // token em `google` — ver o cabeçalho de `CredencialDeConversao`.
        accessToken: "",
        testEventCode: linha.test_event_code,
        google: {
          api: linha.google_api ?? "google_ads",
          refreshToken,
          customerId: linha.google_customer_id,
          loginCustomerId: linha.google_login_customer_id,
          conversionActionId: linha.google_conversion_action_id ?? "",
          modoDeValorDaVenda: linha.google_purchase_value_mode ?? "obrigatorio",
          enviarTelefone: linha.google_send_hashed_phone === true,
        },
      },
    };
  }

  if (!linha.dataset_id || !linha.access_token_encrypted) {
    return { ok: false, motivo: "credencial_incompleta" };
  }

  const token = await decryptWebhookSecret(admin, linha.access_token_encrypted);
  // Decifrar falha quando a GUC da chave mestra não está no ambiente. Devolver
  // "sem conexão" aqui faria a tela pedir para reconectar — e o novo cadastro
  // falharia igual, porque o problema é da instalação e não do cadastro.
  if (!token) return { ok: false, motivo: "cifra_indisponivel" };

  return {
    ok: true,
    credencial: {
      datasetId: linha.dataset_id,
      accessToken: token,
      testEventCode: linha.test_event_code,
      // Página / WABA da organização (#2098) — só aqui, no ramo Meta, porque é
      // a única plataforma que cobra essa identidade em `user_data`. Uma
      // leitura a mais por envio, e só depois que a conexão existe: quem nunca
      // conectou não paga a chamada.
      meta: await lerIdentidadeDaMeta(admin, organizationId),
    },
  };
}
