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

import { lerCapitulos, lerConfigDoCurso, lerLinkDeVideo, resumoDoConteudo, type Capitulo, type LinkDeVideo, type TipoDeAula } from "../curso.ts";
import { LEGENDAS_DE_AULA } from "../formatos.ts";
import { lerConfigDigital, type ConfigDigital } from "../produto.ts";
import { ErroDigital } from "./contexto";
import { arquivoNaTela, lerProdutoDaLoja, type ArquivoNaTela } from "./produtos";

/*
 * O curso no painel: módulos, aulas e materiais, sempre presos à loja e a um
 * produto digital do tipo curso. Toda mudança na estrutura refaz o resumo
 * público (`digital_config.curso.conteudo`), que é o que a página de venda
 * mostra.
 */

export type ModuloNoEditor = {
  id: string;
  titulo: string;
  descricao: string | null;
  posicao: number;
  liberacao: "imediata" | "dias" | "data";
  liberarDias: number | null;
  liberarEm: string | null;
  /* A foto do pôster do módulo na área de membros (null: capa feita com a cor e o título). */
  capa: string | null;
  aulas: AulaNoEditor[];
};

export type MaterialNoEditor = { id: string; titulo: string; url: string | null; arquivo: ArquivoNaTela | null; posicao: number };

export type AulaNoEditor = {
  id: string;
  moduloId: string;
  titulo: string;
  tipo: TipoDeAula;
  video: ArquivoNaTela | null;
  link: LinkDeVideo | null;
  duracaoS: number | null;
  descricao: string | null;
  texto: string | null;
  aoVivoEm: string | null;
  aoVivoLink: string | null;
  previaGratis: boolean;
  comentarios: boolean;
  liberacao: "modulo" | "dias" | "data";
  liberarDias: number | null;
  liberarEm: string | null;
  posicao: number;
  publicada: boolean;
  materiais: MaterialNoEditor[];
  /* A legenda (.vtt ou .srt) e os capítulos da linha do tempo. */
  legenda: { id: string; nome: string; extensao: string; status: string } | null;
  capitulos: Capitulo[];
};

type ArquivoDaLinha = { id: string; nome: string; extensao: string; categoria: string; tamanho_bytes: number; status: string; meta: Record<string, unknown> | null };

export async function exigirCurso(admin: SupabaseClient, lojaId: string, productId: string) {
  const produto = await lerProdutoDaLoja(admin, lojaId, productId);
  const config = lerConfigDigital(produto.digital_config);
  if (config.tipo !== "curso") throw new ErroDigital("Este produto não é um conteúdo em vídeo.", 400);
  return { produto, config };
}

/* O curso tem um entregável "curso": é o acesso a ele que a compra libera. */
export async function garantirEntregavelDoCurso(admin: SupabaseClient, lojaId: string, productId: string): Promise<string> {
  const { data } = await admin
    .from("digital_deliverables")
    .select("id,ativo,apagado_em")
    .eq("user_id", lojaId)
    .eq("product_id", productId)
    .eq("tipo", "curso")
    .is("apagado_em", null)
    .limit(1);
  const existente = ((data ?? []) as Array<{ id: string; ativo: boolean }>)[0];
  if (existente) {
    if (!existente.ativo) await admin.from("digital_deliverables").update({ ativo: true }).eq("id", existente.id).eq("user_id", lojaId);
    return existente.id;
  }
  const { data: criado, error } = await admin
    .from("digital_deliverables")
    .insert({ user_id: lojaId, product_id: productId, tipo: "curso", titulo: "Acesso ao conteúdo", posicao: 0, ativo: true })
    .select("id")
    .single();
  if (error || !criado) throw new ErroDigital("Não foi possível preparar o acesso ao conteúdo.", 500);
  return (criado as { id: string }).id;
}

