import { ErroDoPainel } from "@/lib/fiscal-owner";
import type { createAdminClient } from "@/lib/supabase-admin";

/*
 * KtaloguLive no Owner: os pacotes que as empresas compram (Assinatura →
 * Produtos Digitais, pelo Mercado Pago da Ktalogu), as regras do grátis do
 * mês e o que já foi vendido e usado.
 */

type Admin = ReturnType<typeof createAdminClient>;

export type RegrasDaLive = {
  gratis_lives_por_mes: number;
  gratis_pessoas: number;
  validade_dias: number;
  vendas_abertas: boolean;
  updated_at: string | null;
  updated_by: string | null;
};

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

export type CompraDeLiveNoOwner = {
  id: string;
  empresa_id: string;
  empresa: string;
  pacote_nome: string;
  lives: number;
  pessoas: number;
  preco: number;
  status: string;
  forma_pagamento: string | null;
  aprovada_em: string | null;
  valida_ate: string | null;
  created_at: string;
};

export type UsoDeLive = {
  id: string;
  empresa: string;
  titulo: string;
  origem: "gratis" | "compra";
  pessoas: number;
  pico: number;
  minutos: number | null;
  created_at: string;
};

export type PainelDaLive = {
  regras: RegrasDaLive;
  pacotes: PacoteDeLive[];
  compras: CompraDeLiveNoOwner[];
  usos: UsoDeLive[];
  numeros: { livesNoMes: number; gratisNoMes: number; receitaNoMes: number; aoVivoAgora: number; empresasQueUsaram: number };
};

function inicioDoMes(): string {
  const agora = new Date(Date.now() - 3 * 3600000);
  return new Date(Date.UTC(agora.getUTCFullYear(), agora.getUTCMonth(), 1, 3)).toISOString();
}

async function nomesDasEmpresas(admin: Admin, ids: string[]): Promise<Map<string, string>> {
  const unicos = Array.from(new Set(ids.filter(Boolean)));
  if (!unicos.length) return new Map();
  const { data } = await admin.from("profiles").select("id,store_name,catalog_name").in("id", unicos);
  return new Map(
    ((data ?? []) as Array<{ id: string; store_name: string | null; catalog_name: string | null }>).map((p) => [p.id, p.catalog_name || p.store_name || "Empresa"]),
  );
}

