import { lerAviso, lerPedidoDaApi, PRODUTO_KTALOGU } from "@/lib/assinatura-cakto";
import { lerAbandono } from "@/lib/automacoes-da-assinatura";
import { enviarLembretesDeCarrinho, enviarLembretesDePix, enviarPrimeirosPassos, guardarAbandono } from "@/lib/automacoes-da-assinatura-servidor";
import {
  cancelarPendentesNaCakto,
  emailDaContaDoPedido,
  guardarPagamento,
  naFila,
  processarEntrega,
  registrarEntrega,
  resolverAnteriores,
} from "@/lib/assinatura-cakto-servidor";
import { enviarAvisosDoDia } from "@/lib/avisos-da-assinatura-servidor";
import { caktoConfigurada, lerPagina } from "@/lib/cakto-api";
import { createAdminClient } from "@/lib/supabase-server";

/**
 * A rede de segurança da assinatura da Cakto, rodando dentro do servidor.
 *
 * A cada 5 minutos:
 * - aviso gravado que falhou (ou ficou pela metade) é aplicado de novo;
 * - troca de plano que não terminou procura e cancela as assinaturas antigas;
 * - cancelamento na Cakto que falhou é tentado de novo.
 *
 * A cada 6 horas (precisa de CAKTO_CLIENT_ID e CAKTO_CLIENT_SECRET):
 * - lê o histórico de entregas da Cakto e aplica o que nunca chegou aqui;
 * - traz os pedidos recentes para o histórico de pagamentos;
 * - manda os avisos do dia (vencimento chegando, acesso encerrado).
 */

const PENDENTES_MS = 5 * 60 * 1000;
const CONCILIAR_MS = 6 * 60 * 60 * 1000;
const MAX_TENTATIVAS = 30;
const PAGINAS_DO_HISTORICO = 4;

type Estado = { iniciado: boolean; rodando: boolean };

declare global {
  var __relogioCakto: Estado | undefined;
}

function estado() {
  globalThis.__relogioCakto ??= { iniciado: false, rodando: false };
  return globalThis.__relogioCakto;
}

export async function reprocessarPendentes() {
  const admin = createAdminClient();
  /* Três minutos: menos que isso, a entrega ainda pode estar sendo aplicada pela rota. */
  const tresMinutosAtras = new Date(Date.now() - 3 * 60_000).toISOString();
  const { data, error } = await admin
    .from("subscription_webhook_events")
    .select("id,payload")
    .eq("provider", "cakto")
    .in("status", ["received", "failed"])
    .lt("tentativas", MAX_TENTATIVAS)
    .lt("updated_at", tresMinutosAtras)
    .order("created_at", { ascending: true })
    .limit(25);
  if (error) throw new Error(error.message);
  let feitos = 0;
  for (const linha of (data ?? []) as Array<{ id: string; payload: unknown }>) {
    const { erro } = await processarEntrega(admin, { id: linha.id, jaFeita: false }, lerAviso(linha.payload), linha.payload);
    if (!erro) feitos += 1;
  }
  return { pendentes: data?.length ?? 0, feitos };
}

/** Trocas de plano que ainda não cancelaram as assinaturas anteriores. */
export async function terminarTrocas() {
  const admin = createAdminClient();
  const doisMinutosAtras = new Date(Date.now() - 2 * 60_000).toISOString();
  const { data, error } = await admin
    .from("assinaturas_cakto")
    .select("cakto_subscription_id")
    .eq("anteriores_resolvidas", false)
    .lt("atualizada_em", doisMinutosAtras)
    .limit(10);
  if (error) throw new Error(error.message);
  for (const linha of (data ?? []) as Array<{ cakto_subscription_id: string }>) {
    await naFila(() => resolverAnteriores(admin, linha.cakto_subscription_id)).catch((e) =>
      console.error("[cakto] troca de plano ainda pendente", linha.cakto_subscription_id, e instanceof Error ? e.message : e)
    );
  }
  return naFila(() => cancelarPendentesNaCakto(admin));
}

/** Cada item do histórico pode trazer `data` como lista (webhook V2): vira um aviso por pedido. */
function avisosDoItem(payload: unknown): unknown[] {
  if (!payload || typeof payload !== "object") return [];
  const { event, data } = payload as { event?: unknown; data?: unknown };
  if (Array.isArray(data)) return data.map((item) => ({ event, data: item }));
  return [{ event, data }];
}