export async function lerEstruturaDoCurso(admin: SupabaseClient, lojaId: string, productId: string): Promise<ModuloNoEditor[]> {
  const [modulos, aulas] = await Promise.all([
    admin
      .from("digital_course_modules")
      .select("id,titulo,descricao,posicao,liberacao,liberar_dias,liberar_em,capa_url")
      .eq("user_id", lojaId)
      .eq("product_id", productId)
      .is("apagado_em", null)
      .order("posicao", { ascending: true })
      .order("criado_em", { ascending: true }),
    admin
      .from("digital_course_lessons")
      .select(
        "id,module_id,titulo,tipo,video_link,duracao_s,descricao,texto,ao_vivo_em,ao_vivo_link,previa_gratis,comentarios,liberacao,liberar_dias,liberar_em,posicao,publicada,capitulos,video:digital_files!digital_course_lessons_video_file_id_fkey(id,nome,extensao,categoria,tamanho_bytes,status,meta),legenda:digital_files!digital_course_lessons_legenda_file_id_fkey(id,nome,extensao,status),digital_course_materials(id,titulo,url,posicao,digital_files(id,nome,extensao,categoria,tamanho_bytes,status,meta))"
      )
      .eq("user_id", lojaId)
      .eq("product_id", productId)
      .is("apagado_em", null)
      .order("posicao", { ascending: true })
      .order("criado_em", { ascending: true }),
  ]);
  if (modulos.error || aulas.error) throw new ErroDigital("Não foi possível ler o conteúdo.", 500);

  const porModulo = new Map<string, AulaNoEditor[]>();
  for (const linha of (aulas.data ?? []) as unknown as Array<{
    id: string;
    module_id: string;
    titulo: string;
    tipo: TipoDeAula;
    video_link: unknown;
    duracao_s: number | null;
    descricao: string | null;
    texto: string | null;
    ao_vivo_em: string | null;
    ao_vivo_link: string | null;
    previa_gratis: boolean;
    comentarios: boolean;
    liberacao: "modulo" | "dias" | "data";
    liberar_dias: number | null;
    liberar_em: string | null;
    posicao: number;
    publicada: boolean;
    capitulos: unknown;
    video: ArquivoDaLinha | null;
    legenda: { id: string; nome: string; extensao: string; status: string } | null;
    digital_course_materials: Array<{ id: string; titulo: string; url: string | null; posicao: number; digital_files: ArquivoDaLinha | null }> | null;
  }>) {
    const lista = porModulo.get(linha.module_id) ?? [];
    lista.push({
      id: linha.id,
      moduloId: linha.module_id,
      titulo: linha.titulo,
      tipo: linha.tipo,
      video: linha.video ? arquivoNaTela(linha.video) : null,
      link: linha.video_link && typeof linha.video_link === "object" ? lerLinkDeVideo((linha.video_link as { url?: string }).url) : null,
      duracaoS: linha.duracao_s,
      descricao: linha.descricao,
      texto: linha.texto,
      aoVivoEm: linha.ao_vivo_em,
      aoVivoLink: linha.ao_vivo_link,
      previaGratis: linha.previa_gratis,
      comentarios: linha.comentarios,
      liberacao: linha.liberacao,
      liberarDias: linha.liberar_dias,
      liberarEm: linha.liberar_em,
      posicao: linha.posicao,
      publicada: linha.publicada,
      materiais: (linha.digital_course_materials ?? [])
        .slice()
        .sort((a, b) => a.posicao - b.posicao)
        .map((m) => ({ id: m.id, titulo: m.titulo, url: m.url, arquivo: m.digital_files ? arquivoNaTela(m.digital_files) : null, posicao: m.posicao })),
      legenda: linha.legenda && linha.legenda.status !== "apagado" ? linha.legenda : null,
      capitulos: lerCapitulos(linha.capitulos),
    });
    porModulo.set(linha.module_id, lista);
  }

  return ((modulos.data ?? []) as Array<{
    id: string;
    titulo: string;
    descricao: string | null;
    posicao: number;
    liberacao: "imediata" | "dias" | "data";
    liberar_dias: number | null;
    liberar_em: string | null;
    capa_url: string | null;
  }>).map((m) => ({
    id: m.id,
    titulo: m.titulo,
    descricao: m.descricao,
    posicao: m.posicao,
    liberacao: m.liberacao,
    liberarDias: m.liberar_dias,
    liberarEm: m.liberar_em,
    capa: m.capa_url,
    aulas: porModulo.get(m.id) ?? [],
  }));
}

/* A aula tem o que mostrar? (vídeo pronto, link, texto, material ou o horário ao vivo) */
export function aulaTemConteudo(aula: AulaNoEditor): boolean {
  if (aula.tipo === "video") return Boolean((aula.video && aula.video.status === "pronto") || aula.link);
  if (aula.tipo === "texto") return Boolean(aula.texto?.trim());
  if (aula.tipo === "materiais") return aula.materiais.length > 0;
  return Boolean(aula.aoVivoEm && aula.aoVivoLink);
}

