"use client";

import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ChevronDown, ChevronRight, ChevronUp, Eye, EyeOff, Group, Lock, LockOpen } from "lucide-react";
import { unidadesDasCamadas } from "@/lib/banner-editor/camadas";
import type { Peca } from "@/lib/banner-editor/documento";
import { htmlDaPeca } from "./desenho-guardado";
import { Amostra, fundoDaAmostra } from "./paineis";

/*
  O painel Camadas: de cima (a da frente) para baixo. Grupo é uma linha só,
  com a setinha para abrir e mexer numa peça dele; o botão direito abre o
  mesmo menu da arte (Desagrupar, frente, trás...). Arrastar uma linha muda a
  ordem (o grupo vai inteiro); soltar entre as peças de um grupo aberto põe a
  peça nele, e tirar uma peça de lá tira ela do grupo.

  Cada linha só redesenha quando a própria peça muda: arrastar na arte não
  refaz o painel inteiro.
*/

export type AlvoDaCamada = { peca?: string; grupo?: string };

export function nomeDaCamada(p: Peca) {
  return p.nome || (p.tipo === "texto" ? p.texto.split("\n")[0].slice(0, 30) || "Texto" : p.tipo === "imagem" ? (p.papel === "logo" ? "Logo" : "Imagem") : p.tipo === "textura" ? "Textura" : p.tipo === "icone" ? "Ícone" : "Forma");
}

type Lugar = "acima" | "abaixo";

/* As peças do grupo juntas, encolhidas para caber (cada uma com o desenho guardado). */
const AmostraDoGrupo = memo(
  function AmostraDoGrupo({ pecas, largura, altura }: { pecas: Peca[]; largura: number; altura: number }) {
    const visiveis = pecas.filter((p) => !p.oculta);
    const lista = visiveis.length ? visiveis : pecas;
    const x = Math.min(...lista.map((p) => p.x));
    const y = Math.min(...lista.map((p) => p.y));
    const w = Math.max(1, Math.max(...lista.map((p) => p.x + p.w)) - x);
    const h = Math.max(1, Math.max(...lista.map((p) => p.y + p.h)) - y);
    const escala = Math.min((largura - 8) / w, (altura - 8) / h);
    return (
      <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: "#F3F1F9" }}>
        <div className="absolute" style={{ left: (largura - w * escala) / 2, top: (altura - h * escala) / 2, width: w, height: h, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
          {[...lista].reverse().map((p) => (
            <div key={p.id} style={{ position: "absolute", left: p.x - x, top: p.y - y, width: p.w, height: p.h, transform: p.rot ? `rotate(${p.rot}deg)` : undefined, opacity: p.opacidade }} dangerouslySetInnerHTML={htmlDaPeca(p)} />
          ))}
        </div>
      </div>
    );
  },
  (a, b) => a.largura === b.largura && a.altura === b.altura && a.pecas.length === b.pecas.length && a.pecas.every((p, i) => p === b.pecas[i]),
);

type Acoes = {
  /* Clique: grupo = o grupo inteiro; peça de grupo = só ela. Shift soma. */
  escolher: (e: React.MouseEvent, ids: string[], sozinha: boolean) => void;
  menu: (e: React.MouseEvent, ids: string[], sozinha: boolean) => void;
  mudar: (ids: string[], campos: Partial<Peca>) => void;
  ordenar: (ids: string[], para: "subir" | "descer") => void;
  comecar: (e: React.DragEvent, ids: string[]) => void;
  passar: (e: React.DragEvent, alvo: AlvoDaCamada) => void;
  soltar: (e: React.DragEvent, alvo: AlvoDaCamada) => void;
  terminar: () => void;
  abrirGrupo: (grupo: string) => void;
  renomear: (id: string | null) => void;
};

function Botoes({ ids, ocultas, travadas, acoes }: { ids: string[]; ocultas: boolean; travadas: boolean; acoes: Acoes }) {
  return (
    <>
      <div className="flex items-center text-[#8A849E] opacity-0 transition group-hover:opacity-100">
        <button type="button" title="Subir" onClick={(e) => (e.stopPropagation(), acoes.ordenar(ids, "subir"))} className="rounded-md p-1 hover:bg-white hover:text-[#1F1B2E]">
          <ChevronUp size={14} />
        </button>
        <button type="button" title="Descer" onClick={(e) => (e.stopPropagation(), acoes.ordenar(ids, "descer"))} className="rounded-md p-1 hover:bg-white hover:text-[#1F1B2E]">
          <ChevronDown size={14} />
        </button>
        <button type="button" title={ocultas ? "Mostrar" : "Esconder"} onClick={(e) => (e.stopPropagation(), acoes.mudar(ids, { oculta: !ocultas }))} className="rounded-md p-1 hover:bg-white hover:text-[#1F1B2E]">
          {ocultas ? <EyeOff size={14} /> : <Eye size={14} />}
        </button>
      </div>
      <button type="button" title={travadas ? "Destravar" : "Travar no lugar"} onClick={(e) => (e.stopPropagation(), acoes.mudar(ids, { bloqueada: !travadas }))} className={`rounded-md p-1 ${travadas ? "text-[#6D28D9]" : "text-[#B4AEC6] opacity-0 group-hover:opacity-100"} hover:bg-white`}>
        {travadas ? <Lock size={14} /> : <LockOpen size={14} />}
      </button>
    </>
  );
}

