/**
 * Ler o estado de elegibilidade de uma conversa via `supabase-js` (service role).
 *
 * Espelho de `consulta-pg.ts` para os caminhos que NÃO têm um pool `pg` à mão —
 * o worker legado (`workers/ai-response-worker.ts`), o envio inline de texto fixo
 * do follow-up (`lib/followup/enviar-texto-fixo.ts`), o worker de sentimento e o
 * orquestrador de handoff do lado do CRM. TODOS têm de respeitar o MESMO gate
 * que o drain e o turno do agent-engine: nenhum caminho pode mandar mensagem de
 * IA para uma conversa que uma origem elegível não autorizou.
 *
 * A regra pura (`decidirElegibilidade`) e a normalização
 * (`montarEstadoDeElegibilidade`) são as mesmas dos dois lados — só o transporte
 * muda.
 *
 * ─── Fail-closed ───────────────────────────────────────────────────────────
 *
 * `decidirElegibilidadeDaConversaViaSupabase` DEVOLVE a decisão, ou `null` só
 * quando a conversa não existe. Erro de query VIRA EXCEÇÃO — e cada chamador
 * destes caminhos secundários trata exceção como "não responder" (fail-closed):
 * são caminhos de retaguarda, o caminho robusto é o do agent-engine, e um erro
 * ao ler `contacts.ai_authorized_at` quase sempre é schema pela metade (imagem
 * nova, baseline ainda não aplicado) — exatamente quando NÃO se quer a IA solta.
 */
import type { SupabaseClient } from "@supabase/supabase-js";

import {
  decidirElegibilidade,
  montarEstadoDeElegibilidade,
  type DecisaoDaConversa,
} from "./gate";

interface ConversaEmbed {
  bot_silenced_until: string | null;
  assignee_kind: string | null;
  /** `conversations.metadata` — o modo "IA com o dono" (#3967) mora aqui. */
  metadata: unknown;
  contacts: {
    force_human: boolean | null;
    /** A marca permanente "sempre atendimento humano" (issue 2379). */
    ai_opt_out: boolean | null;
    ai_authorized_at: string | null;
    phone_number: string | null;
    /** `contacts.tags` — a régua de contato interno (#2353) e a da equipe (#3534). */
    tags: string[] | null;
  } | null;
  channel_sessions: { metadata: Record<string, unknown> | null } | null;
  organizations: { status: string | null; settings?: unknown } | null;
}

/**
 * Roda a query e a regra. `null` = conversa não encontrada. Lança em erro de
 * banco — o chamador (caminho secundário) trata como "não responder".
 */
export async function decidirElegibilidadeDaConversaViaSupabase(
  admin: Pick<SupabaseClient, "from">,
  input: { organizationId: string; conversationId: string; agora: Date; ttlMs: number },
): Promise<DecisaoDaConversa | null> {
  const { data, error } = await admin
    .from("conversations")
    .select(
      "bot_silenced_until, assignee_kind, metadata, organizations:organization_id(status, settings), contacts:contact_id(force_human, ai_opt_out, ai_authorized_at, phone_number, tags), channel_sessions:channel_session_id(metadata)",
    )
    .eq("organization_id", input.organizationId)
    .eq("id", input.conversationId)
    .maybeSingle();

  if (error) {
    throw new Error(`elegibilidade: leitura falhou — ${error.message}`);
  }
  if (data == null) return null;

  const row = data as unknown as ConversaEmbed;
  const estado = montarEstadoDeElegibilidade({
    orgStatus: row.organizations?.status ?? null,
    canalDesativado: row.channel_sessions?.metadata?.["disabled"] ?? null,
    aiGate: row.channel_sessions?.metadata?.["ai_gate"] ?? null,
    aiGateMode: row.channel_sessions?.metadata?.["ai_gate_mode"] ?? null,
    aiTestPhoneNumbers: row.channel_sessions?.metadata?.["ai_test_phone_numbers"] ?? null,
    contactPhoneNumber: row.contacts?.phone_number ?? null,
    forceHuman: row.contacts?.force_human ?? false,
    contactTags: row.contacts?.tags ?? null,
    orgSettings: row.organizations?.settings,
    aiOptOut: row.contacts?.ai_opt_out ?? false,
    assigneeKind: row.assignee_kind,
    metadataDaConversa: row.metadata,
    botSilencedUntil: row.bot_silenced_until,
    aiAuthorizedAt: row.contacts?.ai_authorized_at ?? null,
    agora: input.agora,
    ttlMs: input.ttlMs,
  });
  // #3534: `contatoDaEquipe` viaja com a decisão. `contacts.tags` e
  // `organizations.settings` já vêm no embed desta query, então a régua da
  // equipe não custa leitura nova — e o medidor de clima, chamador daqui, passa
  // a saber que a conversa é atendida E que não pode ser passada por clima.
  return { ...decidirElegibilidade(estado), contatoDaEquipe: estado.contatoDaEquipe };
}
