import type { SupabaseClient } from "@supabase/supabase-js";

import { mercadoPagoEmTeste, tokenDoMercadoPagoDaKtalogu } from "@/lib/fiscal/ambiente";
import {
  criarPreferenciaDoPacote,
  pagamentoParaOBanco,
  pagamentoQueManda,
  pagamentosDaCompra,
  type PagamentoDoMercadoPago,
} from "@/lib/fiscal/mercado-pago-ktalogu";
import { ErroDigital } from "@/lib/digitais/servidor/contexto";

import { saldoDeLives } from "./lives";

/*
 * KtaloguLive: os pacotes à venda e a compra pelo Mercado Pago da Ktalogu,
 * no mesmo caminho dos pacotes de notas (preferência com a compra como
 * referência; o aviso, a volta do cliente e a rotina conferem e chamam a
 * função do banco, que é idempotente).
 */

type Admin = SupabaseClient;

export type PacoteDeLive = {
  id: string;
  nome: string;
  descricao: string | null;
  lives: number;
  pessoas: number;
  preco: number;
  destaque: boolean;
  ordem: number;
};

export type CompraDeLive = {
  id: string;
  empresa_id: string;
  pacote_nome: string;
  lives: number;
  pessoas: number;
  preco: number;
  status: "pendente" | "aprovada" | "recusada" | "cancelada" | "reembolsada" | "contestada";
  mp_status: string | null;
  mp_status_detalhe: string | null;
  forma_pagamento: string | null;
  checkout_url: string | null;
  aprovada_em: string | null;
  valida_ate: string | null;
  created_at: string;
};

const CAMPOS_DA_COMPRA = "id,empresa_id,pacote_nome,lives,pessoas,preco,status,mp_status,mp_status_detalhe,forma_pagamento,checkout_url,aprovada_em,valida_ate,created_at";

export async function regrasDaLive(admin: Admin) {
  const { data } = await admin.from("live_regras").select("gratis_lives_por_mes,gratis_pessoas,validade_dias,vendas_abertas").eq("id", 1).maybeSingle();
  const r = (data ?? {}) as { gratis_lives_por_mes?: number; gratis_pessoas?: number; validade_dias?: number; vendas_abertas?: boolean };
  return {
    gratisPorMes: Number(r.gratis_lives_por_mes ?? 2),
    gratisPessoas: Number(r.gratis_pessoas ?? 30),
    validadeDias: Number(r.validade_dias ?? 90),
    vendasAbertas: r.vendas_abertas !== false,
  };
}

export async function pacotesDeLive(admin: Admin): Promise<PacoteDeLive[]> {
  const { data } = await admin
    .from("live_pacotes")
    .select("id,nome,descricao,lives,pessoas,preco,destaque,ordem")
    .eq("ativo", true)
    .order("ordem", { ascending: true })
    .order("preco", { ascending: true });
  return ((data ?? []) as PacoteDeLive[]).map((p) => ({ ...p, preco: Number(p.preco), lives: Number(p.lives), pessoas: Number(p.pessoas) }));
}

/* Quem contestou um pagamento não compra de novo até o suporte olhar (igual aos pacotes de notas). */
async function bloqueio(admin: Admin, empresaId: string): Promise<string | null> {
  const { data } = await admin.from("live_compras").select("id").eq("empresa_id", empresaId).eq("status", "contestada").limit(1);
  return (data ?? []).length ? "A compra de pacotes está bloqueada por causa de uma contestação de pagamento. Fale com o suporte do Ktalogu." : null;
}

