import {
  abrirPreviaDoCurso,
  adicionarMaterial,
  apagarAula,
  apagarModulo,
  criarAula,
  criarModulo,
  lerCurso,
  listarCursos,
  ordenarAulas,
  ordenarModulos,
  salvarAula,
  salvarModulo,
  tirarMaterial,
} from "@/lib/digitais/servidor/acoes/cursos";
import type { ContextoDigital } from "@/lib/digitais/servidor/contexto";

import type { ContextoDoAssistente } from "../contexto";
import type { Campos } from "../esquema";
import { DESCRICAO_DO_CAMPO_DE_IMAGEM } from "../imagens";
import { AVISO_DE_DADOS, reais, recusar, texto } from "./comum";
import {
  abrirDigital,
  arquivoIndicado,
  guardarImagemDigital,
  naRota,
  pedidoDoAssistente,
  prepararImagemDigital,
  semContatoNovo,
  type ImagemDigital,
} from "./digitais-comum";
import { dataEHoraBr, duracaoBr, instanteDeBrasilia } from "./digitais-regras";
import type { Ferramenta, FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/*
 * O conteúdo em vídeo pelo assistente: módulos, vídeos (com o tipo: vídeo,
 * texto, só materiais ou ao vivo), materiais para baixar, legenda,
 * capítulos, prévia grátis, quando cada parte abre e a capa de cada módulo.
 * As mesmas funções das rotas do editor (lib/digitais/servidor/acoes/cursos.ts),
 * com a permissão da tela (digital.products.read / digital.products.write).
 * Na tela, cada item do módulo se chama "vídeo", mesmo quando é texto.
 */

const TIPO_DO_VIDEO: Record<string, string> = { video: "video", texto: "texto", so_materiais: "materiais", ao_vivo: "ao_vivo" };
const TIPO_NA_FERRAMENTA: Record<string, string> = { video: "video", texto: "texto", materiais: "so_materiais", ao_vivo: "ao_vivo" };

type CursoLido = Awaited<ReturnType<typeof lerCurso>>;

async function lerConteudo(ctx: ContextoDoAssistente, contexto: ContextoDigital, id: string): Promise<CursoLido> {
  return naRota(() => lerCurso(contexto, id, pedidoDoAssistente(ctx)));
}

function conteudoParaOAssistente(lido: CursoLido) {
  return {
    id: lido.produto.id,
    nome: lido.produto.nome,
    preco: reais(lido.produto.preco),
    na_loja: lido.produto.naLoja,
    link_na_loja: lido.produto.enderecoNaLoja,
    clientes_com_acesso: lido.produto.alunos,
    vendas: lido.produto.vendas,
    modulos: lido.modulos.map((m) => ({
      modulo_id: m.id,
      titulo: m.titulo,
      descricao: m.descricao,
      abre: m.liberacao === "dias" ? `${m.liberarDias} dias depois da compra` : m.liberacao === "data" ? `no dia ${dataEHoraBr(m.liberarEm)}` : "logo depois da compra",
      capa: m.capa,
      videos: m.aulas.map((a) => ({
        video_id: a.id,
        titulo: a.titulo,
        tipo: TIPO_NA_FERRAMENTA[a.tipo] ?? a.tipo,
        publicado: a.publicada,
        arquivo_de_video: a.video ? { arquivo_id: a.video.id, nome: a.video.nome, situacao: a.video.status, tamanho: a.video.tamanhoLegivel } : null,
        link_do_video: a.link?.url ?? null,
        duracao: duracaoBr(a.duracaoS),
        descricao: texto(a.descricao, 400),
        texto: a.tipo === "texto" ? texto(a.texto, 600) : undefined,
        ao_vivo: a.tipo === "ao_vivo" ? { quando: dataEHoraBr(a.aoVivoEm), link: a.aoVivoLink } : undefined,
        previa_gratis: a.previaGratis,
        comentarios: a.comentarios,
        abre: a.liberacao === "dias" ? `${a.liberarDias} dias depois da compra` : a.liberacao === "data" ? `no dia ${dataEHoraBr(a.liberarEm)}` : "junto com o módulo",
        legenda: a.legenda ? { arquivo_id: a.legenda.id, nome: a.legenda.nome } : null,
        capitulos: a.capitulos.length,
        materiais: a.materiais.map((x) => ({ material_id: x.id, titulo: x.titulo, link: x.url, arquivo: x.arquivo ? { arquivo_id: x.arquivo.id, nome: x.arquivo.nome } : null })),
      })),
    })),
  };
}

/* ------------------------------------------------------------ leituras */

export const listarConteudosEmVideo: FerramentaDeLeitura = {
  nome: "listar_conteudos_em_video",
  titulo: "Ver os conteúdos em vídeo",
  descricao: "Lista os conteúdos em vídeo da loja (área de membros): módulos, quantidade de vídeos, duração total, clientes com acesso e quanto do conteúdo eles já assistiram (média).",
  campos: {},
  tipo: "leitura",
  permissoes: ["digital.products.read"],
  async executar(ctx) {
    const contexto = await abrirDigital(ctx, "digital.products.read", "ver os conteúdos em vídeo");
    const lista = await naRota(() => listarCursos(contexto));
    const conteudos = lista.cursos.map((c) => ({
      id: c.id,
      nome: c.nome,
      preco: reais(c.preco),
      na_loja: c.naLoja,
      modulos: c.modulos,
      videos: c.aulas,
      duracao: duracaoBr(c.duracaoS),
      clientes_com_acesso: c.alunos,
      assistido_em_media: `${c.conclusao}%`,
    }));
    return { dados: { _observacao: AVISO_DE_DADOS, conteudos, total: conteudos.length }, resumo: `Consultou os conteúdos em vídeo (${conteudos.length})` };
  },
};

export const verConteudoEmVideo: FerramentaDeLeitura = {
  nome: "ver_conteudo_em_video",
  titulo: "Ver um conteúdo em vídeo por dentro",
  descricao:
    "Mostra um conteúdo em vídeo como o editor: cada módulo (com modulo_id, quando abre e capa) e cada vídeo dele (video_id, tipo, publicado ou rascunho, o arquivo ou link do vídeo, duração, prévia grátis, comentários, quando abre, legenda, capítulos e os materiais para baixar).",
  campos: { produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo (de listar_conteudos_em_video).", obrigatorio: true } },
  tipo: "leitura",
  permissoes: ["digital.products.read"],
  async executar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.read", "ver os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    return { dados: { _observacao: AVISO_DE_DADOS, conteudo: conteudoParaOAssistente(lido) }, resumo: `Consultou o conteúdo em vídeo ${lido.produto.nome}` };
  },
};

export const linkParaVerComoCliente: FerramentaDeLeitura = {
  nome: "link_para_ver_como_cliente",
  titulo: "Ver o conteúdo como o cliente vê",
  descricao: "Gera o link de \"Ver como cliente\" da tela: abre os conteúdos da loja na área de membros sem compra, por 2 horas (não grava progresso nem comenta). É para a equipe conferir, não para mandar a clientes.",
  campos: { produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true } },
  tipo: "leitura",
  permissoes: ["digital.products.read"],
  async executar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.read", "ver os conteúdos em vídeo");
    const previa = await naRota(() => abrirPreviaDoCurso(contexto, String(args.produto_id), pedidoDoAssistente(ctx)));
    return {
      dados: {
        link: previa.link,
        vale_por: "2 horas",
        proximo_passo: "Mostre o link à pessoa exatamente como veio. É de uso da equipe: não mande a clientes.",
      },
      resumo: "Gerou o link de ver como cliente",
    };
  },
};

