import { apagarArquivoDaBiblioteca, listarArquivos } from "@/lib/digitais/servidor/acoes/arquivos";
import { limitesDaLoja } from "@/lib/digitais/limites";
import { configuracaoDoR2 } from "@/lib/digitais/r2";

import { faxinarArquivosVelhos } from "../arquivos";
import { gerarCodigoDeEnvio, lerCodigoDeEnvio, VALIDADE_DO_ENVIO_MS } from "../codigo-de-envio";
import { arquivoDoCaminho, ehEnvioDigital, TIPO_DO_ENVIO_DE_ARQUIVO, TIPO_DO_ENVIO_DE_VIDEO } from "../envio-digital";
import { AVISO_DE_DADOS, falha, recusar, texto } from "./comum";
import { abrirDigital, arquivoIndicado, COMO_MOSTRAR_O_LINK, linkCurtoDoArquivo, naRota } from "./digitais-comum";
import { duracaoBr, SITUACAO_DO_ARQUIVO, tamanhoBr } from "./digitais-regras";
import type { Ferramenta, FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/*
 * "Arquivos e espaço" pelo assistente: a biblioteca (vídeos, PDFs, legendas,
 * gravações das lives), o espaço do plano, um link curto para assistir ou
 * baixar, apagar, e o envio de vídeo/arquivo NOVO pela página /enviar/<código>
 * (o assistente não recebe o anexo da conversa; ver lib/assistentes/envio-digital.ts).
 * Ver = digital.files.read; enviar e apagar = digital.files.write, como a tela.
 */

const CATEGORIAS = ["video", "pdf", "imagem", "compactado", "audio", "documento", "outro"] as const;

export const listarArquivosDigitais: FerramentaDeLeitura = {
  nome: "listar_arquivos_digitais",
  titulo: "Ver os arquivos e o espaço",
  descricao:
    "A biblioteca dos produtos digitais (Arquivos e espaço): cada arquivo com o arquivo_id, tipo, tamanho, duração (vídeos), situação (enviando, pronto, recusado) e onde é usado; e o espaço do plano (usado, total e o máximo por arquivo). As gravações das lives também ficam aqui.",
  campos: {
    categoria: { tipo: "opcao", descricao: "Filtrar: video, pdf, imagem, compactado, audio, documento, outro ou todos.", opcoes: [...CATEGORIAS, "todos"] },
    busca: { tipo: "texto", descricao: "Parte do nome do arquivo.", max: 80 },
    sem_uso: { tipo: "booleano", descricao: "true = só os que não estão em produto nenhum." },
  },
  tipo: "leitura",
  permissoes: ["digital.files.read"],
  async executar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.files.read", "ver os arquivos dos produtos digitais");
    const lido = await naRota(() => listarArquivos(contexto));
    const busca = String(args.busca ?? "").trim().toLowerCase();
    const arquivos = lido.arquivos
      .filter((a) => !args.categoria || args.categoria === "todos" || a.categoria === args.categoria)
      .filter((a) => !busca || a.nome.toLowerCase().includes(busca))
      .filter((a) => args.sem_uso !== true || a.usadoEm.length === 0)
      .slice(0, 200)
      .map((a) => ({
        arquivo_id: a.id,
        nome: a.nome,
        tipo: a.categoria,
        formato: a.extensao,
        tamanho: a.tamanhoLegivel,
        duracao: a.duracao ? duracaoBr(a.duracao) : null,
        paginas: a.paginas,
        situacao: SITUACAO_DO_ARQUIVO[a.status] ?? a.status,
        motivo_da_recusa: a.motivo,
        enviado_em: a.criadoEm,
        usado_em: a.usadoEm,
      }));
    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        espaco: {
          usado: tamanhoBr(lido.espaco.usado),
          total: tamanhoBr(lido.espaco.total),
          maximo_por_arquivo: tamanhoBr(lido.espaco.porArquivo),
          arquivos: lido.espaco.arquivos,
        },
        deposito_ligado: lido.deposito,
        arquivos,
        total: arquivos.length,
        dica: "Para assistir ou baixar, use link_do_arquivo_digital com o arquivo_id.",
      },
      resumo: `Consultou os arquivos dos produtos digitais (${arquivos.length})`,
    };
  },
};