/* Refaz o resumo público e o "o que você recebe" do curso. */
export async function atualizarResumoDoCurso(admin: SupabaseClient, lojaId: string, productId: string): Promise<ConfigDigital> {
  const [{ produto }, modulos] = await Promise.all([exigirCurso(admin, lojaId, productId), lerEstruturaDoCurso(admin, lojaId, productId)]);
  const config = lerConfigDigital(produto.digital_config);
  const aulas = modulos.flatMap((m) => m.aulas);
  const materiais = aulas.reduce((soma, a) => soma + (a.publicada ? a.materiais.length : 0), 0);
  const conteudo = resumoDoConteudo(
    modulos.map((m) => ({ id: m.id, titulo: m.titulo, descricao: m.descricao, liberacao: m.liberacao, dias: m.liberarDias, em: m.liberarEm })),
    aulas.map((a) => ({
      id: a.id,
      moduloId: a.moduloId,
      titulo: a.titulo,
      tipo: a.tipo,
      duracaoS: a.duracaoS,
      previaGratis: a.previaGratis,
      publicada: a.publicada && aulaTemConteudo(a),
    })),
    materiais
  );
  const horas = conteudo.duracaoS >= 3600 ? `${Math.floor(conteudo.duracaoS / 3600)}h${String(Math.round((conteudo.duracaoS % 3600) / 60)).padStart(2, "0")}` : `${Math.round(conteudo.duracaoS / 60)} min`;
  const atualizada: ConfigDigital = {
    ...config,
    curso: { ...lerConfigDoCurso(config.curso), conteudo },
    recebe: [
      {
        titulo: "Conteúdo em vídeo",
        detalhe: `${conteudo.aulas} ${conteudo.aulas === 1 ? "vídeo" : "vídeos"}${conteudo.duracaoS ? ` · ${horas} de vídeo` : ""}${conteudo.materiais ? ` · ${conteudo.materiais} ${conteudo.materiais === 1 ? "material" : "materiais"}` : ""}`,
        selo: "VÍDEO",
      },
    ],
  };
  const { error } = await admin
    .from("products")
    .update({ digital_config: atualizada })
    .eq("id", productId)
    .eq("user_id", lojaId)
    .eq("product_kind", "digital");
  if (error) throw new ErroDigital("Não foi possível atualizar a página do conteúdo.", 500);
  return atualizada;
}

/* ------------------------------------------------------------ conferências */

export function textoLimpo(valor: unknown, teto: number): string | null {
  if (typeof valor !== "string") return null;
  const limpo = valor.replace(/\u0000/g, "").trim();
  return limpo ? limpo.slice(0, teto) : null;
}

export function liberacaoDoModulo(corpo: Record<string, unknown>) {
  const liberacao = corpo.liberacao === "dias" || corpo.liberacao === "data" ? corpo.liberacao : "imediata";
  const dias = Math.round(Number(corpo.liberarDias));
  if (liberacao === "dias" && !(dias >= 1 && dias <= 3650)) throw new ErroDigital("Diga em quantos dias o módulo abre (de 1 a 3650).", 400);
  const em = typeof corpo.liberarEm === "string" ? new Date(corpo.liberarEm) : null;
  if (liberacao === "data" && (!em || Number.isNaN(em.getTime()))) throw new ErroDigital("Escolha a data em que o módulo abre.", 400);
  return {
    liberacao,
    liberar_dias: liberacao === "dias" ? dias : null,
    liberar_em: liberacao === "data" && em ? em.toISOString() : null,
  } as const;
}

export function liberacaoDaAula(corpo: Record<string, unknown>) {
  const liberacao = corpo.liberacao === "dias" || corpo.liberacao === "data" ? corpo.liberacao : "modulo";
  const dias = Math.round(Number(corpo.liberarDias));
  if (liberacao === "dias" && !(dias >= 1 && dias <= 3650)) throw new ErroDigital("Diga em quantos dias o vídeo abre (de 1 a 3650).", 400);
  const em = typeof corpo.liberarEm === "string" ? new Date(corpo.liberarEm) : null;
  if (liberacao === "data" && (!em || Number.isNaN(em.getTime()))) throw new ErroDigital("Escolha a data em que o vídeo abre.", 400);
  return {
    liberacao,
    liberar_dias: liberacao === "dias" ? dias : null,
    liberar_em: liberacao === "data" && em ? em.toISOString() : null,
  } as const;
}