/* ------------------------------------------------------------ módulos */

const CAMPOS_DE_QUANDO_O_MODULO_ABRE: Campos = {
  abre: { tipo: "opcao", descricao: "Quando o módulo abre para o cliente: logo depois da compra, alguns dias depois ou numa data.", opcoes: ["logo_depois_da_compra", "dias_depois", "numa_data"] },
  abre_em_dias: { tipo: "inteiro", descricao: "Com abre = dias_depois: quantos dias depois da compra.", min: 1, max: 3650 },
  abre_no_dia: { tipo: "data", descricao: "Com abre = numa_data: o dia." },
};

function quandoAbre(args: Record<string, unknown>, padrao: "imediata" | "modulo") {
  if (args.abre === undefined) return {};
  const liberacao = args.abre === "dias_depois" ? "dias" : args.abre === "numa_data" ? "data" : padrao;
  if (liberacao === "dias" && !args.abre_em_dias) recusar("Diga em abre_em_dias quantos dias depois da compra.");
  if (liberacao === "data" && !args.abre_no_dia) recusar("Diga em abre_no_dia o dia em que abre.");
  return {
    liberacao,
    liberarDias: args.abre_em_dias,
    liberarEm: liberacao === "data" ? instanteDeBrasilia(String(args.abre_no_dia)) : undefined,
  };
}

type CargaDoModulo = { produtoId: string; moduloId: string | null; corpo: Record<string, unknown>; capa: ImagemDigital | null; nome: string };

