import { faxinarArquivosVelhos } from "../arquivos";
import { gerarCodigoDeEnvio, lerCodigoDeEnvio, VALIDADE_DO_ENVIO_MS } from "../codigo-de-envio";
import { falha, recusar, texto } from "./comum";
import type { Ferramenta, FerramentaDeLeitura } from "./tipos";

/*
 * Imagem que a pessoa anexou na conversa. O Claude e o ChatGPT não repassam o
 * arquivo ao servidor, então o caminho é um link curto no endereço do painel
 * (/enviar/<código>), onde a pessoa solta a imagem do celular ou do
 * computador. A imagem fica no depósito privado (migration 192) e volta às
 * ferramentas como "envio:<código>" (lib/assistentes/imagens.ts).
 *
 * As duas são de leitura (nada muda na loja, não pede confirmação), mas
 * `pedir_imagem` só serve a quem pode gravar: a imagem só tem uso numa
 * ferramenta de escrita.
 */

/* Trinta links por hora, no máximo, por empresa: uma conversa normal pede um
   ou dois; mais que isso é o assistente andando em círculo. */
const ENVIOS_POR_HORA = 30;

export const pedirImagem: FerramentaDeLeitura = {
  nome: "pedir_imagem",
  titulo: "Pedir uma imagem à pessoa",
  descricao:
    "Gera um link para a pessoa enviar uma imagem (logo, banner, foto) do celular ou do computador. Use sempre que a pessoa anexar uma imagem na conversa ou quiser usar um arquivo dela: o assistente não consegue repassar o anexo ao Ktalogu. Mostre o link à pessoa, peça para abrir e enviar o arquivo, e depois use o código \"envio:...\" no campo de imagem da ferramenta que precisa dela (como trocar_logo). O link vale 24 horas.",
  campos: {
    para_que: { tipo: "texto", descricao: "Para que é a imagem, em poucas palavras (ex.: \"logo\", \"banner da loja\"). Aparece na página de envio.", max: 80 },
  },
  tipo: "leitura",
  permissoes: [],
  async executar(ctx, args) {
    if (!ctx.chave.escopos.includes("escrita")) {
      recusar(
        "Esta conexão só pode consultar. A imagem enviada só serve para trocar algo na loja, e isso pede uma conexão que possa alterar (em Assistentes e Apps, no painel do Ktalogu)."
      );
    }
    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 dos links que a pessoa já recebeu (o código \"envio:...\" vale 24 horas) ou espere um pouco."
      );
    }

    const paraQue = texto(args.para_que, 80);
    const valeAte = new Date(agora + VALIDADE_DO_ENVIO_MS);
    /* Código repetido é quase impossível (56^12), mas a chave do banco diria
       "já existe": sorteia de novo, duas vezes no máximo. */
    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,
        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.");

    /* Os envios vencidos saem na mesma faxina dos arquivos (no máximo uma vez
       por hora por empresa); roda solta e nunca atrasa o link. */
    void faxinarArquivosVelhos(ctx, new Date(agora)).catch((erro) => console.error("[assistentes] faxina dos envios", erro instanceof Error ? erro.message : erro));

    const link = `${ctx.origemDoAdmin.replace(/\/$/, "")}/enviar/${codigo}`;
    return {
      dados: {
        link,
        codigo_para_usar: `envio:${codigo}`,
        vale_ate: valeAte.toISOString(),
        proximo_passo:
          `Mostre este link à pessoa exatamente como veio, letra por letra, sem encurtar nem reescrever: ${link} . Peça para ela abrir o link e enviar a imagem (pelo celular ou pelo computador) e avisar quando terminar. Depois, confira com ver_envio_de_imagem, ou use direto "envio:${codigo}" no campo de imagem da ferramenta que precisa dela. Se a pessoa enviar outra imagem pelo mesmo link, vale a última.`,
      },
      resumo: paraQue ? `Pediu uma imagem (${paraQue})` : "Pediu uma imagem",
    };
  },
};

export const verEnvioDeImagem: FerramentaDeLeitura = {
  nome: "ver_envio_de_imagem",
  titulo: "Ver se a imagem chegou",
  descricao:
    "Diz se a pessoa já enviou a imagem pelo link de pedir_imagem: nome do arquivo, tamanho em pixels e em KB. Quando chegou, use o código \"envio:...\" no campo de imagem da ferramenta que precisa dela.",
  campos: {
    codigo: { tipo: "texto", descricao: "O código devolvido por pedir_imagem (\"envio:...\").", obrigatorio: true, max: 200 },
  },
  tipo: "leitura",
  permissoes: [],
  async executar(ctx, args) {
    const codigo = lerCodigoDeEnvio(args.codigo);
    if (!codigo) recusar('Código inválido. Use o "envio:..." devolvido por pedir_imagem.');
    const { data, error } = await ctx.admin
      .from("assistentes_envios")
      .select("para_que,nome,tamanho,largura,altura,vence_em,recebido_em,caminho")
      .eq("codigo", codigo)
      .eq("company_id", ctx.chave.companyId)
      .maybeSingle();
    if (error) falha("conferir o envio da imagem", error);
    const envio = data as {
      para_que: string | null;
      nome: string | null;
      tamanho: number | null;
      largura: number | null;
      altura: number | null;
      vence_em: string;
      recebido_em: string | null;
      caminho: string | null;
    } | null;
    if (!envio) recusar("Este código de envio não existe nesta conta. Chame pedir_imagem para gerar outro link.");

    const vencido = new Date(envio.vence_em).getTime() <= Date.now();
    const recebido = Boolean(envio.recebido_em && envio.caminho);
    const situacao = vencido ? "vencido" : recebido ? "recebida" : "aguardando";
    return {
      dados: {
        codigo_para_usar: `envio:${codigo}`,
        situacao,
        recebida: recebido && !vencido,
        para_que: envio.para_que,
        ...(recebido
          ? {
              arquivo: envio.nome,
              tamanho: envio.largura && envio.altura ? `${envio.largura} × ${envio.altura} pixels` : null,
              tamanho_kb: envio.tamanho ? Math.max(1, Math.round(envio.tamanho / 1024)) : null,
              recebida_em: envio.recebido_em,
            }
          : {}),
        vale_ate: envio.vence_em,
        proximo_passo: vencido
          ? "O link venceu (vale 24 horas). Chame pedir_imagem para gerar outro."
          : recebido
            ? `A imagem chegou. Use "envio:${codigo}" no campo de imagem da ferramenta que precisa dela.`
            : "A imagem ainda não chegou. Peça à pessoa para abrir o link, enviar o arquivo e avisar quando terminar; depois confira de novo.",
      },
      resumo: `Conferiu o envio de imagem (${situacao})`,
    };
  },
};

export const FERRAMENTAS_DE_ENVIO_DE_IMAGEM: Ferramenta[] = [pedirImagem, verEnvioDeImagem];