const classeDaLinha = (escolhida: boolean, meio: boolean, arrastando: boolean, indicador: Lugar | null) =>
  `group relative flex cursor-pointer items-center gap-2.5 rounded-[12px] p-1.5 pr-2 transition ${escolhida ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : meio ? "bg-[#F7F4FF]" : "hover:bg-[#F6F4FB]"} ${arrastando ? "opacity-40" : ""} ${indicador === "acima" ? "before:absolute before:inset-x-1 before:-top-[3px] before:h-[3px] before:rounded-full before:bg-[#8B5CF6]" : indicador === "abaixo" ? "after:absolute after:inset-x-1 after:-bottom-[3px] after:h-[3px] after:rounded-full after:bg-[#8B5CF6]" : ""}`;

const LinhaDaPeca = memo(function LinhaDaPeca({ peca, noGrupo, escolhida, meio, arrastando, indicador, renomeando, acoes }: { peca: Peca; noGrupo: boolean; escolhida: boolean; meio: boolean; arrastando: boolean; indicador: Lugar | null; renomeando: boolean; acoes: Acoes }) {
  const alvo = useMemo(() => ({ peca: peca.id }), [peca.id]);
  const ids = useMemo(() => [peca.id], [peca.id]);
  return (
    <div
      data-camada={peca.id}
      onClick={(e) => acoes.escolher(e, ids, noGrupo)}
      onContextMenu={(e) => acoes.menu(e, ids, noGrupo)}
      draggable={!renomeando}
      onDragStart={(e) => acoes.comecar(e, ids)}
      onDragOver={(e) => acoes.passar(e, alvo)}
      onDrop={(e) => acoes.soltar(e, alvo)}
      onDragEnd={acoes.terminar}
      className={`${classeDaLinha(escolhida, meio, arrastando, indicador)} ${noGrupo ? "ml-5" : ""}`}
    >
      <div className="shrink-0 overflow-hidden rounded-[8px] ring-1 ring-black/5">
        <Amostra peca={peca} largura={noGrupo ? 46 : 54} altura={noGrupo ? 32 : 38} fundo={fundoDaAmostra(peca)} />
      </div>
      {renomeando ? (
        <input
          autoFocus
          defaultValue={nomeDaCamada(peca)}
          onClick={(e) => e.stopPropagation()}
          onKeyDown={(e) => {
            e.stopPropagation();
            if (e.key === "Enter") (e.target as HTMLInputElement).blur();
            if (e.key === "Escape") acoes.renomear(null);
          }}
          onBlur={(e) => {
            const novo = e.target.value.trim().slice(0, 40);
            if (novo && novo !== nomeDaCamada(peca)) acoes.mudar(ids, { nome: novo });
            acoes.renomear(null);
          }}
          className="h-7 min-w-0 flex-1 rounded-[6px] border border-[#8B5CF6] bg-white px-1.5 text-[12.5px] text-[#1F1B2E] outline-none"
          aria-label="Nome da camada"
        />
      ) : (
        <span
          onDoubleClick={(e) => {
            e.stopPropagation();
            acoes.renomear(peca.id);
          }}
          title="Dois cliques para renomear"
          className={`min-w-0 flex-1 truncate text-[12.5px] ${escolhida ? "font-semibold text-[#5B21B6]" : "text-[#1F1B2E]"} ${peca.oculta ? "opacity-40" : ""}`}
        >
          {nomeDaCamada(peca)}
        </span>
      )}
      <Botoes ids={ids} ocultas={Boolean(peca.oculta)} travadas={Boolean(peca.bloqueada)} acoes={acoes} />
    </div>
  );
});