async function lerModulo(ctx: ContextoDoAssistente, contexto: ContextoDigital, args: Record<string, unknown>) {
  const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
  const modulo = args.modulo_id ? lido.modulos.find((m) => m.id === args.modulo_id) : null;
  if (args.modulo_id && !modulo) recusar("Módulo não encontrado neste conteúdo. Veja os módulos em ver_conteudo_em_video.");
  return { lido, modulo };
}

export const criarModuloFerramenta: FerramentaDeEscrita<CargaDoModulo> = {
  nome: "criar_modulo",
  titulo: "Criar um módulo",
  descricao: "Cria um módulo no fim de um conteúdo em vídeo (até 100), com nome, descrição, quando abre e a capa (o pôster do módulo na área de membros).",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    titulo: { tipo: "texto", descricao: "Nome do módulo.", max: 160 },
    descricao: { tipo: "texto", descricao: "Descrição curta.", max: 1000 },
    ...CAMPOS_DE_QUANDO_O_MODULO_ABRE,
    capa: { tipo: "texto", descricao: `Capa do módulo. ${DESCRICAO_DO_CAMPO_DE_IMAGEM}`, max: 2000 },
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido } = await lerModulo(ctx, contexto, args);
    semContatoNovo("", `${args.titulo ?? ""}\n${args.descricao ?? ""}`, "no editor do conteúdo em vídeo");
    const capa = args.capa ? await prepararImagemDigital(ctx, args.capa, "livre") : null;
    const corpo = { titulo: args.titulo, descricao: args.descricao, ...quandoAbre(args, "imediata") };
    return {
      previa: { conteudo: lido.produto.nome, modulo_novo: args.titulo ?? `Módulo ${lido.modulos.length + 1}`, ...(capa ? { capa: capa.origem } : {}) },
      resumo: `Prévia de módulo novo em ${lido.produto.nome}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, moduloId: null, corpo, capa, nome: lido.produto.nome },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const pedido = pedidoDoAssistente(ctx);
    const feito = await naRota(() => criarModulo(contexto, carga.produtoId, carga.corpo, pedido));
    if (carga.capa) {
      const capa = await guardarImagemDigital(ctx, carga.capa, "digitais/modulos");
      await naRota(() => salvarModulo(contexto, carga.produtoId, feito.id, { capa }, pedido));
    }
    return { dados: { modulo_id: feito.id, conteudo: carga.nome }, resumo: `Criou um módulo em ${carga.nome}` };
  },
};

export const alterarModulo: FerramentaDeEscrita<CargaDoModulo> = {
  nome: "alterar_modulo",
  titulo: "Mudar um módulo",
  descricao: "Muda o nome, a descrição, quando o módulo abre ou a capa dele. Mande só o que muda.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    modulo_id: { tipo: "id", descricao: "Id do módulo.", obrigatorio: true },
    titulo: { tipo: "texto", descricao: "Novo nome.", max: 160 },
    descricao: { tipo: "texto", descricao: "Nova descrição.", max: 1000 },
    ...CAMPOS_DE_QUANDO_O_MODULO_ABRE,
    capa: { tipo: "texto", descricao: `Nova capa. ${DESCRICAO_DO_CAMPO_DE_IMAGEM}`, max: 2000 },
    tirar_capa: { tipo: "booleano", descricao: "true = sem foto (capa feita com a cor e o título)." },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido, modulo } = await lerModulo(ctx, contexto, args);
    const corpo: Record<string, unknown> = { ...quandoAbre(args, "imediata") };
    if (args.titulo !== undefined) corpo.titulo = args.titulo;
    if (args.descricao !== undefined) corpo.descricao = args.descricao;
    if (args.tirar_capa === true) corpo.capa = "";
    semContatoNovo(`${modulo!.titulo}\n${modulo!.descricao ?? ""}`, `${args.titulo ?? modulo!.titulo}\n${args.descricao ?? modulo!.descricao ?? ""}`, "no editor do conteúdo em vídeo");
    const capa = args.capa ? await prepararImagemDigital(ctx, args.capa, "livre") : null;
    if (!Object.keys(corpo).length && !capa) recusar("Nada para mudar.");
    return {
      previa: { modulo: modulo!.titulo, mudancas: corpo, ...(capa ? { capa: capa.origem } : {}) },
      resumo: `Prévia da mudança no módulo ${modulo!.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, moduloId: modulo!.id, corpo, capa, nome: modulo!.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const corpo = { ...carga.corpo };
    if (carga.capa) corpo.capa = await guardarImagemDigital(ctx, carga.capa, "digitais/modulos");
    await naRota(() => salvarModulo(contexto, carga.produtoId, carga.moduloId!, corpo, pedidoDoAssistente(ctx)));
    return { dados: { modulo_id: carga.moduloId, alterado: true }, resumo: `Mudou o módulo ${carga.nome}` };
  },
};