export async function conciliarComCakto() {
  if (!caktoConfigurada()) return { lidos: 0, aplicados: 0, desligada: true };
  const admin = createAdminClient();
  let lidos = 0;
  let aplicados = 0;
  for (let pagina = 1; pagina <= PAGINAS_DO_HISTORICO; pagina += 1) {
    const json = await lerPagina<{ payload?: unknown }>(`/public_api/webhook/event_history/?limit=50&page=${pagina}`);
    for (const item of json.results ?? []) {
      for (const corpo of avisosDoItem(item.payload)) {
        const aviso = lerAviso(corpo);
        if (!aviso || aviso.produtoId !== PRODUTO_KTALOGU) {
          /* Abandono de checkout que não chegou por aqui também vira carrinho. */
          const abandono = lerAbandono(corpo);
          if (abandono) await guardarAbandono(admin, abandono).catch(() => undefined);
          continue;
        }
        lidos += 1;
        const entrega = await registrarEntrega(admin, aviso.chave, corpo, aviso);
        if (entrega.jaFeita) continue;
        const { erro } = await processarEntrega(admin, entrega, aviso);
        if (!erro) aplicados += 1;
      }
    }
    if (!json.next) break;
  }
  return { lidos, aplicados, desligada: false };
}

/**
 * Traz os pedidos da Ktalogu (planos novos e antigos) para o histórico. A
 * primeira vez lê tudo; depois, só as páginas mais recentes.
 */
export async function sincronizarPagamentos(tudo = false) {
  if (!caktoConfigurada()) return { gravados: 0 };
  const admin = createAdminClient();
  const { count } = await admin.from("pagamentos_cakto").select("pedido_id", { count: "exact", head: true });
  const paginas = tudo || !count ? 50 : 2;
  let gravados = 0;
  for (let pagina = 1; pagina <= paginas; pagina += 1) {
    const json = await lerPagina(`/public_api/orders/?limit=100&page=${pagina}`);
    for (const bruto of json.results ?? []) {
      const lido = lerPedidoDaApi(bruto);
      if (!lido) continue;
      const email = await emailDaContaDoPedido(admin, lido.pagamento.assinatura_id, lido.email);
      await guardarPagamento(admin, email, lido.pagamento);
      gravados += 1;
    }
    if (!json.next) break;
  }
  return { gravados };
}

async function volta(completa: boolean) {
  const e = estado();
  if (e.rodando) return;
  e.rodando = true;
  const passo = async (nome: string, fazer: () => Promise<unknown>) => {
    try {
      await fazer();
    } catch (erro) {
      console.error(`[cakto] ${nome} falhou`, erro instanceof Error ? erro.message : erro);
    }
  };
  try {
    await passo("reprocessar avisos", async () => {
      const r = await reprocessarPendentes();
      if (r.pendentes) console.info(`[cakto] ${r.feitos} de ${r.pendentes} avisos pendentes aplicados`);
    });
    await passo("terminar trocas de plano", terminarTrocas);
    /* Lembretes que dependem da hora: carrinho (1 h e 24 h) e Pix pendente. */
    await passo("lembretes de carrinho", () => enviarLembretesDeCarrinho(createAdminClient()));
    await passo("lembretes de Pix", () => enviarLembretesDePix(createAdminClient()));
    if (completa) {
      await passo("conciliar com a Cakto", async () => {
        const r = await conciliarComCakto();
        if (r.aplicados) console.info(`[cakto] conciliação aplicou ${r.aplicados} avisos que não tinham chegado`);
      });
      await passo("sincronizar pagamentos", () => sincronizarPagamentos());
      await passo("avisos do dia", () => enviarAvisosDoDia(createAdminClient()));
      await passo("primeiros passos", () => enviarPrimeirosPassos(createAdminClient()));
    }
  } finally {
    e.rodando = false;
  }
}

export function iniciarRelogioCakto() {
  if (process.env.NODE_ENV === "test") return;
  const e = estado();
  if (e.iniciado) return;
  if (!process.env.SUPABASE_SERVICE_ROLE_KEY || !process.env.CAKTO_WEBHOOK_SECRET) {
    console.info("[cakto] relógio da assinatura desligado: falta SUPABASE_SERVICE_ROLE_KEY ou CAKTO_WEBHOOK_SECRET");
    return;
  }
  e.iniciado = true;
  setInterval(() => void volta(false), PENDENTES_MS);
  setInterval(() => void volta(true), CONCILIAR_MS);
  setTimeout(() => void volta(true), 60_000);
}
