"use client";

import { useCallback, useEffect, useRef, useState } from "react";

/*
 * A parte que recebe o arquivo: área para soltar, botão "Escolher imagem"
 * (no celular abre a galeria ou a câmera), colar com Ctrl+V, miniatura e
 * barra de progresso. A conferência de verdade é no servidor; aqui só se
 * poupa a pessoa de esperar um envio que já se sabe que vai ser recusado.
 */

const LIMITE_EM_BYTES = 15 * 1024 * 1024;

type Recebido = { nome: string | null; largura: number | null; altura: number | null } | null;

type Estado =
  | { fase: "parado" }
  | { fase: "enviando"; progresso: number }
  | { fase: "pronto"; largura: number; altura: number }
  | { fase: "erro"; mensagem: string };

function kb(bytes: number) {
  return bytes >= 1024 * 1024 ? `${(bytes / (1024 * 1024)).toLocaleString("pt-BR", { maximumFractionDigits: 1 })} MB` : `${Math.max(1, Math.round(bytes / 1024))} KB`;
}

export function EnvioDeImagem({ codigo, loja, paraQue, recebido }: { codigo: string; loja: string | null; paraQue: string | null; recebido: Recebido }) {
  const entrada = useRef<HTMLInputElement>(null);
  /* O arquivo escolhido e a miniatura dele (um endereço temporário do navegador). */
  const [escolha, setEscolha] = useState<{ arquivo: File; miniatura: string } | null>(null);
  const arquivo = escolha?.arquivo ?? null;
  const miniatura = escolha?.miniatura ?? null;
  const [estado, setEstado] = useState<Estado>({ fase: "parado" });
  const [arrastando, setArrastando] = useState(false);
  const enviando = estado.fase === "enviando";

  /* A miniatura antiga sai da memória quando troca de arquivo ou fecha a página. */
  useEffect(() => {
    if (!miniatura) return;
    return () => URL.revokeObjectURL(miniatura);
  }, [miniatura]);

  const enviar = useCallback(
    (escolhido: File) => {
      if (enviando) return;
      setEscolha({ arquivo: escolhido, miniatura: URL.createObjectURL(escolhido) });
      if (escolhido.type === "image/svg+xml" || /\.svg$/i.test(escolhido.name)) {
        setEstado({ fase: "erro", mensagem: "Imagem em SVG não é aceita. Envie PNG, JPG ou WebP." });
        return;
      }
      if (escolhido.type && !escolhido.type.startsWith("image/")) {
        setEstado({ fase: "erro", mensagem: "Este arquivo não é uma imagem. Envie PNG, JPG ou WebP." });
        return;
      }
      if (escolhido.size > LIMITE_EM_BYTES) {
        setEstado({ fase: "erro", mensagem: `A imagem tem ${kb(escolhido.size)} e o limite é 15 MB. Escolha uma menor.` });
        return;
      }
      setEstado({ fase: "enviando", progresso: 0 });
      const formulario = new FormData();
      formulario.append("arquivo", escolhido, escolhido.name || "imagem");
      /* XMLHttpRequest e não fetch: só ele conta o progresso do envio. */
      const pedido = new XMLHttpRequest();
      pedido.open("POST", `/api/enviar/${encodeURIComponent(codigo)}`);
      pedido.responseType = "json";
      pedido.upload.onprogress = (evento) => {
        if (evento.lengthComputable) setEstado({ fase: "enviando", progresso: Math.round((evento.loaded / evento.total) * 100) });
      };
      pedido.onload = () => {
        const corpo = (pedido.response ?? {}) as { ok?: boolean; erro?: string; largura?: number; altura?: number };
        if (pedido.status >= 200 && pedido.status < 300 && corpo.ok) {
          setEstado({ fase: "pronto", largura: corpo.largura ?? 0, altura: corpo.altura ?? 0 });
        } else {
          setEstado({ fase: "erro", mensagem: corpo.erro || "Não deu para enviar a imagem. Tente de novo." });
        }
      };
      pedido.onerror = () => setEstado({ fase: "erro", mensagem: "A conexão caiu no meio do envio. Confira a internet e tente de novo." });
      pedido.send(formulario);
    },
    [codigo, enviando]
  );

  /* Colar uma imagem copiada (print, imagem de outro site) em qualquer ponto da página. */
  useEffect(() => {
    function aoColar(evento: ClipboardEvent) {
      const item = Array.from(evento.clipboardData?.items ?? []).find((i) => i.kind === "file" && i.type.startsWith("image/"));
      const colado = item?.getAsFile();
      if (!colado) return;
      evento.preventDefault();
      const extensao = colado.type.split("/")[1]?.replace("jpeg", "jpg") || "png";
      enviar(colado.name && colado.name !== "image.png" ? colado : new File([colado], `imagem-colada.${extensao}`, { type: colado.type }));
    }
    document.addEventListener("paste", aoColar);
    return () => document.removeEventListener("paste", aoColar);
  }, [enviar]);

  function abrirEscolha() {
    if (!enviando) entrada.current?.click();
  }

  const titulo = paraQue ? `Envie a imagem (${paraQue})` : "Envie a imagem";

  return (
    <section>
      <h1 className="envio-titulo">{titulo}</h1>
      <p className="envio-sub">
        {loja ? (
          <>
            Enviando imagem para <strong>{loja}</strong>.{" "}
          </>
        ) : null}
        Escolha o arquivo no celular ou no computador. Depois, volte para a conversa com o assistente.
      </p>

      {recebido && estado.fase !== "pronto" ? (
        <p className="envio-ja">
          Uma imagem já foi enviada por este link{recebido.nome ? ` (${recebido.nome}` : ""}
          {recebido.nome && recebido.largura && recebido.altura ? `, ${recebido.largura} × ${recebido.altura}` : ""}
          {recebido.nome ? ")" : ""}. Se enviar outra, ela substitui a anterior.
        </p>
      ) : null}

      <div
        className="envio-alvo"
        role="button"
        tabIndex={0}
        aria-label="Escolher a imagem para enviar"
        data-arrastando={arrastando ? "sim" : "nao"}
        onClick={abrirEscolha}
        onKeyDown={(evento) => {
          if (evento.key === "Enter" || evento.key === " ") {
            evento.preventDefault();
            abrirEscolha();
          }
        }}
        onDragOver={(evento) => {
          evento.preventDefault();
          setArrastando(true);
        }}
        onDragLeave={() => setArrastando(false)}
        onDrop={(evento) => {
          evento.preventDefault();
          setArrastando(false);
          const solto = evento.dataTransfer.files?.[0];
          if (solto) enviar(solto);
        }}
      >
        <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <rect x="3" y="3" width="18" height="18" rx="3" />
          <circle cx="9" cy="9" r="2" />
          <path d="m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21" />
        </svg>
        <button type="button" className="envio-botao" disabled={enviando} onClick={(evento) => { evento.stopPropagation(); abrirEscolha(); }}>
          {estado.fase === "pronto" || recebido ? "Escolher outra imagem" : "Escolher imagem"}
        </button>
        <span className="envio-alvo-texto">
          ou <strong>arraste a imagem para cá</strong>, ou cole com Ctrl+V
        </span>
        <input
          ref={entrada}
          className="envio-escondido"
          type="file"
          accept="image/*"
          tabIndex={-1}
          onChange={(evento) => {
            const escolhido = evento.target.files?.[0];
            evento.target.value = "";
            if (escolhido) enviar(escolhido);
          }}
        />
      </div>

      {arquivo && miniatura ? (
        <div className="envio-previa">
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img src={miniatura} alt="Miniatura da imagem escolhida" />
          <div className="envio-previa-info">
            <div className="envio-previa-nome">{arquivo.name || "imagem"}</div>
            <div className="envio-previa-detalhe">
              {kb(arquivo.size)}
              {estado.fase === "pronto" && estado.largura ? ` · ${estado.largura} × ${estado.altura} pixels` : ""}
              {estado.fase === "enviando" ? ` · enviando ${estado.progresso}%` : ""}
            </div>
            {estado.fase === "enviando" ? (
              <div className="envio-barra" role="progressbar" aria-valuemin={0} aria-valuemax={100} aria-valuenow={estado.progresso}>
                <span style={{ width: `${estado.progresso}%` }} />
              </div>
            ) : null}
          </div>
        </div>
      ) : null}

      {estado.fase === "pronto" ? (
        <div className="envio-mensagem" data-tipo="ok" role="status">
          <strong>Pronto!</strong>
          Volte para a conversa e diga &quot;enviei&quot;.
        </div>
      ) : null}
      {estado.fase === "erro" ? (
        <div className="envio-mensagem" data-tipo="erro" role="alert">
          {estado.mensagem}
        </div>
      ) : null}

      <p className="envio-dica">PNG, JPG ou WebP, até 15 MB. Para logo, o melhor é PNG com fundo transparente. Este link vale 24 horas.</p>
    </section>
  );
}
