import { configuracaoDoR2, enderecoParaBaixar } from "@/lib/digitais/r2";
import { ErroNosDados, lerDadosDaLive } from "@/lib/live/sala";
import {
  atualizarLive,
  baseDaLoja,
  CAMPOS_DA_LIVE,
  cancelarLive,
  criarLive,
  lerLive,
  linkDaLive,
  linkDeQuemApresenta,
  liveParaATela,
  novoConvite,
  publicarLive,
  saldoDeLives,
  vagasPorProduto,
  type LiveNoBanco,
} from "@/lib/live/servidor/lives";

import { ErroDigital, type ContextoDigital } from "../contexto";

/*
 * O que as rotas das lives fazem (app/api/digitais/lives/**), fora delas: a
 * tela do painel e o assistente (MCP) chamam as mesmas funções. O trabalho de
 * verdade (criar produto + sala, links, cancelar) mora em lib/live/servidor.
 */

/* As lives da loja, com o que sobra do mês (grátis e pacotes). */
export async function listarLivesDaLoja({ admin, lojaId, pode }: ContextoDigital) {
  const [lives, saldo] = await Promise.all([
    admin.from("live_salas").select(CAMPOS_DA_LIVE).eq("user_id", lojaId).order("comeca_em", { ascending: false }).limit(200),
    saldoDeLives(admin, lojaId),
  ]);
  if (lives.error) throw new ErroDigital("Não foi possível ler as lives.", 500);
  const lista = (lives.data ?? []) as LiveNoBanco[];
  const produtos = lista.map((l) => l.product_id).filter((id): id is string => Boolean(id));
  const [vagas, produtosR, base] = await Promise.all([
    vagasPorProduto(admin, lojaId, produtos),
    produtos.length
      ? admin.from("products").select("id,price,image_url,show_in_catalog,active,description").eq("user_id", lojaId).in("id", produtos)
      : Promise.resolve({ data: [] }),
    baseDaLoja(admin, lojaId).catch(() => null),
  ]);
  const porId = new Map(
    (
      (produtosR.data ?? []) as Array<{
        id: string;
        price: number;
        image_url: string | null;
        show_in_catalog: boolean | null;
        active: boolean | null;
        description: string | null;
      }>
    ).map((p) => [p.id, p]),
  );
  return {
    podeEditar: pode("digital.products.write"),
    saldo,
    lives: lista.map((l) => {
      const p = l.product_id ? porId.get(l.product_id) : undefined;
      return liveParaATela(l, {
        vagas: l.product_id ? vagas.get(l.product_id) ?? 0 : 0,
        naLoja: Boolean(p?.show_in_catalog) && p?.active !== false,
        preco: Number(p?.price) || 0,
        capa: p?.image_url ?? null,
        descricao: p?.description ?? null,
        base,
      });
    }),
  };
}

/* Cria a live (fora da loja até publicar). */
export async function criarLiveDaLoja({ admin, lojaId }: ContextoDigital, corpo: unknown) {
  try {
    const dados = lerDadosDaLive(corpo);
    const criada = await criarLive(admin, lojaId, dados);
    /* "Começar agora": já devolve o link de quem apresenta para abrir a sala. */
    const apresentar =
      (corpo as { agora?: unknown } | null)?.agora === true ? await linkDeQuemApresenta(admin, lojaId, await lerLive(admin, lojaId, criada.id)) : null;
    return { ok: true, ...criada, apresentar };
  } catch (erro) {
    if (erro instanceof ErroNosDados) throw new ErroDigital(erro.message, 400);
    throw erro;
  }
}

/* Uma live, com os links, o saldo do mês e os produtos da loja (para soltar e para o liveshop). */
export async function lerLiveDaLoja({ admin, lojaId, pode }: ContextoDigital, id: string) {
  const live = await lerLive(admin, lojaId, id);
  const [saldo, vagas, produtoR, produtosDaLoja, links] = await Promise.all([
    saldoDeLives(admin, lojaId),
    vagasPorProduto(admin, lojaId, live.product_id ? [live.product_id] : []),
    live.product_id
      ? admin.from("products").select("price,image_url,show_in_catalog,active,description").eq("id", live.product_id).eq("user_id", lojaId).maybeSingle()
      : Promise.resolve({ data: null }),
    admin.from("products").select("id,name,price,image_url,product_kind").eq("user_id", lojaId).neq("active", false).order("name", { ascending: true }).limit(800),
    linkDaLive(admin, lojaId, live).catch(() => ({ sala: null, pagina: null })),
  ]);
  const p = produtoR.data as {
    price: number;
    image_url: string | null;
    show_in_catalog: boolean | null;
    active: boolean | null;
    description: string | null;
  } | null;
  return {
    podeEditar: pode("digital.products.write"),
    saldo,
    links,
    live: liveParaATela(live, {
      vagas: live.product_id ? vagas.get(live.product_id) ?? 0 : 0,
      naLoja: Boolean(p?.show_in_catalog) && p?.active !== false,
      preco: Number(p?.price) || 0,
      capa: p?.image_url ?? null,
      descricao: p?.description ?? null,
      base: links.sala ? links.sala.replace(/\/[a-z]{3}-[a-z]{4}-[a-z]{3}$/, "") : null,
    }),
    produtos: ((produtosDaLoja.data ?? []) as Array<{ id: string; name: string; price: number | null; image_url: string | null; product_kind: string | null }>)
      .filter((x) => x.id !== live.product_id)
      .map((x) => ({ id: x.id, nome: x.name, preco: Number(x.price) || 0, imagem: x.image_url, digital: x.product_kind === "digital" })),
  };
}

