import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import {
  ActionIcon,
  Autocomplete,
  Avatar,
  Badge,
  Group,
  Menu,
  Paper,
  Popover,
  Select,
  Stack,
  Text,
  Tooltip,
} from "@mantine/core";
import {
  IconArchive,
  IconCalendarDue,
  IconCheck,
  IconClock,
  IconCopy,
  IconDotsVertical,
  IconEdit,
  IconGripVertical,
  IconHistory,
  IconHourglass,
  IconLock,
  IconLockOpen,
  IconPalette,
  IconUserSquare,
  IconTrash,
  IconUserCircle,
} from "@tabler/icons-react";
import { DatePickerInput } from "@mantine/dates";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { Card, CardColor, User } from "../types";
import { colorBg, colorBorder, tagColor } from "./colors";
import { ColorPickerGrid } from "./ColorPickerGrid";
import { formatDateTimeShort, formatDuration } from "./format";
import { JiraSyncIndicator } from "./JiraSyncIndicator";

interface Props {
  card: Card;
  now: number;
  onDelete: (id: string) => void;
  onEdit: (card: Card) => void;
  onDuplicate?: (id: string) => void;
  onChangeColor: (id: string, color: CardColor) => void;
  onShowHistory: (card: Card) => void;
  onToggleLock: (id: string, locked: boolean) => void;
  onAssign: (id: string, assignee_id: string | null) => void;
  onSetDeadline?: (id: string, deadline: string | null) => void;
  onSetRequester?: (id: string, requester: string) => void;
  onArchive?: (id: string) => void;
  requesterOptions?: string[];
  onOpenCustomColor?: (cardId: string, current: string) => void;
  activeSticker?: string | null;
  onAddSticker?: (cardId: string, x: number, y: number) => void;
  onRemoveSticker?: (cardId: string, index: number) => void;
  onMoveSticker?: (cardId: string, index: number, x: number, y: number) => void;
  onCommitSticker?: (cardId: string) => void;
  users: User[];
  assignee?: User;
  inDoneColumn?: boolean;
  columnOverdue?: boolean;
  isOverlay?: boolean;
  highlight?: boolean;
}

// PERF debug helpers (gated): cuentan renders por capa durante drag.
function _probeRender() {
  const w = window as unknown as { _cardRenderProbe?: boolean; _cardRenderCount?: number };
  if (w._cardRenderProbe) w._cardRenderCount = (w._cardRenderCount || 0) + 1;
}
function _probeBodyRender() {
  const w = window as unknown as { _cardRenderProbe?: boolean; _cardBodyRenderCount?: number };
  if (w._cardRenderProbe) w._cardBodyRenderCount = (w._cardBodyRenderCount || 0) + 1;
}

// KanbanCardBody — contiene Stack + sticker overlay + states locales (popovers,
// requesterDraft). Memoizado para que dnd-kit re-render del wrapper exterior
// (provocado por useSortable cada pointermove) NO rebote a este tree.
interface CardBodyProps {
  card: Card;
  isDone: boolean;
  isOverlay?: boolean;
  highlight?: boolean;
  activeSticker?: string | null;
  cardElRef: React.MutableRefObject<HTMLElement | null>;
  now: number;
  users: User[];
  assignee?: User;
  requesterOptions?: string[];
  menuOpen: boolean;
  setMenuOpen: (v: boolean | ((p: boolean) => boolean)) => void;
  onDelete: (id: string) => void;
  onEdit: (card: Card) => void;
  onDuplicate?: (id: string) => void;
  onChangeColor: (id: string, color: CardColor) => void;
  onShowHistory: (card: Card) => void;
  onToggleLock: (id: string, locked: boolean) => void;
  onAssign: (id: string, assignee_id: string | null) => void;
  onSetDeadline?: (id: string, deadline: string | null) => void;
  onSetRequester?: (id: string, requester: string) => void;
  onArchive?: (id: string) => void;
  onOpenCustomColor?: (cardId: string, current: string) => void;
  onRemoveSticker?: (cardId: string, index: number) => void;
  onMoveSticker?: (cardId: string, index: number, x: number, y: number) => void;
  onCommitSticker?: (cardId: string) => void;
}