export const linkDoArquivoDigital: FerramentaDeLeitura = {
  nome: "link_do_arquivo_digital",
  titulo: "Link para assistir ou baixar um arquivo",
  descricao:
    "Gera um link curto (vale 24 horas) para a pessoa assistir no navegador ou baixar um arquivo da biblioteca — um vídeo, um PDF ou a gravação de uma live. Mostre o link à pessoa exatamente como veio.",
  campos: { arquivo_id: { tipo: "texto", descricao: "O arquivo_id (de listar_arquivos_digitais ou listar_gravacoes) ou o código \"envio:...\".", obrigatorio: true, max: 200 } },
  tipo: "leitura",
  permissoes: ["digital.files.read"],
  async executar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.files.read", "baixar os arquivos dos produtos digitais");
    if (!configuracaoDoR2()) recusar("O depósito de arquivos não está ligado neste servidor agora.");
    const arquivo = await arquivoIndicado(ctx, contexto, args.arquivo_id);
    if (arquivo.status !== "pronto") recusar(`Este arquivo ainda não está pronto (situação: ${SITUACAO_DO_ARQUIVO[arquivo.status] ?? arquivo.status}).`);
    const links = await linkCurtoDoArquivo(ctx, arquivo);
    return {
      dados: {
        arquivo: arquivo.nome,
        link_para_baixar: links.baixar,
        ...(arquivo.categoria === "video" || arquivo.categoria === "pdf" || arquivo.categoria === "audio" || arquivo.categoria === "imagem"
          ? { link_para_assistir: links.assistir }
          : {}),
        vale_ate: links.vale_ate,
        proximo_passo: COMO_MOSTRAR_O_LINK,
      },
      resumo: `Gerou o link do arquivo ${arquivo.nome}`,
    };
  },
};

/* ------------------------------------------------------------ envio novo */

/* Trinta links por hora, por empresa, somados aos de imagem: uma conversa normal pede um ou dois. */
const ENVIOS_POR_HORA = 30;

async function pedirEnvio(ctx: Parameters<FerramentaDeLeitura["executar"]>[0], args: Record<string, unknown>, tipo: string) {
  if (!ctx.chave.escopos.includes("escrita")) {
    recusar("Esta conexão só pode consultar. O arquivo enviado só serve para mudar os produtos digitais, e isso pede uma conexão que possa alterar (em Assistentes e Apps, no painel).");
  }
  const contexto = await abrirDigital(ctx, "digital.files.write", "enviar arquivos dos produtos digitais");
  if (!configuracaoDoR2()) recusar("O envio de arquivos ainda está sendo ligado neste servidor. Tente mais tarde.");
  const empresa = ctx.chave.companyId;
  const agora = Date.now();
  const { count, error: erroDaConta } = await ctx.admin
    .from("assistentes_envios")
    .select("codigo", { count: "exact", head: true })
    .eq("company_id", empresa)
    .gte("criado_em", new Date(agora - 60 * 60 * 1000).toISOString());
  if (erroDaConta) falha("preparar o link de envio", erroDaConta);
  if ((count ?? 0) >= ENVIOS_POR_HORA) recusar("Já foram gerados muitos links de envio na última hora. Use um link que a pessoa já recebeu (vale 24 horas) ou espere um pouco.");

  const paraQue = texto(args.para_que, 80);
  const valeAte = new Date(agora + VALIDADE_DO_ENVIO_MS);
  let codigo = "";
  for (let tentativa = 0; tentativa < 3; tentativa++) {
    const sorteado = gerarCodigoDeEnvio();
    const { error } = await ctx.admin.from("assistentes_envios").insert({
      codigo: sorteado,
      company_id: empresa,
      usuario_id: ctx.chave.usuarioId,
      origem: "pessoa",
      para_que: paraQue,
      tipo,
      vence_em: valeAte.toISOString(),
    });
    if (!error) {
      codigo = sorteado;
      break;
    }
    if ((error as { code?: string }).code !== "23505") falha("preparar o link de envio", error);
  }
  if (!codigo) recusar("Não deu para preparar o link de envio agora. Tente de novo em instantes.");
  void faxinarArquivosVelhos(ctx, new Date(agora)).catch((erro) => console.error("[assistentes] faxina dos envios", erro instanceof Error ? erro.message : erro));

  const limites = limitesDaLoja(contexto.situacao);
  const link = `${ctx.origemDoAdmin.replace(/\/$/, "")}/enviar/${codigo}`;
  const coisa = tipo === TIPO_DO_ENVIO_DE_VIDEO ? "o vídeo" : "o arquivo";
  return {
    dados: {
      link,
      codigo_para_usar: `envio:${codigo}`,
      vale_ate: valeAte.toISOString(),
      limite_por_arquivo: tamanhoBr(limites.porArquivo),
      proximo_passo: `Mostre este link à pessoa exatamente como veio, letra por letra: ${link} . Peça para ela abrir, escolher ${coisa}, deixar a página aberta até aparecer "Pronto!" e avisar. Confira com ver_envio_digital e depois use "envio:${codigo}" no campo de arquivo da ferramenta (alterar_video_do_conteudo, usar_video_enviado, adicionar_material, adicionar_item_ao_produto...). O arquivo também fica em Arquivos e espaço.`,
    },
    resumo: paraQue ? `Pediu ${coisa === "o vídeo" ? "um vídeo" : "um arquivo"} (${paraQue})` : `Pediu ${coisa === "o vídeo" ? "um vídeo" : "um arquivo"}`,
  };
}