export const apagarModuloFerramenta: FerramentaDeEscrita<CargaDoModulo> = {
  nome: "apagar_modulo",
  titulo: "Apagar um módulo",
  descricao: "Apaga o módulo e os vídeos dele da área de membros (o progresso e os comentários dos clientes ficam guardados; os arquivos continuam na biblioteca). Sempre pede confirmação.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    modulo_id: { tipo: "id", descricao: "Id do módulo.", obrigatorio: true },
  },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido, modulo } = await lerModulo(ctx, contexto, args);
    return {
      previa: { conteudo: lido.produto.nome, apagar_modulo: modulo!.titulo, videos_que_saem: modulo!.aulas.length },
      resumo: `Prévia de apagar o módulo ${modulo!.titulo}`,
      avisos: modulo!.aulas.length ? [`Os ${modulo!.aulas.length} vídeos do módulo somem da área de membros.`] : [],
      carga: { produtoId: lido.produto.id, moduloId: modulo!.id, corpo: {}, capa: null, nome: modulo!.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => apagarModulo(contexto, carga.produtoId, carga.moduloId!, pedidoDoAssistente(ctx)));
    return { dados: { apagado: carga.nome }, resumo: `Apagou o módulo ${carga.nome}` };
  },
};

type CargaDaOrdem = { produtoId: string; moduloId: string | null; ids: string[]; nome: string };