/* O vídeo da biblioteca que a aula vai usar: desta loja, e vídeo de verdade. */
/* A legenda escolhida é um .vtt ou .srt da biblioteca desta loja. */
export async function conferirLegendaDaBiblioteca(admin: SupabaseClient, lojaId: string, arquivoId: string) {
  if (!/^[0-9a-f-]{36}$/i.test(arquivoId)) throw new ErroDigital("Legenda não encontrada na sua biblioteca.", 404);
  const { data } = await admin.from("digital_files").select("id,status,extensao").eq("id", arquivoId).eq("user_id", lojaId).maybeSingle();
  const arquivo = data as { id: string; status: string; extensao: string } | null;
  if (!arquivo || arquivo.status === "apagado" || arquivo.status === "recusado") throw new ErroDigital("Legenda não encontrada na sua biblioteca.", 404);
  if (!LEGENDAS_DE_AULA.has(String(arquivo.extensao).toLowerCase())) throw new ErroDigital("A legenda precisa ser um arquivo .vtt ou .srt.", 400);
  return arquivo;
}

export async function conferirVideoDaBiblioteca(admin: SupabaseClient, lojaId: string, arquivoId: string) {
  const { data } = await admin
    .from("digital_files")
    .select("id,categoria,status,extensao,meta")
    .eq("id", arquivoId)
    .eq("user_id", lojaId)
    .maybeSingle();
  const arquivo = data as { id: string; categoria: string; status: string; extensao: string; meta: Record<string, unknown> | null } | null;
  if (!arquivo || arquivo.status === "apagado" || arquivo.status === "recusado") throw new ErroDigital("Vídeo não encontrado na sua biblioteca.", 404);
  if (arquivo.categoria !== "video") throw new ErroDigital("Esse arquivo não é um vídeo. Use MP4, WebM ou MOV.", 400);
  const duracao = Number(arquivo.meta?.duracao);
  return { arquivo, duracaoS: Number.isFinite(duracao) && duracao > 0 ? Math.round(duracao) : null };
}

/* Um arquivo qualquer da biblioteca (material da aula). */
export async function conferirArquivoDaBiblioteca(admin: SupabaseClient, lojaId: string, arquivoId: string) {
  const { data } = await admin.from("digital_files").select("id,nome,status").eq("id", arquivoId).eq("user_id", lojaId).maybeSingle();
  const arquivo = data as { id: string; nome: string; status: string } | null;
  if (!arquivo || arquivo.status === "apagado" || arquivo.status === "recusado") throw new ErroDigital("Arquivo não encontrado na sua biblioteca.", 404);
  return arquivo;
}

/* O módulo é deste curso? */
export async function exigirModulo(admin: SupabaseClient, lojaId: string, productId: string, moduloId: string) {
  if (!/^[0-9a-f-]{36}$/i.test(moduloId)) throw new ErroDigital("Módulo não encontrado.", 404);
  const { data } = await admin
    .from("digital_course_modules")
    .select("id,posicao")
    .eq("id", moduloId)
    .eq("user_id", lojaId)
    .eq("product_id", productId)
    .is("apagado_em", null)
    .maybeSingle();
  if (!data) throw new ErroDigital("Módulo não encontrado.", 404);
  return data as { id: string; posicao: number };
}

/* A aula é deste curso? */
export async function exigirAula(admin: SupabaseClient, lojaId: string, productId: string, aulaId: string) {
  if (!/^[0-9a-f-]{36}$/i.test(aulaId)) throw new ErroDigital("Vídeo não encontrado.", 404);
  const { data } = await admin
    .from("digital_course_lessons")
    .select("id,module_id,posicao,tipo,video_file_id")
    .eq("id", aulaId)
    .eq("user_id", lojaId)
    .eq("product_id", productId)
    .is("apagado_em", null)
    .maybeSingle();
  if (!data) throw new ErroDigital("Vídeo não encontrado.", 404);
  return data as { id: string; module_id: string; posicao: number; tipo: TipoDeAula; video_file_id: string | null };
}

/* Reordena: as posições seguem a ordem dos ids recebidos (só os deste dono). */
export async function gravarOrdem(
  admin: SupabaseClient,
  tabela: "digital_course_modules" | "digital_course_lessons",
  lojaId: string,
  productId: string,
  ids: string[],
  extra: Record<string, unknown> = {}
) {
  const agora = new Date().toISOString();
  for (let i = 0; i < ids.length; i += 1) {
    const { error } = await admin
      .from(tabela)
      .update({ posicao: i, atualizado_em: agora, ...extra })
      .eq("id", ids[i])
      .eq("user_id", lojaId)
      .eq("product_id", productId)
      .is("apagado_em", null);
    if (error) throw new ErroDigital("Não foi possível salvar a ordem.", 500);
  }
}