const LinhaDoGrupo = memo(
  function LinhaDoGrupo({ grupo, pecas, aberto, escolhido, arrastando, indicador, acoes }: { grupo: string; pecas: Peca[]; aberto: boolean; escolhido: boolean; arrastando: boolean; indicador: Lugar | null; acoes: Acoes }) {
    const ids = pecas.map((p) => p.id);
    const alvo = { grupo };
    const ocultas = pecas.every((p) => p.oculta);
    const travadas = pecas.every((p) => p.bloqueada);
    return (
      <div
        data-camada-grupo={grupo}
        onClick={(e) => acoes.escolher(e, ids, false)}
        onContextMenu={(e) => acoes.menu(e, ids, false)}
        draggable
        onDragStart={(e) => acoes.comecar(e, ids)}
        onDragOver={(e) => acoes.passar(e, alvo)}
        onDrop={(e) => acoes.soltar(e, alvo)}
        onDragEnd={acoes.terminar}
        className={classeDaLinha(escolhido, false, arrastando, indicador)}
      >
        <button
          type="button"
          data-abrir-grupo
          title={aberto ? "Fechar o grupo" : "Abrir o grupo (mexer numa peça dele)"}
          onClick={(e) => {
            e.stopPropagation();
            acoes.abrirGrupo(grupo);
          }}
          className="-mr-1 flex h-7 w-5 shrink-0 items-center justify-center rounded-md text-[#6D28D9] hover:bg-white"
        >
          {aberto ? <ChevronDown size={15} strokeWidth={2.4} /> : <ChevronRight size={15} strokeWidth={2.4} />}
        </button>
        <div className="shrink-0 overflow-hidden rounded-[8px] ring-1 ring-black/5">
          <AmostraDoGrupo pecas={pecas} largura={54} altura={38} />
        </div>
        <span className={`flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden whitespace-nowrap text-[12.5px] ${escolhido ? "font-semibold text-[#5B21B6]" : "text-[#1F1B2E]"} ${ocultas ? "opacity-40" : ""}`}>
          <Group size={13} className="shrink-0 text-[#7C3AED]" />
          <span className="shrink-0">Grupo</span>
          <span className="shrink-0 rounded-full bg-[#EDE7FF] px-1.5 py-[1px] text-[10px] font-bold text-[#6D28D9]">{pecas.length} peças</span>
        </span>
        <Botoes ids={ids} ocultas={ocultas} travadas={travadas} acoes={acoes} />
      </div>
    );
  },
  (a, b) => a.grupo === b.grupo && a.aberto === b.aberto && a.escolhido === b.escolhido && a.arrastando === b.arrastando && a.indicador === b.indicador && a.acoes === b.acoes && a.pecas.length === b.pecas.length && a.pecas.every((p, i) => p === b.pecas[i]),
);