export const ordenarModulosFerramenta: FerramentaDeEscrita<CargaDaOrdem> = {
  nome: "ordenar_modulos",
  titulo: "Mudar a ordem dos módulos",
  descricao: "Põe os módulos de um conteúdo em vídeo na ordem da lista (mande todos os modulo_id, do primeiro ao último).",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    modulo_ids: { tipo: "textos", descricao: "Os modulo_id na ordem nova.", obrigatorio: true, minItens: 1, maxItens: 100, max: 40 },
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido } = await lerModulo(ctx, contexto, args);
    const ids = args.modulo_ids as string[];
    const existentes = new Set(lido.modulos.map((m) => m.id));
    if (ids.some((id) => !existentes.has(id))) recusar("Há modulo_id que não é deste conteúdo. Veja ver_conteudo_em_video.");
    if (ids.length !== existentes.size) recusar(`Mande todos os ${existentes.size} módulos, na ordem nova.`);
    return {
      previa: { ordem: ids.map((id) => lido.modulos.find((m) => m.id === id)?.titulo) },
      resumo: `Prévia da ordem dos módulos de ${lido.produto.nome}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, moduloId: null, ids, nome: lido.produto.nome },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => ordenarModulos(contexto, carga.produtoId, { ids: carga.ids }, pedidoDoAssistente(ctx)));
    return { dados: { ordenado: true }, resumo: `Reordenou os módulos de ${carga.nome}` };
  },
};

/* ------------------------------------------------------------ vídeos */

const CAMPOS_DO_VIDEO: Campos = {
  titulo: { tipo: "texto", descricao: "Nome do vídeo.", max: 160 },
  tipo: { tipo: "opcao", descricao: "Tipo de conteúdo: video, texto, so_materiais ou ao_vivo.", opcoes: ["video", "texto", "so_materiais", "ao_vivo"] },
  arquivo_de_video: {
    tipo: "texto",
    descricao: "Um vídeo da biblioteca: o id (de listar_arquivos_digitais) ou o código \"envio:...\" de pedir_video. A duração vem do arquivo.",
    max: 200,
  },
  link_do_video: { tipo: "texto", descricao: "Ou um link do YouTube, Vimeo ou Panda Video.", max: 2000 },
  tirar_video: { tipo: "booleano", descricao: "true = tira o vídeo (arquivo e link)." },
  duracao_min: { tipo: "numero", descricao: "Duração em minutos (para link; o arquivo já traz a dele).", min: 0, max: 1440 },
  descricao: { tipo: "texto", descricao: "Do que o vídeo trata, em poucas linhas.", max: 4000 },
  texto: { tipo: "texto", descricao: "Tipo texto: o conteúdo (linha em branco entre parágrafos).", max: 20000 },
  ao_vivo_dia: { tipo: "data", descricao: "Tipo ao_vivo: o dia da transmissão." },
  ao_vivo_hora: { tipo: "hora", descricao: "Tipo ao_vivo: a hora (Brasília)." },
  ao_vivo_link: { tipo: "texto", descricao: "Tipo ao_vivo: o link da transmissão (https://).", max: 2000 },
  legenda: { tipo: "texto", descricao: "A legenda .vtt ou .srt da biblioteca (id ou \"envio:...\" de pedir_arquivo_digital). O cliente liga pelo botão CC.", max: 200 },
  tirar_legenda: { tipo: "booleano", descricao: "true = tira a legenda." },
  capitulos: { tipo: "texto", descricao: "Capítulos da linha do tempo, um por linha: o tempo e o nome (ex.: \"00:00 Abertura\"). Vazio apaga.", max: 8000 },
  previa_gratis: { tipo: "booleano", descricao: "true = quem ainda não comprou pode assistir este vídeo na página de venda." },
  comentarios: { tipo: "booleano", descricao: "Comentários neste vídeo (os clientes tiram dúvidas embaixo)." },
  publicado: { tipo: "booleano", descricao: "false = rascunho (só a equipe vê)." },
  abre: { tipo: "opcao", descricao: "Quando este vídeo abre: junto com o módulo, alguns dias depois da compra ou numa data.", opcoes: ["junto_com_o_modulo", "dias_depois", "numa_data"] },
  abre_em_dias: { tipo: "inteiro", descricao: "Com abre = dias_depois: quantos dias.", min: 1, max: 3650 },
  abre_no_dia: { tipo: "data", descricao: "Com abre = numa_data: o dia." },
};

type CargaDoVideo = { produtoId: string; videoId: string | null; moduloId: string | null; corpo: Record<string, unknown>; nome: string };

async function corpoDoVideo(ctx: ContextoDoAssistente, contexto: ContextoDigital, args: Record<string, unknown>, atual: { titulo: string; descricao: string | null; texto: string | null } | null) {
  const corpo: Record<string, unknown> = {};
  if (args.titulo !== undefined) corpo.titulo = args.titulo;
  if (args.tipo !== undefined) corpo.tipo = TIPO_DO_VIDEO[String(args.tipo)];
  if (args.tirar_video === true) {
    corpo.videoArquivoId = null;
    corpo.videoLink = null;
  } else if (args.arquivo_de_video !== undefined) {
    const arquivo = await arquivoIndicado(ctx, contexto, args.arquivo_de_video);
    if (arquivo.categoria !== "video") recusar("Esse arquivo não é um vídeo. Use MP4, WebM ou MOV.");
    corpo.videoArquivoId = arquivo.id;
  } else if (args.link_do_video !== undefined) {
    corpo.videoLink = args.link_do_video;
  }
  if (args.duracao_min !== undefined) corpo.duracaoS = Math.round(Number(args.duracao_min) * 60);
  if (args.descricao !== undefined) corpo.descricao = args.descricao;
  if (args.texto !== undefined) corpo.texto = args.texto;
  if (args.ao_vivo_dia !== undefined) {
    const quando = instanteDeBrasilia(String(args.ao_vivo_dia), (args.ao_vivo_hora as string | undefined) ?? "00:00");
    if (!quando) recusar("Confira o dia e a hora da transmissão ao vivo.");
    corpo.aoVivoEm = quando;
  }
  if (args.ao_vivo_link !== undefined) corpo.aoVivoLink = args.ao_vivo_link;
  if (args.tirar_legenda === true) corpo.legendaArquivoId = null;
  else if (args.legenda !== undefined) corpo.legendaArquivoId = (await arquivoIndicado(ctx, contexto, args.legenda)).id;
  if (args.capitulos !== undefined) corpo.capitulos = String(args.capitulos);
  if (args.previa_gratis !== undefined) corpo.previaGratis = args.previa_gratis;
  if (args.comentarios !== undefined) corpo.comentarios = args.comentarios;
  if (args.publicado !== undefined) corpo.publicada = args.publicado;
  if (args.abre !== undefined) {
    const liberacao = args.abre === "dias_depois" ? "dias" : args.abre === "numa_data" ? "data" : "modulo";
    if (liberacao === "dias" && !args.abre_em_dias) recusar("Diga em abre_em_dias quantos dias depois da compra.");
    if (liberacao === "data" && !args.abre_no_dia) recusar("Diga em abre_no_dia o dia em que abre.");
    Object.assign(corpo, { liberacao, liberarDias: args.abre_em_dias, liberarEm: liberacao === "data" ? instanteDeBrasilia(String(args.abre_no_dia)) : undefined });
  }
  semContatoNovo(
    atual ? `${atual.titulo}\n${atual.descricao ?? ""}\n${atual.texto ?? ""}` : "",
    `${args.titulo ?? atual?.titulo ?? ""}\n${args.descricao ?? atual?.descricao ?? ""}\n${args.texto ?? atual?.texto ?? ""}`,
    "no editor do conteúdo em vídeo"
  );
  return corpo;
}

function acharVideo(lido: CursoLido, videoId: unknown) {
  for (const modulo of lido.modulos) {
    const video = modulo.aulas.find((a) => a.id === videoId);
    if (video) return { modulo, video };
  }
  recusar("Vídeo não encontrado neste conteúdo. Veja os video_id em ver_conteudo_em_video.");
}

export const adicionarVideoAoConteudo: FerramentaDeEscrita<CargaDoVideo> = {
  nome: "adicionar_video_ao_conteudo",
  titulo: "Acrescentar um vídeo a um módulo",
  descricao:
    "Acrescenta um vídeo no fim de um módulo (até 500 por conteúdo). O tipo pode ser vídeo, texto, só materiais ou ao vivo. Já dá para mandar tudo junto: o arquivo (\"envio:...\" de pedir_video ou o id da biblioteca) ou o link do YouTube/Vimeo/Panda, descrição, prévia grátis, quando abre, legenda, capítulos.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    modulo_id: { tipo: "id", descricao: "Id do módulo.", obrigatorio: true },
    ...CAMPOS_DO_VIDEO,
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido, modulo } = await lerModulo(ctx, contexto, args);
    const corpo = await corpoDoVideo(ctx, contexto, args, null);
    return {
      previa: { conteudo: lido.produto.nome, modulo: modulo!.titulo, video_novo: args.titulo ?? `Vídeo ${modulo!.aulas.length + 1}`, campos: Object.keys(corpo) },
      resumo: `Prévia de vídeo novo em ${lido.produto.nome}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: null, moduloId: modulo!.id, corpo, nome: lido.produto.nome },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const pedido = pedidoDoAssistente(ctx);
    const feito = await naRota(() =>
      criarAula(contexto, carga.produtoId, { moduloId: carga.moduloId, titulo: carga.corpo.titulo, tipo: carga.corpo.tipo }, pedido)
    );
    const resto = { ...carga.corpo };
    delete resto.titulo;
    delete resto.tipo;
    let aviso: string | null = null;
    if (Object.keys(resto).length) {
      try {
        await naRota(() => salvarAula(contexto, carga.produtoId, feito.id, resto, pedido));
      } catch (erro) {
        aviso = `O vídeo foi criado, mas parte dele não foi salva: ${erro instanceof Error ? erro.message : "erro"}. Corrija com alterar_video_do_conteudo.`;
      }
    }
    return { dados: { video_id: feito.id, ...(aviso ? { aviso } : {}) }, resumo: `Acrescentou um vídeo em ${carga.nome}` };
  },
};