/* O curso pode ir para a loja? (o que falta, em português) */
export function pendenciasDoCurso(params: { nome: string; imagem: string | null; modulos: ModuloNoEditor[] }): string[] {
  const faltas: string[] = [];
  if (!params.nome.trim()) faltas.push("Dê um nome ao conteúdo.");
  if (!params.imagem) faltas.push("Coloque uma capa: é ela que aparece na vitrine.");
  const aulas = params.modulos.flatMap((m) => m.aulas).filter((a) => a.publicada);
  if (!aulas.some(aulaTemConteudo)) faltas.push("Monte pelo menos um item com conteúdo: vídeo, texto, material ou transmissão ao vivo.");
  if (aulas.some((a) => a.tipo === "video" && a.video && a.video.status !== "pronto")) faltas.push("Espere os vídeos terminarem de enviar.");
  return faltas;
}

/* Os cursos em que um arquivo da biblioteca aparece (vídeo de aula, material ou apresentação). */
export async function usosNosCursos(admin: SupabaseClient, lojaId: string, arquivoId: string): Promise<{ produtos: string[]; nomes: string[] }> {
  const [aulas, materiais, apresentacoes] = await Promise.all([
    admin
      .from("digital_course_lessons")
      .select("product_id,products(name)")
      .eq("user_id", lojaId)
      .or(`video_file_id.eq.${arquivoId},legenda_file_id.eq.${arquivoId}`)
      .is("apagado_em", null),
    admin
      .from("digital_course_materials")
      .select("digital_course_lessons!inner(product_id,apagado_em,products(name))")
      .eq("user_id", lojaId)
      .eq("file_id", arquivoId)
      .is("digital_course_lessons.apagado_em", null),
    admin
      .from("products")
      .select("id,name")
      .eq("user_id", lojaId)
      .eq("product_kind", "digital")
      .eq("digital_config->curso->apresentacao->>arquivoId", arquivoId),
  ]);
  const produtos = new Map<string, string>();
  for (const linha of (aulas.data ?? []) as unknown as Array<{ product_id: string; products: { name: string } | null }>) {
    produtos.set(linha.product_id, linha.products?.name ?? "um conteúdo em vídeo");
  }
  for (const linha of (materiais.data ?? []) as unknown as Array<{ digital_course_lessons: { product_id: string; products: { name: string } | null } | null }>) {
    if (linha.digital_course_lessons) produtos.set(linha.digital_course_lessons.product_id, linha.digital_course_lessons.products?.name ?? "um conteúdo em vídeo");
  }
  for (const linha of (apresentacoes.data ?? []) as Array<{ id: string; name: string }>) produtos.set(linha.id, linha.name);
  return { produtos: Array.from(produtos.keys()), nomes: Array.from(new Set(produtos.values())) };
}

/* Arquivo que não vai existir (envio cancelado ou recusado): sai das aulas, dos materiais e da apresentação. */
export async function tirarArquivoDosCursos(admin: SupabaseClient, lojaId: string, arquivoId: string): Promise<void> {
  const { produtos } = await usosNosCursos(admin, lojaId, arquivoId);
  const agora = new Date().toISOString();
  await admin.from("digital_course_lessons").update({ video_file_id: null, atualizado_em: agora }).eq("user_id", lojaId).eq("video_file_id", arquivoId);
  await admin.from("digital_course_lessons").update({ legenda_file_id: null, atualizado_em: agora }).eq("user_id", lojaId).eq("legenda_file_id", arquivoId);
  await admin.from("digital_course_materials").delete().eq("user_id", lojaId).eq("file_id", arquivoId);
  for (const produtoId of produtos) {
    const { data } = await admin.from("products").select("digital_config").eq("id", produtoId).eq("user_id", lojaId).maybeSingle();
    const config = lerConfigDigital((data as { digital_config?: unknown } | null)?.digital_config);
    const apresentacao = config.curso?.apresentacao;
    if (config.curso && apresentacao && "arquivoId" in apresentacao && apresentacao.arquivoId === arquivoId) {
      await admin
        .from("products")
        .update({ digital_config: { ...config, curso: { ...config.curso, apresentacao: null } } })
        .eq("id", produtoId)
        .eq("user_id", lojaId);
    }
    await atualizarResumoDoCurso(admin, lojaId, produtoId).catch(() => undefined);
  }
}

/* O vídeo terminou de chegar: os cursos que usam ele passam a mostrar a aula. */
export async function arquivoProntoNosCursos(admin: SupabaseClient, lojaId: string, arquivoId: string): Promise<number> {
  const { produtos } = await usosNosCursos(admin, lojaId, arquivoId);
  for (const produtoId of produtos) await atualizarResumoDoCurso(admin, lojaId, produtoId).catch(() => undefined);
  return produtos.length;
}