/* A aba "Produtos Digitais" da Assinatura: o que tem, o que dá para comprar e o que já comprou. */
export async function painelDaLive(admin: Admin, empresaId: string) {
  const [saldo, pacotes, regras, bloqueado, compras] = await Promise.all([
    saldoDeLives(admin, empresaId),
    pacotesDeLive(admin),
    regrasDaLive(admin),
    bloqueio(admin, empresaId),
    admin
      .from("live_compras")
      .select(CAMPOS_DA_COMPRA)
      .eq("empresa_id", empresaId)
      .or("mp_status_detalhe.is.null,mp_status_detalhe.neq.preferencia_falhou")
      .order("created_at", { ascending: false })
      .limit(24),
  ]);
  return {
    saldo,
    pacotes,
    regras,
    vendasAbertas: regras.vendasAbertas && Boolean(tokenDoMercadoPagoDaKtalogu()),
    bloqueioDeCompra: bloqueado,
    compras: (compras.data ?? []) as CompraDeLive[],
  };
}

export async function criarCompraDeLive(
  admin: Admin,
  input: { empresaId: string; pacoteId: string; usuarioId: string; email: string | null; nome: string | null; origem: string },
) {
  const token = tokenDoMercadoPagoDaKtalogu();
  if (!token) throw new ErroDigital("A venda de pacotes está fechada no momento.", 503);
  if (process.env.NODE_ENV === "production" && mercadoPagoEmTeste(token)) throw new ErroDigital("A venda de pacotes está em manutenção.", 503);
  const regras = await regrasDaLive(admin);
  if (!regras.vendasAbertas) throw new ErroDigital("A venda de pacotes está pausada no momento.", 503);
  const bloqueado = await bloqueio(admin, input.empresaId);
  if (bloqueado) throw new ErroDigital(bloqueado, 403);

  const { data: pacote } = await admin.from("live_pacotes").select("id,nome,lives,pessoas,preco,ativo").eq("id", input.pacoteId).maybeSingle();
  const p = pacote as { id: string; nome: string; lives: number; pessoas: number; preco: number; ativo: boolean } | null;
  if (!p || !p.ativo) throw new ErroDigital("Este pacote não está mais à venda.", 404);

  /* Clique repetido: reaproveita o checkout aberto há pouco do mesmo pacote. */
  const meiaHora = new Date(Date.now() - 30 * 60 * 1000).toISOString();
  const { data: aberta } = await admin
    .from("live_compras")
    .select("id,checkout_url,preco")
    .eq("empresa_id", input.empresaId)
    .eq("pacote_id", p.id)
    .eq("status", "pendente")
    .gte("created_at", meiaHora)
    .not("checkout_url", "is", null)
    .order("created_at", { ascending: false })
    .limit(1)
    .maybeSingle();
  const reaproveitar = aberta as { id: string; checkout_url: string | null; preco: number } | null;
  if (reaproveitar?.checkout_url && Number(reaproveitar.preco) === Number(p.preco)) return { compraId: reaproveitar.id, checkoutUrl: reaproveitar.checkout_url };

  const { data: compra, error } = await admin
    .from("live_compras")
    .insert({
      empresa_id: input.empresaId,
      pacote_id: p.id,
      pacote_nome: p.nome,
      lives: p.lives,
      pessoas: p.pessoas,
      preco: p.preco,
      criada_por: input.usuarioId,
    })
    .select("id")
    .single();
  if (error || !compra) throw new ErroDigital("Não foi possível registrar a compra.", 500);
  const compraId = (compra as { id: string }).id;

  const origem = input.origem.replace(/\/+$/, "");
  const urlDeVolta = `${origem}/dashboard/assinatura?aba=digitais&compra=${compraId}`;
  const urlDoAviso = origem.startsWith("https://") ? `${origem}/api/webhooks/mercado-pago-ktalogu?source_news=webhooks` : null;
  try {
    const preferencia = await criarPreferenciaDoPacote({
      token,
      compraId,
      pacote: { nome: p.nome, quantidade: p.lives, preco: Number(p.preco) },
      comprador: { email: input.email, nome: input.nome },
      urlDeVolta,
      urlDoAviso,
      item: {
        id: `ktalogulive-${p.lives}x${p.pessoas}`,
        title: `KtaloguLive — ${p.nome}`,
        description: `${p.lives} ${p.lives === 1 ? "live" : "lives"} com até ${p.pessoas} pessoas cada`,
      },
    });
    const checkoutUrl = (mercadoPagoEmTeste(token) ? preferencia.sandbox_init_point || preferencia.init_point : preferencia.init_point) || null;
    if (!checkoutUrl) throw new Error("O Mercado Pago não devolveu o link de pagamento.");
    await admin.from("live_compras").update({ mp_preferencia_id: preferencia.id, checkout_url: checkoutUrl, updated_at: new Date().toISOString() }).eq("id", compraId);
    return { compraId, checkoutUrl };
  } catch (erro) {
    await admin
      .from("live_compras")
      .update({ status: "cancelada", mp_status_detalhe: "preferencia_falhou", updated_at: new Date().toISOString() })
      .eq("id", compraId);
    throw new ErroDigital(erro instanceof Error ? erro.message : "Não foi possível abrir o pagamento.", 503);
  }
}