export const pedirVideo: FerramentaDeLeitura = {
  nome: "pedir_video",
  titulo: "Pedir um vídeo à pessoa",
  descricao:
    "Gera um link para a pessoa enviar um vídeo (MP4, MOV, M4V ou WEBM, até o limite do plano) do celular ou do computador para os produtos digitais. O assistente não recebe o vídeo anexado na conversa: use sempre este link. O vídeo vai direto para Arquivos e espaço. O link vale 24 horas.",
  campos: { para_que: { tipo: "texto", descricao: "Para que é o vídeo (ex.: \"vídeo 3 do módulo Preço\"). Aparece na página de envio.", max: 80 } },
  tipo: "leitura",
  permissoes: ["digital.files.write"],
  executar: (ctx, args) => pedirEnvio(ctx, args, TIPO_DO_ENVIO_DE_VIDEO),
};

export const pedirArquivoDigital: FerramentaDeLeitura = {
  nome: "pedir_arquivo_digital",
  titulo: "Pedir um arquivo à pessoa",
  descricao:
    "Como pedir_video, para qualquer arquivo aceito nos produtos digitais: PDF, ZIP, imagem, áudio, documento, legenda (.vtt/.srt) ou vídeo. Use para material para baixar, arquivo que o comprador recebe, legenda ou o arquivo final de uma encomenda.",
  campos: { para_que: { tipo: "texto", descricao: "Para que é o arquivo. Aparece na página de envio.", max: 80 } },
  tipo: "leitura",
  permissoes: ["digital.files.write"],
  executar: (ctx, args) => pedirEnvio(ctx, args, TIPO_DO_ENVIO_DE_ARQUIVO),
};