const KanbanCardBody = memo(function KanbanCardBody({
  card,
  isDone,
  isOverlay,
  activeSticker,
  cardElRef,
  now,
  users,
  assignee,
  requesterOptions,
  menuOpen,
  setMenuOpen,
  onDelete,
  onEdit,
  onDuplicate,
  onChangeColor,
  onShowHistory,
  onToggleLock,
  onAssign,
  onSetDeadline,
  onSetRequester,
  onArchive,
  onOpenCustomColor,
  onRemoveSticker,
  onMoveSticker,
  onCommitSticker,
}: CardBodyProps) {
  _probeBodyRender();
  const stickerMode = !!activeSticker;
  const [colorPopOpen, setColorPopOpen] = useState(false);
  const [assigneePopOpen, setAssigneePopOpen] = useState(false);
  const [requesterPopOpen, setRequesterPopOpen] = useState(false);
  const [deadlinePopOpen, setDeadlinePopOpen] = useState(false);
  const [requesterDraft, setRequesterDraft] = useState(card.requester || "");
  const draggingStickerRef = useRef<number | null>(null);

  const enteredAt = card.entered_at ? new Date(card.entered_at).getTime() : now;
  const liveMs = Math.max(0, now - enteredAt);
  const deadlineAt = card.deadline ? new Date(card.deadline).getTime() : 0;
  const deadlineRemainingMs = deadlineAt ? deadlineAt - now : 0;
  const overdue = deadlineAt ? deadlineRemainingMs < 0 : false;
  const createdAtMs = card.created_at ? new Date(card.created_at).getTime() : 0;
  const deadlineTotalMs = deadlineAt && createdAtMs ? deadlineAt - createdAtMs : 0;
  const deadlineRatio = deadlineTotalMs > 0 ? deadlineRemainingMs / deadlineTotalMs : 0;
  let dlColor: string = "blue";
  let dlVariant: "light" | "filled" = "light";
  if (overdue) { dlColor = "red.9"; dlVariant = "filled"; }
  else if (deadlineRatio < 0.1) { dlColor = "red"; dlVariant = "filled"; }
  else if (deadlineRatio < 0.5) { dlColor = "yellow"; dlVariant = "light"; }
  const lockedAt = card.locked_at ? new Date(card.locked_at).getTime() : 0;
  const lockedMs = card.locked && lockedAt ? Math.max(0, now - lockedAt) : 0;
  const createdAt = card.created_at ? new Date(card.created_at).getTime() : 0;
  const completedAt = card.completed_at ? new Date(card.completed_at).getTime() : 0;
  const totalDoneMs = isDone && createdAt && completedAt ? Math.max(0, completedAt - createdAt) : 0;

  const startStickerDrag = (index: number) => (e: React.PointerEvent<HTMLSpanElement>) => {
    if (!stickerMode || isOverlay || !onMoveSticker) return;
    if (e.button !== 0) return;
    e.stopPropagation();
    e.preventDefault();
    const rect = cardElRef.current?.getBoundingClientRect();
    if (!rect) return;
    draggingStickerRef.current = index;
    const target = e.currentTarget;
    target.setPointerCapture(e.pointerId);
    const onMove = (ev: PointerEvent) => {
      const idx = draggingStickerRef.current;
      if (idx === null) return;
      const x = (ev.clientX - rect.left) / rect.width;
      const y = (ev.clientY - rect.top) / rect.height;
      onMoveSticker(card.id, idx, Math.max(0, Math.min(1, x)), Math.max(0, Math.min(1, y)));
    };
    const onUp = (ev: PointerEvent) => {
      target.releasePointerCapture?.(ev.pointerId);
      target.removeEventListener("pointermove", onMove);
      target.removeEventListener("pointerup", onUp);
      target.removeEventListener("pointercancel", onUp);
      draggingStickerRef.current = null;
      onCommitSticker?.(card.id);
    };
    target.addEventListener("pointermove", onMove);
    target.addEventListener("pointerup", onUp);
    target.addEventListener("pointercancel", onUp);
  };

  const onStickerContextMenu = (index: number) => (e: React.MouseEvent) => {
    if (!stickerMode || isOverlay) return;
    e.preventDefault();
    e.stopPropagation();
    onRemoveSticker?.(card.id, index);
  };

  const menuItems = !menuOpen ? null : (
    <>
      <Menu.Label>Acciones</Menu.Label>
      <Menu.Item leftSection={<IconEdit size={14} />} onClick={() => { setMenuOpen(false); onEdit(card); }}>Editar</Menu.Item>
      {onDuplicate && (
        <Menu.Item leftSection={<IconCopy size={14} />} onClick={() => { setMenuOpen(false); onDuplicate(card.id); }}>Duplicar</Menu.Item>
      )}
      <Popover opened={colorPopOpen} onChange={setColorPopOpen} position="right-start" withArrow shadow="md">
        <Popover.Target>
          <Menu.Item
            leftSection={<IconPalette size={14} />}
            onClick={(e) => { e.preventDefault(); e.stopPropagation(); setColorPopOpen((v) => !v); }}
            closeMenuOnClick={false}
          >
            Color
          </Menu.Item>
        </Popover.Target>
        <Popover.Dropdown p="xs" onDoubleClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}>
          <ColorPickerGrid
            value={card.color}
            onChange={(c) => onChangeColor(card.id, c as CardColor)}
            onOpenCustom={onOpenCustomColor ? () => onOpenCustomColor(card.id, card.color || "#888888") : undefined}
          />
        </Popover.Dropdown>
      </Popover>
      <Popover opened={assigneePopOpen} onChange={setAssigneePopOpen} position="right-start" withArrow shadow="md" withinPortal={false}>
        <Popover.Target>
          <Menu.Item
            leftSection={<IconUserCircle size={14} />}
            onClick={(e) => { e.preventDefault(); e.stopPropagation(); setAssigneePopOpen((v) => !v); }}
            closeMenuOnClick={false}
          >
            Asignar a {assignee ? `(${assignee.display_name || assignee.username})` : "..."}
          </Menu.Item>
        </Popover.Target>
        <Popover.Dropdown p="xs" onDoubleClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}>
          <Select
            placeholder="Sin asignar"
            value={card.assignee_id ?? null}
            onChange={(v) => { onAssign(card.id, v); setAssigneePopOpen(false); setMenuOpen(false); }}
            data={users.map((u) => ({ value: u.id, label: u.display_name || u.username }))}
            clearable
            searchable
            autoFocus
            comboboxProps={{ withinPortal: false }}
          />
        </Popover.Dropdown>
      </Popover>
      <Popover opened={requesterPopOpen} onChange={setRequesterPopOpen} position="right-start" withArrow shadow="md" withinPortal={false}>
        <Popover.Target>
          <Menu.Item
            leftSection={<IconUserSquare size={14} />}
            onClick={(e) => { e.preventDefault(); e.stopPropagation(); setRequesterDraft(card.requester || ""); setRequesterPopOpen((v) => !v); }}
            closeMenuOnClick={false}
          >
            Solicitante {card.requester ? `(${card.requester})` : "..."}
          </Menu.Item>
        </Popover.Target>
        <Popover.Dropdown p="xs" onDoubleClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}>
          <Autocomplete
            placeholder="Sin solicitante"
            value={requesterDraft}
            onChange={setRequesterDraft}
            data={requesterOptions || []}
            autoFocus
            comboboxProps={{ withinPortal: false }}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.preventDefault();
                onSetRequester?.(card.id, requesterDraft.trim());
                setRequesterPopOpen(false);
                setMenuOpen(false);
              } else if (e.key === "Escape") {
                setRequesterPopOpen(false);
              }
            }}
            onOptionSubmit={(v) => { setRequesterDraft(v); onSetRequester?.(card.id, v); setRequesterPopOpen(false); setMenuOpen(false); }}
          />
        </Popover.Dropdown>
      </Popover>
      <Menu.Item
        leftSection={card.locked ? <IconLockOpen size={14} /> : <IconLock size={14} />}
        color={card.locked ? "yellow" : undefined}
        onClick={() => { setMenuOpen(false); onToggleLock(card.id, !card.locked); }}
      >
        {card.locked ? "Desbloquear" : "Bloquear"}
      </Menu.Item>
      <Menu.Item leftSection={<IconHistory size={14} />} onClick={() => { setMenuOpen(false); onShowHistory(card); }}>Historial</Menu.Item>
      {onSetDeadline && (
        <Popover opened={deadlinePopOpen} onChange={setDeadlinePopOpen} position="right-start" withArrow shadow="md" withinPortal={false}>
          <Popover.Target>
            <Menu.Item
              leftSection={<IconCalendarDue size={14} />}
              onClick={(e) => { e.preventDefault(); e.stopPropagation(); setDeadlinePopOpen((v) => !v); }}
              closeMenuOnClick={false}
            >
              {card.deadline ? `Deadline (${card.deadline.slice(0, 10)})` : "Deadline..."}
            </Menu.Item>
          </Popover.Target>
          <Popover.Dropdown p="xs" onDoubleClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}>
            <DatePickerInput
              value={card.deadline ? card.deadline.slice(0, 10) : null}
              onChange={(v) => {
                const s = v ? (typeof v === "string" ? v.slice(0, 10) : new Date(v as unknown as string).toISOString().slice(0, 10)) : null;
                onSetDeadline(card.id, s ? `${s}T23:59:59Z` : null);
                setDeadlinePopOpen(false);
                setMenuOpen(false);
              }}
              clearable
              valueFormat="DD/MM/YYYY"
              size="xs"
              placeholder="Elegir fecha"
              popoverProps={{ withinPortal: false }}
            />
            {card.deadline && (
              <Tooltip label="Quitar deadline" withArrow>
                <ActionIcon size="sm" variant="subtle" color="red" mt={6} onClick={() => { onSetDeadline(card.id, null); setDeadlinePopOpen(false); setMenuOpen(false); }}>
                  <IconTrash size={12} />
                </ActionIcon>
              </Tooltip>
            )}
          </Popover.Dropdown>
        </Popover>
      )}
      {isDone && onArchive && (
        <Menu.Item
          leftSection={<IconArchive size={14} />}
          color="teal"
          onClick={() => { setMenuOpen(false); onArchive(card.id); }}
        >
          Archivar
        </Menu.Item>
      )}
      <Menu.Divider />
      <Menu.Item leftSection={<IconTrash size={14} />} color="red" onClick={() => { setMenuOpen(false); onDelete(card.id); }}>Borrar</Menu.Item>
    </>
  );

  return (
    <>
      <Stack gap={6} style={{ position: "relative", zIndex: 1, pointerEvents: stickerMode ? "none" : undefined }}>
        <Group justify="space-between" gap={4} wrap="nowrap" align="flex-start">
          <Group gap={4} wrap="nowrap" style={{ flex: 1, minWidth: 0 }} align="flex-start">
            <IconGripVertical size={14} color="var(--mantine-color-dark-2)" style={{ flexShrink: 0, marginTop: 4 }} />
            {card.locked && (
              <Tooltip label="Bloqueada" withArrow>
                <IconLock size={14} color="var(--mantine-color-yellow-6)" style={{ flexShrink: 0, marginTop: 4 }} />
              </Tooltip>
            )}
            <Text
              size="sm"
              fw={500}
              style={{ flex: 1, wordBreak: "break-word", whiteSpace: "normal", textDecoration: isDone ? "line-through" : "none", opacity: isDone ? 0.7 : 1 }}
            >
              {card.title}
            </Text>
          </Group>
          <Stack gap={4} align="center" style={{ flexShrink: 0 }}>
            <Menu opened={menuOpen} onChange={setMenuOpen} position="bottom-end" shadow="md" withArrow>
              <Menu.Target>
                <ActionIcon variant="subtle" color="gray" size="sm" aria-label="Acciones" onPointerDown={(e) => e.stopPropagation()}>
                  <IconDotsVertical size={14} />
                </ActionIcon>
              </Menu.Target>
              <Menu.Dropdown onDoubleClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} onContextMenu={(e) => e.stopPropagation()}>
                {menuItems}
              </Menu.Dropdown>
            </Menu>
            <JiraSyncIndicator cardId={card.id} />
          </Stack>
        </Group>
        {(card.requester || assignee) && (
          <Group gap={6} wrap="nowrap" style={{ minWidth: 0 }}>
            {card.requester && (
              <>
                <Avatar size={18} radius="xs" color={tagColor(card.requester)} style={{ flexShrink: 0 }}>
                  {card.requester.slice(0, 2).toUpperCase()}
                </Avatar>
                <Text size="xs" c="dimmed" truncate>{card.requester}</Text>
              </>
            )}
            {card.requester && assignee && (
              <Text size="xs" c="dimmed" style={{ flexShrink: 0 }}>-</Text>
            )}
            {assignee && (
              <>
                <Avatar size={18} radius="xl" color={assignee.color || "blue"} style={{ flexShrink: 0 }}>
                  {(assignee.display_name || assignee.username).slice(0, 2).toUpperCase()}
                </Avatar>
                <Text size="xs" c="dimmed" truncate>{assignee.display_name || assignee.username}</Text>
              </>
            )}
          </Group>
        )}
        {card.description && (
          <Text size="xs" c="dimmed" lineClamp={3}>{card.description}</Text>
        )}
        {card.tags && card.tags.length > 0 && (
          <Group gap={4} wrap="wrap">
            {card.tags.map((t) => (
              <Badge key={t} size="xs" variant="light" color={tagColor(t)} radius="sm">{t}</Badge>
            ))}
          </Group>
        )}
        <Group gap={4} wrap="wrap">
          {card.locked && (
            <Badge size="xs" variant="light" color="yellow" leftSection={<IconLock size={10} />}>{formatDuration(lockedMs)}</Badge>
          )}
          {!card.locked && isDone && card.completed_at ? (
            <>
              <Badge size="xs" variant="light" color="teal" leftSection={<IconCheck size={10} />}>{formatDateTimeShort(card.completed_at)}</Badge>
              <Badge size="xs" variant="light" color="gray" leftSection={<IconClock size={10} />}>Total: {formatDuration(totalDoneMs)}</Badge>
              {card.total_locked_ms > 0 && (
                <Badge size="xs" variant="light" color="yellow" leftSection={<IconLock size={10} />}>{formatDuration(card.total_locked_ms)}</Badge>
              )}
            </>
          ) : !card.locked ? (
            card.deadline ? (
              <Tooltip label={`Vence: ${formatDateTimeShort(card.deadline)}`} withArrow>
                <Badge size="xs" variant={dlVariant} color={dlColor} leftSection={<IconHourglass size={10} />}>
                  {overdue ? `-${formatDuration(-deadlineRemainingMs)}` : formatDuration(deadlineRemainingMs)}
                </Badge>
              </Tooltip>
            ) : (
              <Badge size="xs" variant="light" color="gray" leftSection={<IconClock size={10} />}>{formatDuration(liveMs)}</Badge>
            )
          ) : null}
        </Group>
        {card.seq_num > 0 && (
          <Text size="xs" c="dimmed" style={{ marginTop: -2 }}>#{String(card.seq_num).padStart(5, "0")}</Text>
        )}
      </Stack>
      {card.stickers && card.stickers.length > 0 && (
        <div
          data-sticker-overlay
          style={{ position: "absolute", inset: 0, pointerEvents: "none", overflow: "hidden", borderRadius: "inherit", zIndex: 0 }}
        >
          {card.stickers.map((s, i) => (
            <span
              key={i}
              onPointerDown={startStickerDrag(i)}
              onContextMenu={onStickerContextMenu(i)}
              title={stickerMode ? "Arrastra para mover. Click derecho para borrar." : ""}
              style={{
                position: "absolute",
                left: `${s.x * 100}%`,
                top: `${s.y * 100}%`,
                transform: "translate(-50%, -50%)",
                fontSize: 48,
                lineHeight: 1,
                opacity: 1,
                userSelect: "none",
                cursor: stickerMode && !isOverlay ? "grab" : "default",
                pointerEvents: stickerMode && !isOverlay ? "auto" : "none",
                touchAction: "none",
              }}
            >
              {s.emoji}
            </span>
          ))}
        </div>
      )}
    </>
  );
});