async function aplicar(admin: Admin, compraId: string, pagamento: PagamentoDoMercadoPago) {
  const { error } = await admin.rpc("live_aplicar_pagamento_compra", { p_compra: compraId, p_pagamento: pagamentoParaOBanco(pagamento) });
  if (error) throw new Error(error.message);
}

/* A volta do cliente (e a rotina): confere no Mercado Pago e aplica. */
export async function conferirCompraDeLive(admin: Admin, input: { compraId: string; empresaId?: string | null }): Promise<CompraDeLive> {
  let consulta = admin.from("live_compras").select(CAMPOS_DA_COMPRA).eq("id", input.compraId);
  if (input.empresaId) consulta = consulta.eq("empresa_id", input.empresaId);
  const { data } = await consulta.maybeSingle();
  const atual = data as CompraDeLive | null;
  if (!atual) throw new ErroDigital("Compra não encontrada.", 404);
  const token = tokenDoMercadoPagoDaKtalogu();
  if (!token) return atual;
  const manda = pagamentoQueManda(await pagamentosDaCompra(token, atual.id));
  if (!manda) {
    const horas = (Date.now() - new Date(atual.created_at).getTime()) / 3600000;
    if (atual.status === "pendente" && horas > 48) {
      await admin.from("live_compras").update({ status: "cancelada", mp_status_detalhe: "expirada", updated_at: new Date().toISOString() }).eq("id", atual.id).eq("status", "pendente");
    }
  } else {
    await aplicar(admin, atual.id, manda);
  }
  const { data: depois } = await admin.from("live_compras").select(CAMPOS_DA_COMPRA).eq("id", atual.id).single();
  return depois as CompraDeLive;
}

/* O aviso do Mercado Pago que não era de pacote de notas: é do KtaloguLive? */
export async function aplicarAvisoDaLive(admin: Admin, compraId: string, pagamento: PagamentoDoMercadoPago) {
  const { data } = await admin.from("live_compras").select("id").eq("id", compraId).maybeSingle();
  if (!data) return { resultado: "compra_desconhecida" as const };
  const token = tokenDoMercadoPagoDaKtalogu();
  const todos = token ? await pagamentosDaCompra(token, compraId).catch(() => [pagamento]) : [pagamento];
  await aplicar(admin, compraId, pagamentoQueManda(todos.length ? todos : [pagamento]) ?? pagamento);
  return { resultado: "aplicado" as const };
}

/* A rotina: compras pendentes entre 2 minutos e 3 dias. */
export async function conferirComprasPendentesDaLive(admin: Admin) {
  const { data } = await admin
    .from("live_compras")
    .select("id")
    .eq("status", "pendente")
    .lte("created_at", new Date(Date.now() - 2 * 60000).toISOString())
    .gte("created_at", new Date(Date.now() - 3 * 24 * 3600000).toISOString())
    .limit(20);
  for (const linha of (data ?? []) as Array<{ id: string }>) {
    await conferirCompraDeLive(admin, { compraId: linha.id }).catch(() => undefined);
  }
}