export const verEnvioDigital: FerramentaDeLeitura = {
  nome: "ver_envio_digital",
  titulo: "Ver se o vídeo ou arquivo chegou",
  descricao: "Diz se a pessoa já enviou pelo link de pedir_video / pedir_arquivo_digital: o nome, o tamanho, a situação na biblioteca e o arquivo_id.",
  campos: { codigo: { tipo: "texto", descricao: "O código \"envio:...\".", obrigatorio: true, max: 200 } },
  tipo: "leitura",
  permissoes: ["digital.files.read"],
  async executar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.files.read", "ver os arquivos dos produtos digitais");
    const codigo = lerCodigoDeEnvio(args.codigo);
    if (!codigo) recusar('Código inválido. Use o "envio:..." devolvido por pedir_video.');
    const { data, error } = await ctx.admin
      .from("assistentes_envios")
      .select("tipo,para_que,caminho,nome,recebido_em,vence_em")
      .eq("codigo", codigo)
      .eq("company_id", ctx.chave.companyId)
      .maybeSingle();
    if (error) falha("conferir o envio", error);
    const envio = data as { tipo: string | null; para_que: string | null; caminho: string | null; nome: string | null; recebido_em: string | null; vence_em: string } | null;
    if (!envio || !ehEnvioDigital(envio.tipo)) recusar("Este código de envio não existe nesta conta (ou é de imagem: veja ver_envio_de_imagem).");
    const arquivoId = arquivoDoCaminho(envio.caminho);
    type ArquivoDoEnvio = { id: string; nome: string; status: string; tamanho_bytes: number; meta: { duracao?: number } | null; motivo: string | null };
    let arquivo = null as ArquivoDoEnvio | null;
    if (arquivoId) {
      const { data: linha } = await contexto.admin
        .from("digital_files")
        .select("id,nome,status,tamanho_bytes,meta,motivo")
        .eq("id", arquivoId)
        .eq("user_id", contexto.lojaId)
        .maybeSingle();
      arquivo = (linha as ArquivoDoEnvio | null) ?? null;
    }
    const vencido = new Date(envio.vence_em).getTime() <= Date.now();
    const pronto = Boolean(envio.recebido_em && arquivo?.status === "pronto");
    const situacao = pronto ? "chegou" : arquivo?.status === "recusado" ? "recusado" : arquivo ? "enviando" : vencido ? "vencido" : "aguardando";
    return {
      dados: {
        codigo_para_usar: `envio:${codigo}`,
        situacao,
        para_que: envio.para_que,
        ...(arquivo
          ? {
              arquivo_id: arquivo.id,
              arquivo: arquivo.nome,
              tamanho: tamanhoBr(arquivo.tamanho_bytes),
              duracao: duracaoBr(arquivo.meta?.duracao),
              motivo_da_recusa: arquivo.motivo,
            }
          : {}),
        vale_ate: envio.vence_em,
        proximo_passo: pronto
          ? `Chegou. Use "envio:${codigo}" (ou o arquivo_id) no campo de arquivo da ferramenta que precisa dele.`
          : situacao === "recusado"
            ? "O arquivo foi recusado na conferência (veja o motivo). Peça para enviar de novo pelo mesmo link."
            : vencido
              ? "O link venceu (vale 24 horas). Chame pedir_video de novo."
              : "Ainda não chegou. Peça à pessoa para deixar a página aberta até aparecer \"Pronto!\" e confira de novo.",
      },
      resumo: `Conferiu um envio de arquivo (${situacao})`,
    };
  },
};

type CargaDoArquivo = { arquivoId: string; nome: string };

export const apagarArquivoDigital: FerramentaDeEscrita<CargaDoArquivo> = {
  nome: "apagar_arquivo_digital",
  titulo: "Apagar um arquivo da biblioteca",
  descricao:
    "Cancela um envio em andamento ou apaga um arquivo da biblioteca (vídeo, PDF, gravação de live...). Arquivo que está num produto, num conteúdo em vídeo, na galeria de um produto ou que alguém comprou não sai: primeiro tire de lá. O apagado sai do depósito em 30 dias. Sempre pede confirmação.",
  campos: { arquivo_id: { tipo: "texto", descricao: "O arquivo_id (ou \"envio:...\").", obrigatorio: true, max: 200 } },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: ["digital.files.write"],
  async preparar(ctx, args) {
    const contexto = await abrirDigital(ctx, "digital.files.write", "apagar arquivos dos produtos digitais");
    const arquivo = await arquivoIndicado(ctx, contexto, args.arquivo_id);
    return {
      previa: { apagar_arquivo: arquivo.nome, situacao: SITUACAO_DO_ARQUIVO[arquivo.status] ?? arquivo.status },
      resumo: `Prévia de apagar o arquivo ${arquivo.nome}`,
      avisos: ["Depois de 30 dias o arquivo sai do depósito de vez."],
      carga: { arquivoId: arquivo.id, nome: arquivo.nome },
    };
  },
  async gravar(ctx, carga) {
    const contexto = await abrirDigital(ctx, "digital.files.write", "apagar arquivos dos produtos digitais");
    const feito = await naRota(() => apagarArquivoDaBiblioteca(contexto.admin, contexto.lojaId, carga.arquivoId));
    return { dados: { apagado: carga.nome, ...("cancelado" in feito ? { envio_cancelado: true } : {}) }, resumo: `Apagou o arquivo ${carga.nome}` };
  },
};

export const FERRAMENTAS_DE_ARQUIVOS_DIGITAIS: Ferramenta[] = [
  listarArquivosDigitais,
  linkDoArquivoDigital,
  pedirVideo,
  pedirArquivoDigital,
  verEnvioDigital,
  apagarArquivoDigital,
] as Ferramenta[];