export function PainelDeCamadas({
  pecas,
  selecionadas,
  aoEscolher,
  aoMenu,
  aoMudar,
  aoOrdenar,
  aoSoltar,
}: {
  pecas: Peca[];
  selecionadas: string[];
  /* ids: a peça ou as do grupo; sozinha = só a peça, mesmo sendo de grupo; somar = Shift. */
  aoEscolher: (ids: string[], sozinha: boolean, somar: boolean) => void;
  /* Botão direito: escolhe (se ainda não estava) e abre o menu da arte no ponto. */
  aoMenu: (ids: string[], sozinha: boolean, x: number, y: number) => void;
  aoMudar: (ids: string[], campos: Partial<Peca>) => void;
  aoOrdenar: (ids: string[], para: "subir" | "descer") => void;
  aoSoltar: (ids: string[], alvo: AlvoDaCamada, lugar: Lugar) => void;
}) {
  const unidades = useMemo(() => unidadesDasCamadas(pecas), [pecas]);
  /* Grupos abertos e fechados pela setinha (o da peça escolhida sozinha abre sozinho, se a pessoa não fechou). */
  const [abertos, setAbertos] = useState<{ abertos: Set<string>; fechados: Set<string> }>(() => ({ abertos: new Set(), fechados: new Set() }));
  const [arrastadas, setArrastadas] = useState<string[]>([]);
  const [sobre, setSobre] = useState<{ chave: string; lugar: Lugar } | null>(null);
  const [renomeando, setRenomeando] = useState<string | null>(null);
  /* O que está sendo arrastado também num ref: o "soltar" pode chegar antes do desenho novo. */
  const arrastadasRef = useRef<string[]>([]);
  const escolhidas = useMemo(() => new Set(selecionadas), [selecionadas]);

  /* Escolheu sozinha uma peça de grupo (dois cliques na arte): o grupo aparece aberto para mostrar qual. */
  const grupoDaSozinha = selecionadas.length === 1 ? pecas.find((p) => p.id === selecionadas[0])?.grupo : undefined;
  const estaAberto = (grupo: string) => abertos.abertos.has(grupo) || (grupo === grupoDaSozinha && !abertos.fechados.has(grupo));
  const estaAbertoRef = useRef(estaAberto);
  useEffect(() => {
    estaAbertoRef.current = estaAberto;
  });

  /* As funções das linhas não mudam entre um desenho e outro (as linhas ficam guardadas); o que mudam vem do ref. */
  const props = useRef({ aoEscolher, aoMenu, aoMudar, aoOrdenar, aoSoltar });
  useEffect(() => {
    props.current = { aoEscolher, aoMenu, aoMudar, aoOrdenar, aoSoltar };
  });
  const lugarNoAlvo = (e: React.DragEvent): Lugar => {
    const caixa = (e.currentTarget as HTMLElement).getBoundingClientRect();
    return e.clientY < caixa.top + caixa.height / 2 ? "acima" : "abaixo";
  };
  const chaveDo = (alvo: AlvoDaCamada) => (alvo.peca ? `p:${alvo.peca}` : `g:${alvo.grupo}`);
  const terminar = useCallback(() => {
    arrastadasRef.current = [];
    setArrastadas([]);
    setSobre(null);
  }, []);
  const acoes = useMemo<Acoes>(
    () => ({
      escolher: (e, ids, sozinha) => props.current.aoEscolher(ids, sozinha, e.shiftKey),
      menu: (e, ids, sozinha) => {
        e.preventDefault();
        e.stopPropagation();
        props.current.aoMenu(ids, sozinha, e.clientX, e.clientY);
      },
      mudar: (ids, campos) => props.current.aoMudar(ids, campos),
      ordenar: (ids, para) => props.current.aoOrdenar(ids, para),
      comecar: (e, ids) => {
        arrastadasRef.current = ids;
        setArrastadas(ids);
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("text/plain", ids.join(","));
      },
      passar: (e, alvo) => {
        if (!arrastadasRef.current.length) return;
        e.preventDefault();
        e.stopPropagation();
        const lugar = lugarNoAlvo(e);
        const chave = chaveDo(alvo);
        setSobre((s) => (s && s.chave === chave && s.lugar === lugar ? s : { chave, lugar }));
      },
      soltar: (e, alvo) => {
        e.preventDefault();
        e.stopPropagation();
        const ids = arrastadasRef.current;
        if (ids.length) props.current.aoSoltar(ids, alvo, lugarNoAlvo(e));
        terminar();
      },
      terminar,
      abrirGrupo: (grupo) => {
        const fechar = estaAbertoRef.current(grupo);
        setAbertos((a) => {
          const [abertosAgora, fechadosAgora] = [new Set(a.abertos), new Set(a.fechados)];
          if (fechar) {
            abertosAgora.delete(grupo);
            fechadosAgora.add(grupo);
          } else {
            abertosAgora.add(grupo);
            fechadosAgora.delete(grupo);
          }
          return { abertos: abertosAgora, fechados: fechadosAgora };
        });
      },
      renomear: setRenomeando,
    }),
    [terminar],
  );

  const arrastando = new Set(arrastadas);
  const indicadorDe = (chave: string) => (sobre && sobre.chave === chave ? sobre.lugar : null);
  return (
    <div>
      <p className="mb-3 text-[12px] leading-snug text-[#8A849E]">A de cima fica na frente. Arraste para mudar a ordem; a setinha abre o grupo; botão direito para desagrupar; dois cliques no nome renomeiam.</p>
      <div className="space-y-1" onDragLeave={(e) => !e.currentTarget.contains(e.relatedTarget as Node) && setSobre(null)}>
        {unidades.map((u) => {
          if (u.tipo === "peca") {
            const p = u.peca;
            return <LinhaDaPeca key={p.id} peca={p} noGrupo={false} escolhida={escolhidas.has(p.id)} meio={false} arrastando={arrastando.has(p.id)} indicador={indicadorDe(`p:${p.id}`)} renomeando={renomeando === p.id} acoes={acoes} />;
          }
          const inteiro = u.pecas.every((p) => escolhidas.has(p.id));
          const aberto = estaAberto(u.grupo);
          return (
            <div key={`g:${u.grupo}`} className="space-y-1">
              <LinhaDoGrupo grupo={u.grupo} pecas={u.pecas} aberto={aberto} escolhido={inteiro} arrastando={u.pecas.every((p) => arrastando.has(p.id))} indicador={indicadorDe(`g:${u.grupo}`)} acoes={acoes} />
              {aberto
                ? u.pecas.map((p) => (
                    <LinhaDaPeca key={p.id} peca={p} noGrupo escolhida={!inteiro && escolhidas.has(p.id)} meio={inteiro} arrastando={arrastando.has(p.id)} indicador={indicadorDe(`p:${p.id}`)} renomeando={renomeando === p.id} acoes={acoes} />
                  ))
                : null}
            </div>
          );
        })}
      </div>
    </div>
  );
}