function KanbanCardImpl({
  card,
  now,
  onDelete,
  onEdit,
  onDuplicate,
  onChangeColor,
  onShowHistory,
  onToggleLock,
  onAssign,
  onSetDeadline,
  onSetRequester,
  onArchive,
  requesterOptions,
  onOpenCustomColor,
  activeSticker,
  onAddSticker,
  onRemoveSticker,
  onMoveSticker,
  onCommitSticker,
  users,
  assignee,
  inDoneColumn,
  columnOverdue,
  isOverlay,
  highlight,
}: Props) {
  _probeRender();
  const isDone = inDoneColumn || !!card.completed_at;
  const [menuOpen, setMenuOpen] = useState(false);
  const cardElRef = useRef<HTMLElement | null>(null);
  const stickerMode = !!activeSticker;
  // Memo: useSortable es sensible a la identidad del objeto `data`. Si lo
  // re-creamos cada render, el setNodeRef interno se vuelve inestable y
  // dispara loops por useMergedRef de Mantine (Paper). Issue: maximum
  // update depth visto durante drag.
  const sortableData = useMemo(
    () => ({ type: "card" as const, columnId: card.column_id, locked: card.locked }),
    [card.column_id, card.locked]
  );
  // Perf: disable layout animations. dnd-kit's default animates the slide of
  // non-dragged items into their new sort position via an FLIP-like loop that
  // re-runs useSortable on every pointermove for ALL cards in the
  // SortableContext. With dozens of cards that drops frames hard (p95>=80ms).
  // Disabling animations keeps the visual shift driven only by the active
  // card's transform.
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
    id: card.id,
    data: sortableData,
    disabled: stickerMode,
    animateLayoutChanges: () => false,
  });

  const setCardRef = useCallback((el: HTMLElement | null) => {
    cardElRef.current = el;
    setNodeRef(el);
  }, [setNodeRef]);

  useEffect(() => {
    if (highlight && cardElRef.current) {
      cardElRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
    }
  }, [highlight]);

  const onCardClickAddSticker = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!stickerMode || !onAddSticker || isOverlay) return;
    if ((e.target as HTMLElement).closest("[data-sticker-overlay]")) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width;
    const y = (e.clientY - rect.top) / rect.height;
    onAddSticker(card.id, Math.max(0, Math.min(1, x)), Math.max(0, Math.min(1, y)));
  };

  const borderColorPicked = highlight
    ? "var(--mantine-color-blue-5)"
    : columnOverdue
      ? "var(--mantine-color-red-6)"
      : card.locked
        ? "var(--mantine-color-yellow-6)"
        : colorBorder(card.color);
  const style: React.CSSProperties = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.4 : 1,
    background: colorBg(card.color),
    borderColor: borderColorPicked,
    borderWidth: highlight || card.locked || columnOverdue ? 2 : 1,
    boxShadow: highlight
      ? "0 0 0 3px var(--mantine-color-blue-4)"
      : columnOverdue
        ? "0 0 0 2px var(--mantine-color-red-3)"
        : undefined,
    filter: isDone ? "brightness(0.55) saturate(0.7)" : undefined,
  };

  const onContextMenu = (e: React.MouseEvent) => {
    e.preventDefault();
    setMenuOpen(true);
  };


  return (
    <Paper
      ref={setCardRef}
      style={{ ...style, position: "relative", cursor: stickerMode ? "copy" : "grab", touchAction: "none" }}
      withBorder
      p="xs"
      shadow={isOverlay ? "lg" : "xs"}
      radius="md"
      data-card-id={card.id}
      data-column-overdue={columnOverdue ? "true" : "false"}
      data-locked={card.locked ? "true" : "false"}
      onContextMenu={onContextMenu}
      onClick={onCardClickAddSticker}
      onDoubleClick={(e) => {
        e.stopPropagation();
        onEdit(card);
      }}
      {...attributes}
      {...(stickerMode ? {} : listeners)}
    >
      <KanbanCardBody
        card={card}
        isDone={isDone}
        isOverlay={isOverlay}
        highlight={highlight}
        activeSticker={activeSticker}
        cardElRef={cardElRef}
        now={now}
        users={users}
        assignee={assignee}
        requesterOptions={requesterOptions}
        menuOpen={menuOpen}
        setMenuOpen={setMenuOpen}
        onDelete={onDelete}
        onEdit={onEdit}
        onDuplicate={onDuplicate}
        onChangeColor={onChangeColor}
        onShowHistory={onShowHistory}
        onToggleLock={onToggleLock}
        onAssign={onAssign}
        onSetDeadline={onSetDeadline}
        onSetRequester={onSetRequester}
        onArchive={onArchive}
        onOpenCustomColor={onOpenCustomColor}
        onRemoveSticker={onRemoveSticker}
        onMoveSticker={onMoveSticker}
        onCommitSticker={onCommitSticker}
      />
    </Paper>
  );
}

export const KanbanCard = memo(KanbanCardImpl);