export const alterarVideoDoConteudo: FerramentaDeEscrita<CargaDoVideo> = {
  nome: "alterar_video_do_conteudo",
  titulo: "Mudar um vídeo do conteúdo",
  descricao:
    "Muda um vídeo de um módulo em qualquer detalhe do editor: nome, tipo, o arquivo de vídeo (\"envio:...\" de pedir_video ou id da biblioteca) ou o link, duração, descrição, texto, transmissão ao vivo, legenda, capítulos, prévia grátis, comentários, publicado/rascunho e quando abre. Mande só o que muda.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    video_id: { tipo: "id", descricao: "Id do vídeo (de ver_conteudo_em_video).", obrigatorio: true },
    ...CAMPOS_DO_VIDEO,
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    const { video } = acharVideo(lido, args.video_id);
    const corpo = await corpoDoVideo(ctx, contexto, args, video);
    if (!Object.keys(corpo).length) recusar("Nada para mudar.");
    return {
      previa: { video: video.titulo, campos: Object.keys(corpo) },
      resumo: `Prévia da mudança no vídeo ${video.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: video.id, moduloId: null, corpo, nome: video.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => salvarAula(contexto, carga.produtoId, carga.videoId!, carga.corpo, pedidoDoAssistente(ctx)));
    return { dados: { video_id: carga.videoId, alterado: true }, resumo: `Mudou o vídeo ${carga.nome}` };
  },
};

export const usarVideoEnviado: FerramentaDeEscrita<CargaDoVideo> = {
  nome: "usar_video_enviado",
  titulo: "Pôr o vídeo enviado num conteúdo",
  descricao:
    "Liga o vídeo que a pessoa mandou pelo link de pedir_video a um vídeo de um conteúdo (a duração vem do arquivo). Sem video_id, cria um vídeo novo no fim do módulo_id com esse arquivo.",
  campos: {
    codigo: { tipo: "texto", descricao: "O código \"envio:...\" devolvido por pedir_video.", obrigatorio: true, max: 200 },
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    video_id: { tipo: "id", descricao: "O vídeo que recebe o arquivo." },
    modulo_id: { tipo: "id", descricao: "Sem video_id: o módulo onde nasce um vídeo novo." },
    titulo: { tipo: "texto", descricao: "Nome do vídeo (para vídeo novo; padrão: o nome do arquivo).", max: 160 },
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    const arquivo = await arquivoIndicado(ctx, contexto, args.codigo);
    if (arquivo.categoria !== "video") recusar("O arquivo enviado não é um vídeo.");
    if (args.video_id) {
      const { video } = acharVideo(lido, args.video_id);
      return {
        previa: { video: video.titulo, arquivo: arquivo.nome },
        resumo: `Prévia de pôr ${arquivo.nome} no vídeo ${video.titulo}`,
        avisos: [],
        carga: { produtoId: lido.produto.id, videoId: video.id, moduloId: null, corpo: { videoArquivoId: arquivo.id }, nome: video.titulo },
      };
    }
    if (!args.modulo_id) recusar("Diga o video_id que recebe o arquivo, ou o modulo_id para criar um vídeo novo.");
    const modulo = lido.modulos.find((m) => m.id === args.modulo_id);
    if (!modulo) recusar("Módulo não encontrado neste conteúdo.");
    const titulo = (args.titulo as string | undefined) ?? arquivo.nome.replace(/\.[a-z0-9]{1,5}$/i, "").slice(0, 160);
    semContatoNovo("", titulo, "no editor do conteúdo em vídeo");
    return {
      previa: { modulo: modulo.titulo, video_novo: titulo, arquivo: arquivo.nome },
      resumo: `Prévia de vídeo novo com ${arquivo.nome}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: null, moduloId: modulo.id, corpo: { titulo, videoArquivoId: arquivo.id }, nome: titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const pedido = pedidoDoAssistente(ctx);
    let videoId = carga.videoId;
    if (!videoId) videoId = (await naRota(() => criarAula(contexto, carga.produtoId, { moduloId: carga.moduloId, titulo: carga.corpo.titulo, tipo: "video" }, pedido))).id;
    await naRota(() => salvarAula(contexto, carga.produtoId, videoId!, { videoArquivoId: carga.corpo.videoArquivoId }, pedido));
    return { dados: { video_id: videoId, arquivo_ligado: true }, resumo: `Pôs o vídeo enviado em ${carga.nome}` };
  },
};