export async function painelDaLive(admin: Admin): Promise<PainelDaLive> {
  const desde = inicioDoMes();
  const [regrasR, pacotesR, comprasR, usosR, aoVivoR] = await Promise.all([
    admin.from("live_regras").select("gratis_lives_por_mes,gratis_pessoas,validade_dias,vendas_abertas,updated_at,updated_by").eq("id", 1).maybeSingle(),
    admin.from("live_pacotes").select("id,nome,descricao,lives,pessoas,preco,ativo,destaque,ordem").order("ordem", { ascending: true }).order("preco", { ascending: true }),
    admin
      .from("live_compras")
      .select("id,empresa_id,pacote_nome,lives,pessoas,preco,status,forma_pagamento,aprovada_em,valida_ate,created_at,mp_status_detalhe")
      .order("created_at", { ascending: false })
      .limit(150),
    admin.from("live_usos").select("id,empresa_id,sala_id,origem,pessoas,created_at").order("created_at", { ascending: false }).limit(150),
    admin.from("live_salas").select("id", { count: "exact", head: true }).eq("status", "ao_vivo"),
  ]);
  if (regrasR.error || pacotesR.error) throw new ErroDoPainel("Não foi possível ler o KtaloguLive.", 500);
  const compras = ((comprasR.data ?? []) as Array<Omit<CompraDeLiveNoOwner, "empresa"> & { mp_status_detalhe: string | null }>).filter(
    (c) => c.mp_status_detalhe !== "preferencia_falhou",
  );
  const usosCrus = (usosR.data ?? []) as Array<{ id: string; empresa_id: string; sala_id: string; origem: "gratis" | "compra"; pessoas: number; created_at: string }>;
  const salasIds = usosCrus.map((u) => u.sala_id);
  const { data: salas } = salasIds.length
    ? await admin.from("live_salas").select("id,titulo,pico_pessoas,iniciada_em,encerrada_em").in("id", salasIds)
    : { data: [] };
  const porSala = new Map(
    ((salas ?? []) as Array<{ id: string; titulo: string; pico_pessoas: number; iniciada_em: string | null; encerrada_em: string | null }>).map((s) => [s.id, s]),
  );
  const nomes = await nomesDasEmpresas(admin, [...compras.map((c) => c.empresa_id), ...usosCrus.map((u) => u.empresa_id)]);

  const noMes = usosCrus.filter((u) => u.created_at >= desde);
  const r = (regrasR.data ?? {}) as Partial<RegrasDaLive>;
  return {
    regras: {
      gratis_lives_por_mes: Number(r.gratis_lives_por_mes ?? 2),
      gratis_pessoas: Number(r.gratis_pessoas ?? 30),
      validade_dias: Number(r.validade_dias ?? 90),
      vendas_abertas: r.vendas_abertas !== false,
      updated_at: r.updated_at ?? null,
      updated_by: r.updated_by ?? null,
    },
    pacotes: ((pacotesR.data ?? []) as PacoteDeLive[]).map((p) => ({ ...p, preco: Number(p.preco), lives: Number(p.lives), pessoas: Number(p.pessoas) })),
    compras: compras.map((c) => ({ ...c, preco: Number(c.preco), empresa: nomes.get(c.empresa_id) ?? "Empresa" })),
    usos: usosCrus.map((u) => {
      const sala = porSala.get(u.sala_id);
      const minutos =
        sala?.iniciada_em && sala.encerrada_em ? Math.max(1, Math.round((new Date(sala.encerrada_em).getTime() - new Date(sala.iniciada_em).getTime()) / 60000)) : null;
      return { id: u.id, empresa: nomes.get(u.empresa_id) ?? "Empresa", titulo: sala?.titulo ?? "Live", origem: u.origem, pessoas: u.pessoas, pico: sala?.pico_pessoas ?? 0, minutos, created_at: u.created_at };
    }),
    numeros: {
      livesNoMes: noMes.length,
      gratisNoMes: noMes.filter((u) => u.origem === "gratis").length,
      receitaNoMes: compras.filter((c) => c.status === "aprovada" && (c.aprovada_em ?? "") >= desde).reduce((soma, c) => soma + Number(c.preco), 0),
      aoVivoAgora: aoVivoR.count ?? 0,
      empresasQueUsaram: new Set(noMes.map((u) => u.empresa_id)).size,
    },
  };
}

function texto(valor: unknown, teto: number): string {
  return typeof valor === "string" ? valor.trim().slice(0, teto) : "";
}

function inteiro(valor: unknown): number {
  const n = typeof valor === "number" ? valor : Number(String(valor ?? "").replace(/\D/g, ""));
  return Number.isFinite(n) ? Math.round(n) : NaN;
}

export function lerPacoteDeLive(corpo: Record<string, unknown>) {
  const nome = texto(corpo.nome, 60);
  if (nome.length < 2) throw new ErroDoPainel("Dê um nome ao pacote.");
  const lives = inteiro(corpo.lives);
  if (!Number.isInteger(lives) || lives < 1 || lives > 1000) throw new ErroDoPainel("Quantas lives o pacote libera (de 1 a 1.000)?");
  const pessoas = inteiro(corpo.pessoas);
  if (!Number.isInteger(pessoas) || pessoas < 1 || pessoas > 100000) throw new ErroDoPainel("Quantas pessoas cabem em cada live (de 1 a 100.000)?");
  const preco = Number(corpo.preco);
  if (!Number.isFinite(preco) || preco < 1) throw new ErroDoPainel("O preço precisa ser de pelo menos R$ 1,00.");
  return {
    nome,
    descricao: texto(corpo.descricao, 200) || null,
    lives,
    pessoas,
    preco: Math.round(preco * 100) / 100,
    ativo: corpo.ativo !== false,
    destaque: corpo.destaque === true,
    ordem: Number.isFinite(Number(corpo.ordem)) ? Math.round(Number(corpo.ordem)) : 0,
  };
}