/* Salva o que a loja preencheu; `naLoja` põe ou tira da vitrine. */
export async function salvarLiveDaLoja({ admin, lojaId, situacao }: ContextoDigital, id: string, bruto: unknown) {
  const live = await lerLive(admin, lojaId, id);
  const corpo = bruto as Record<string, unknown> | null;
  try {
    const dados = lerDadosDaLive(corpo?.dados ?? corpo);
    await atualizarLive(admin, lojaId, live, dados);
  } catch (erro) {
    if (erro instanceof ErroNosDados) throw new ErroDigital(erro.message, 400);
    throw erro;
  }
  if (typeof corpo?.naLoja === "boolean") await publicarLive(admin, lojaId, await lerLive(admin, lojaId, id), corpo.naLoja, situacao);
  return { ok: true };
}

/* Cancela a live (sai da loja; quem comprou é avisado pela loja). */
export async function cancelarLiveDaLoja({ admin, lojaId }: ContextoDigital, id: string) {
  const live = await lerLive(admin, lojaId, id);
  await cancelarLive(admin, lojaId, live);
  return { ok: true };
}

/* "Entrar ao vivo": o link de quem apresenta, novo a cada clique (vale 12 horas). */
export async function linkParaApresentar({ admin, lojaId }: ContextoDigital, id: string) {
  const live = await lerLive(admin, lojaId, id);
  if (live.status === "cancelada") throw new ErroDigital("Esta live foi cancelada.", 400);
  if (live.status === "encerrada") throw new ErroDigital("Esta live já aconteceu.", 400);
  return { link: await linkDeQuemApresenta(admin, lojaId, live) };
}

/* Um link novo para convidar alguém a apresentar junto (o anterior para de valer). */
export async function conviteParaApresentarJunto({ admin, lojaId }: ContextoDigital, id: string) {
  const live = await lerLive(admin, lojaId, id);
  if (live.status === "cancelada" || live.status === "encerrada") throw new ErroDigital("Esta live não acontece mais.", 400);
  return { link: await novoConvite(admin, lojaId, live) };
}

export type GravacaoDaLive = {
  id: string;
  nome: string;
  mime: string | null;
  tamanho_bytes: number;
  storage_key: string;
  meta: { segundos?: number } | null;
  criado_em: string;
};

/* Os arquivos de gravação desta live (prontos, no depósito, na ordem em que foram gravados). */
export async function arquivosDasGravacoes({ admin, lojaId }: ContextoDigital, id: string): Promise<GravacaoDaLive[]> {
  const live = await lerLive(admin, lojaId, id);
  const { data } = await admin
    .from("digital_files")
    .select("id,nome,mime,tamanho_bytes,storage_key,meta,criado_em")
    .eq("user_id", lojaId)
    .eq("meta->>live", live.id)
    .eq("status", "pronto")
    .order("criado_em", { ascending: true })
    .limit(50);
  return (data ?? []) as GravacaoDaLive[];
}

/* As gravações desta live, com o endereço para baixar (vence em 15 minutos). */
export async function gravacoesParaOPainel(contexto: ContextoDigital, id: string) {
  await lerLive(contexto.admin, contexto.lojaId, id);
  const cfg = configuracaoDoR2();
  if (!cfg) throw new ErroDigital("O depósito de arquivos não está ligado neste servidor.", 503);
  const arquivos = await arquivosDasGravacoes(contexto, id);
  return {
    gravacoes: arquivos.map((a) => ({
      id: a.id,
      nome: a.nome,
      bytes: Number(a.tamanho_bytes) || 0,
      segundos: Number(a.meta?.segundos) || 0,
      baixar: enderecoParaBaixar(cfg, a.storage_key, { nome: a.nome, tipo: a.mime }),
      assistir: enderecoParaBaixar(cfg, a.storage_key, { nome: a.nome, tipo: a.mime, abrirNoNavegador: true }),
    })),
  };
}

/* Os produtos da loja (físicos e digitais), para soltar na live e para o liveshop. */
export async function produtosParaALive({ admin, lojaId }: ContextoDigital) {
  const { data } = await admin
    .from("products")
    .select("id,name,price,image_url,product_kind")
    .eq("user_id", lojaId)
    .neq("active", false)
    .order("name", { ascending: true })
    .limit(800);
  return {
    produtos: ((data ?? []) as Array<{ id: string; name: string; price: number | null; image_url: string | null; product_kind: string | null }>).map((x) => ({
      id: x.id,
      nome: x.name,
      preco: Number(x.price) || 0,
      imagem: x.image_url,
      digital: x.product_kind === "digital",
    })),
  };
}