export const apagarVideoDoConteudo: FerramentaDeEscrita<CargaDoVideo> = {
  nome: "apagar_video_do_conteudo",
  titulo: "Apagar um vídeo do conteúdo",
  descricao: "Apaga um vídeo de um módulo (some da área de membros; o progresso e os comentários ficam guardados; o arquivo continua na biblioteca). Sempre pede confirmação.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    video_id: { tipo: "id", descricao: "Id do vídeo.", obrigatorio: true },
  },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    const { modulo, video } = acharVideo(lido, args.video_id);
    return {
      previa: { conteudo: lido.produto.nome, modulo: modulo.titulo, apagar_video: video.titulo },
      resumo: `Prévia de apagar o vídeo ${video.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: video.id, moduloId: null, corpo: {}, nome: video.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => apagarAula(contexto, carga.produtoId, carga.videoId!, pedidoDoAssistente(ctx)));
    return { dados: { apagado: carga.nome }, resumo: `Apagou o vídeo ${carga.nome}` };
  },
};

export const ordenarVideos: FerramentaDeEscrita<CargaDaOrdem> = {
  nome: "ordenar_videos",
  titulo: "Mudar a ordem dos vídeos de um módulo",
  descricao: "Põe os vídeos de um módulo na ordem da lista. Um video_id de outro módulo vem para este (é assim que se muda um vídeo de módulo). Mande todos os vídeos que o módulo deve ter.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    modulo_id: { tipo: "id", descricao: "Id do módulo.", obrigatorio: true },
    video_ids: { tipo: "textos", descricao: "Os video_id na ordem nova.", obrigatorio: true, minItens: 1, maxItens: 500, max: 40 },
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const { lido, modulo } = await lerModulo(ctx, contexto, args);
    const ids = args.video_ids as string[];
    const todos = new Map(lido.modulos.flatMap((m) => m.aulas.map((a) => [a.id, a.titulo] as const)));
    if (ids.some((id) => !todos.has(id))) recusar("Há video_id que não é deste conteúdo. Veja ver_conteudo_em_video.");
    const faltando = modulo!.aulas.filter((a) => !ids.includes(a.id));
    if (faltando.length) recusar(`Faltam vídeos do módulo na lista: ${faltando.map((a) => a.titulo).join(", ")}. Para tirar um vídeo do módulo, coloque-o na lista de outro módulo.`);
    return {
      previa: { modulo: modulo!.titulo, ordem: ids.map((id) => todos.get(id)) },
      resumo: `Prévia da ordem dos vídeos de ${modulo!.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, moduloId: modulo!.id, ids, nome: modulo!.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => ordenarAulas(contexto, carga.produtoId, { moduloId: carga.moduloId, ids: carga.ids }, pedidoDoAssistente(ctx)));
    return { dados: { ordenado: true }, resumo: `Reordenou os vídeos de ${carga.nome}` };
  },
};