/* Só um pacote em destaque ("Mais escolhido"). */
async function destaqueUnico(admin: Admin, id: string) {
  await admin.from("live_pacotes").update({ destaque: false }).neq("id", id).eq("destaque", true);
}

export async function criarPacoteDeLive(admin: Admin, corpo: Record<string, unknown>) {
  const pacote = lerPacoteDeLive(corpo);
  const { data, error } = await admin.from("live_pacotes").insert(pacote).select("id").single();
  if (error || !data) throw new ErroDoPainel("Não foi possível criar o pacote.", 500);
  if (pacote.destaque) await destaqueUnico(admin, (data as { id: string }).id);
}

export async function alterarPacoteDeLive(admin: Admin, id: string, corpo: Record<string, unknown>) {
  if (!/^[0-9a-f-]{36}$/i.test(id)) throw new ErroDoPainel("Pacote inválido.");
  const pacote = lerPacoteDeLive(corpo);
  const { error } = await admin.from("live_pacotes").update({ ...pacote, updated_at: new Date().toISOString() }).eq("id", id);
  if (error) throw new ErroDoPainel("Não foi possível salvar o pacote.", 500);
  if (pacote.destaque) await destaqueUnico(admin, id);
}

export async function excluirPacoteDeLive(admin: Admin, id: string) {
  if (!/^[0-9a-f-]{36}$/i.test(id)) throw new ErroDoPainel("Pacote inválido.");
  /* Pacote já comprado fica (a compra aponta para ele): só sai da venda. */
  const { count } = await admin.from("live_compras").select("id", { count: "exact", head: true }).eq("pacote_id", id);
  if ((count ?? 0) > 0) {
    await admin.from("live_pacotes").update({ ativo: false, destaque: false }).eq("id", id);
    return { saiuDaVenda: true };
  }
  await admin.from("live_pacotes").delete().eq("id", id);
  return { saiuDaVenda: false };
}

export async function salvarRegrasDaLive(admin: Admin, corpo: Record<string, unknown>, quem: string | null) {
  const gratis = inteiro(corpo.gratis_lives_por_mes);
  const pessoas = inteiro(corpo.gratis_pessoas);
  const validade = inteiro(corpo.validade_dias);
  if (!Number.isInteger(gratis) || gratis < 0 || gratis > 1000) throw new ErroDoPainel("Lives grátis por mês: de 0 a 1.000.");
  if (!Number.isInteger(pessoas) || pessoas < 1 || pessoas > 100000) throw new ErroDoPainel("Pessoas na live grátis: de 1 a 100.000.");
  if (!Number.isInteger(validade) || validade < 1 || validade > 3650) throw new ErroDoPainel("Validade do pacote: de 1 a 3.650 dias.");
  const { error } = await admin.from("live_regras").upsert({
    id: 1,
    gratis_lives_por_mes: gratis,
    gratis_pessoas: pessoas,
    validade_dias: validade,
    vendas_abertas: corpo.vendas_abertas !== false,
    updated_at: new Date().toISOString(),
    updated_by: quem,
  });
  if (error) throw new ErroDoPainel("Não foi possível salvar as regras.", 500);
}

/*
 * Quanto uma live custa na Cloudflare (Realtime SFU): cada pessoa recebe o
 * vídeo de quem apresenta, ~1,5 Mbps. US$ 0,05 por GB depois de 1.000 GB
 * grátis no mês (a conta inteira). Só para o dono pensar no preço.
 */
export function custoEstimadoDaLive(pessoas: number, minutos = 90, dolar = 5.5): { gb: number; reais: number } {
  const gb = (pessoas * 1.5 * 1_000_000 * minutos * 60) / 8 / 1_000_000_000;
  return { gb: Math.round(gb * 10) / 10, reais: Math.round(gb * 0.05 * dolar * 100) / 100 };
}