/* ------------------------------------------------------------ materiais */

type CargaDoMaterial = { produtoId: string; videoId: string; materialId: string | null; corpo: Record<string, unknown>; nome: string };

export const adicionarMaterialFerramenta: FerramentaDeEscrita<CargaDoMaterial> = {
  nome: "adicionar_material",
  titulo: "Pôr um material para baixar num vídeo",
  descricao: "Acrescenta um material embaixo de um vídeo (PDF, planilha, moldes...): um arquivo da biblioteca (id ou \"envio:...\" de pedir_arquivo_digital) ou um link. Até 30 por vídeo.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    video_id: { tipo: "id", descricao: "Id do vídeo.", obrigatorio: true },
    arquivo: { tipo: "texto", descricao: "Arquivo da biblioteca (id ou \"envio:...\").", max: 200 },
    link: { tipo: "texto", descricao: "Ou um link (https://).", max: 2000 },
    titulo: { tipo: "texto", descricao: "Nome do material (padrão: o nome do arquivo ou do site).", max: 160 },
  },
  tipo: "escrita",
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    const { video } = acharVideo(lido, args.video_id);
    if (!args.arquivo === !args.link) recusar("Mande o arquivo OU o link do material.");
    const corpo: Record<string, unknown> = { titulo: args.titulo };
    if (args.arquivo) corpo.arquivoId = (await arquivoIndicado(ctx, contexto, args.arquivo)).id;
    else corpo.url = args.link;
    semContatoNovo("", String(args.titulo ?? ""), "no editor do conteúdo em vídeo");
    return {
      previa: { video: video.titulo, material: args.titulo ?? (args.link ? args.link : "arquivo da biblioteca") },
      resumo: `Prévia de material novo em ${video.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: video.id, materialId: null, corpo, nome: video.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const feito = await naRota(() => adicionarMaterial(contexto, carga.produtoId, carga.videoId, carga.corpo, pedidoDoAssistente(ctx)));
    return { dados: { material_id: feito.id }, resumo: `Pôs um material no vídeo ${carga.nome}` };
  },
};

export const tirarMaterialFerramenta: FerramentaDeEscrita<CargaDoMaterial> = {
  nome: "tirar_material",
  titulo: "Tirar um material de um vídeo",
  descricao: "Tira um material de baixo de um vídeo (o arquivo continua na biblioteca). Sempre pede confirmação.",
  campos: {
    produto_id: { tipo: "id", descricao: "Id do conteúdo em vídeo.", obrigatorio: true },
    video_id: { tipo: "id", descricao: "Id do vídeo.", obrigatorio: true },
    material_id: { tipo: "id", descricao: "Id do material (de ver_conteudo_em_video).", obrigatorio: true },
  },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: ["digital.products.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    const lido = await lerConteudo(ctx, contexto, String(args.produto_id));
    const { video } = acharVideo(lido, args.video_id);
    const material = video.materiais.find((m) => m.id === args.material_id);
    if (!material) recusar("Material não encontrado neste vídeo.");
    return {
      previa: { video: video.titulo, tirar_material: material.titulo },
      resumo: `Prévia de tirar o material ${material.titulo}`,
      avisos: [],
      carga: { produtoId: lido.produto.id, videoId: video.id, materialId: material.id, corpo: {}, nome: material.titulo },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.products.write", "montar os conteúdos em vídeo");
    await naRota(() => tirarMaterial(contexto, carga.produtoId, carga.videoId, carga.materialId!, pedidoDoAssistente(ctx)));
    return { dados: { tirado: carga.nome }, resumo: `Tirou o material ${carga.nome}` };
  },
};

export const FERRAMENTAS_DO_CONTEUDO_EM_VIDEO: Ferramenta[] = [
  listarConteudosEmVideo,
  verConteudoEmVideo,
  linkParaVerComoCliente,
  criarModuloFerramenta,
  alterarModulo,
  apagarModuloFerramenta,
  ordenarModulosFerramenta,
  adicionarVideoAoConteudo,
  alterarVideoDoConteudo,
  usarVideoEnviado,
  apagarVideoDoConteudo,
  ordenarVideos,
  adicionarMaterialFerramenta,
  tirarMaterialFerramenta,
] as Ferramenta[];
