"use client";

import dynamic from "next/dynamic";
import { useCallback, useEffect, useRef, useState } from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder";
import {
  AlignLeft,
  Bold,
  Brain,
  ChevronRight,
  Italic,
  List,
  ListOrdered,
  Plus,
  Sparkles,
  Trash2,
  X,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useMindMaps, type MindMap, type MindNode, type MindDrawing, type NodeColor } from "@/hooks/use-mind-maps";

// ─── Types ────────────────────────────────────────────────────────────────────

interface DrawingPoint { x: number; y: number }

type DrawMode = "select" | "sketch" | "line" | "arrow";

// ─── Constants ────────────────────────────────────────────────────────────────

const NODE_W = 200;
const NODE_H = 56;
const LEVEL_GAP = 280;
const MIN_V_GAP = 80;

const COLOR_STYLES: Record<NodeColor, {
  border: string; bg: string; text: string; dot: string;
  glow: string; ring: string;
}> = {
  amber:   { border: "border-amber-500/[0.28]",   bg: "bg-amber-500/[0.07]",   text: "text-amber-100",   dot: "bg-amber-400",   glow: "rgba(245,158,11,0.22)",   ring: "ring-amber-500/25"   },
  violet:  { border: "border-violet-500/[0.28]",  bg: "bg-violet-500/[0.07]",  text: "text-violet-100",  dot: "bg-violet-400",  glow: "rgba(139,92,246,0.22)",   ring: "ring-violet-500/25"  },
  emerald: { border: "border-emerald-500/[0.28]", bg: "bg-emerald-500/[0.07]", text: "text-emerald-100", dot: "bg-emerald-400", glow: "rgba(16,185,129,0.22)",   ring: "ring-emerald-500/25" },
  sky:     { border: "border-sky-500/[0.28]",     bg: "bg-sky-500/[0.07]",     text: "text-sky-100",     dot: "bg-sky-400",     glow: "rgba(14,165,233,0.22)",   ring: "ring-sky-500/25"     },
  rose:    { border: "border-rose-500/[0.28]",    bg: "bg-rose-500/[0.07]",    text: "text-rose-100",    dot: "bg-rose-400",    glow: "rgba(244,63,94,0.22)",    ring: "ring-rose-500/25"    },
};

const NODE_COLORS: NodeColor[] = ["amber", "violet", "emerald", "sky", "rose"];

// ─── Helpers ──────────────────────────────────────────────────────────────────

function uid() { return Math.random().toString(36).slice(2, 10); }

function stripHtml(html: string): string {
  return html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
}

// ─── Layout ───────────────────────────────────────────────────────────────────

interface NodePos { x: number; y: number }

function computeLayout(map: MindMap): Record<string, NodePos> {
  const positions: Record<string, NodePos> = {};

  function subtreeSize(nodeId: string): number {
    const node = map.nodes[nodeId];
    if (!node || node.children.length === 0) return 1;
    return node.children.reduce((sum, cid) => sum + subtreeSize(cid), 0);
  }

  function place(nodeId: string, level: number, yTop: number, slots: number): void {
    const node = map.nodes[nodeId];
    if (!node) return;
    const x = 60 + level * LEVEL_GAP;
    const totalH = slots * MIN_V_GAP;
    const y = yTop + totalH / 2 - NODE_H / 2;
    positions[nodeId] = node.manualPosition ?? { x, y };
    if (!node.children.length) return;
    let childY = yTop;
    for (const cid of node.children) {
      const s = subtreeSize(cid);
      place(cid, level + 1, childY, s);
      childY += s * MIN_V_GAP;
    }
  }

  place(map.rootId, 0, 0, subtreeSize(map.rootId));
  return positions;
}

function canvasSize(positions: Record<string, NodePos>) {
  const xs = Object.values(positions).map((p) => p.x + NODE_W + 80);
  const ys = Object.values(positions).map((p) => p.y + NODE_H + 80);
  return {
    width: Math.max(1200, Math.max(...xs)),
    height: Math.max(700, Math.max(...ys)),
  };
}

// ─── SVG Helpers ──────────────────────────────────────────────────────────────

const STROKE_COLORS: Record<NodeColor, string> = {
  amber:   "rgba(251,191,36,0.40)",
  violet:  "rgba(167,139,250,0.40)",
  emerald: "rgba(52,211,153,0.40)",
  sky:     "rgba(56,189,248,0.40)",
  rose:    "rgba(251,113,133,0.40)",
};

function Connection({ x1, y1, x2, y2, color }: { x1: number; y1: number; x2: number; y2: number; color: NodeColor }) {
  const cx1 = x1 + NODE_W, cy1 = y1 + NODE_H / 2;
  const cx2 = x2, cy2 = y2 + NODE_H / 2;
  const mx = (cx1 + cx2) / 2;
  return (
    <path
      d={`M ${cx1} ${cy1} C ${mx} ${cy1}, ${mx} ${cy2}, ${cx2} ${cy2}`}
      fill="none" stroke={STROKE_COLORS[color]} strokeWidth={1.5}
    />
  );
}

function pointsToPath(pts: DrawingPoint[]) {
  if (!pts.length) return "";
  return pts.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
}

function arrowHead(s: DrawingPoint, e: DrawingPoint): string {
  const a = Math.atan2(e.y - s.y, e.x - s.x);
  const l = 14, sp = Math.PI / 7;
  return `${e.x},${e.y} ${e.x - l * Math.cos(a - sp)},${e.y - l * Math.sin(a - sp)} ${e.x - l * Math.cos(a + sp)},${e.y - l * Math.sin(a + sp)}`;
}

// ─── Node Detail Panel ────────────────────────────────────────────────────────

interface DetailPanelProps {
  node: MindNode;
  isRoot: boolean;
  onUpdateTitle: (id: string, title: string) => void;
  onUpdateDescription: (id: string, desc: string) => void;
  onUpdateColor: (id: string, color: NodeColor) => void;
  onAddChild: () => void;
  onDelete: () => void;
  onClose: () => void;
}

function NodeDetailPanel({
  node, isRoot, onUpdateTitle, onUpdateDescription, onUpdateColor, onAddChild, onDelete, onClose,
}: DetailPanelProps) {
  const [localTitle, setLocalTitle] = useState(node.title);

  useEffect(() => { setLocalTitle(node.title); }, [node.id, node.title]);

  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit,
      Placeholder.configure({ placeholder: "Beschreibung, Gedanken, Stichpunkte…" }),
    ],
    content: node.description || "",
    editorProps: {
      attributes: {
        class: "outline-none min-h-[100px] text-[13px] leading-relaxed text-white/75 prose-mindmap",
      },
    },
    onBlur: ({ editor }) => {
      const html = editor.getHTML();
      if (html !== node.description) onUpdateDescription(node.id, html);
    },
  });

  useEffect(() => {
    if (!editor) return;
    const cur = editor.getHTML();
    const target = node.description || "";
    if (cur !== target) editor.commands.setContent(target);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [node.id]);

  const btnBase = "h-7 w-7 flex items-center justify-center rounded-lg transition-colors text-white/40 hover:text-white/80 hover:bg-white/[0.06]";
  const btnActive = "bg-white/[0.10] text-white/90";

  return (
    <aside className="fixed inset-0 z-40 flex flex-col overflow-hidden bg-[#0a0a0d] md:static md:inset-auto md:z-auto md:w-[300px] md:shrink-0 md:border-l md:border-white/[0.06]">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 border-b border-white/[0.05]">
        <span className="text-[11px] font-bold uppercase tracking-[0.14em] text-white/30">Node</span>
        <button onClick={onClose} className="text-white/30 hover:text-white/60 transition-colors">
          <X className="w-4 h-4" />
        </button>
      </div>

      <div className="flex-1 overflow-y-auto px-4 py-4 space-y-5">
        {/* Title */}
        <div>
          <label className="text-[10px] font-semibold uppercase tracking-[0.14em] text-white/25 block mb-1.5">
            Titel
          </label>
          <input
            value={localTitle}
            onChange={(e) => setLocalTitle(e.target.value)}
            onBlur={() => {
              const t = localTitle.trim();
              if (t && t !== node.title) onUpdateTitle(node.id, t);
            }}
            onKeyDown={(e) => {
              if (e.key === "Enter") (e.target as HTMLInputElement).blur();
            }}
            className="w-full rounded-xl border border-white/[0.08] bg-white/[0.03] px-3 py-2 text-[14px] font-semibold text-white placeholder:text-white/25 outline-none focus:border-white/[0.16] transition-colors"
          />
        </div>

        {/* Color */}
        <div>
          <label className="text-[10px] font-semibold uppercase tracking-[0.14em] text-white/25 block mb-2">
            Farbe
          </label>
          <div className="flex gap-2.5">
            {NODE_COLORS.map((c) => (
              <button
                key={c}
                onClick={() => onUpdateColor(node.id, c)}
                title={c}
                className={cn(
                  "w-6 h-6 rounded-full transition-all",
                  COLOR_STYLES[c].dot,
                  node.color === c ? "scale-125 ring-2 ring-white/40" : "opacity-50 hover:opacity-85 hover:scale-110"
                )}
              />
            ))}
          </div>
        </div>

        {/* Description */}
        <div>
          <label className="text-[10px] font-semibold uppercase tracking-[0.14em] text-white/25 block mb-1.5">
            Beschreibung
          </label>

          {/* Toolbar */}
          <div className="flex items-center gap-0.5 mb-1.5 rounded-lg border border-white/[0.06] bg-white/[0.02] p-1">
            <button
              onClick={() => editor?.chain().focus().toggleBold().run()}
              className={cn(btnBase, editor?.isActive("bold") && btnActive)}
              title="Fett (Cmd+B)"
            >
              <Bold className="w-3.5 h-3.5" />
            </button>
            <button
              onClick={() => editor?.chain().focus().toggleItalic().run()}
              className={cn(btnBase, editor?.isActive("italic") && btnActive)}
              title="Kursiv (Cmd+I)"
            >
              <Italic className="w-3.5 h-3.5" />
            </button>
            <div className="w-px h-4 bg-white/[0.08] mx-0.5" />
            <button
              onClick={() => editor?.chain().focus().toggleBulletList().run()}
              className={cn(btnBase, editor?.isActive("bulletList") && btnActive)}
              title="Stichpunkte"
            >
              <List className="w-3.5 h-3.5" />
            </button>
            <button
              onClick={() => editor?.chain().focus().toggleOrderedList().run()}
              className={cn(btnBase, editor?.isActive("orderedList") && btnActive)}
              title="Nummeriert"
            >
              <ListOrdered className="w-3.5 h-3.5" />
            </button>
          </div>

          {/* Editor */}
          <div className="rounded-xl border border-white/[0.07] bg-white/[0.025] px-3 py-2.5 min-h-[120px] cursor-text [&_.ProseMirror]:outline-none [&_.ProseMirror_p]:m-0 [&_.ProseMirror_p+p]:mt-1 [&_.ProseMirror_ul]:pl-4 [&_.ProseMirror_ul]:list-disc [&_.ProseMirror_ol]:pl-4 [&_.ProseMirror_ol]:list-decimal [&_.ProseMirror_li]:my-0.5 [&_.ProseMirror_strong]:text-white [&_.ProseMirror_em]:text-white/70 [&_.ProseMirror_.is-editor-empty:first-child::before]:content-[attr(data-placeholder)] [&_.ProseMirror_.is-editor-empty:first-child::before]:text-white/20 [&_.ProseMirror_.is-editor-empty:first-child::before]:float-left [&_.ProseMirror_.is-editor-empty:first-child::before]:h-0 [&_.ProseMirror_.is-editor-empty:first-child::before]:pointer-events-none"
            onClick={() => editor?.commands.focus()}
          >
            <EditorContent editor={editor} />
          </div>
        </div>
      </div>

      {/* Actions */}
      <div className="border-t border-white/[0.05] px-4 py-3 flex items-center gap-2">
        <button
          onClick={onAddChild}
          className="flex items-center gap-1.5 rounded-lg border border-violet-500/[0.22] bg-violet-500/10 px-3 py-1.5 text-[12px] font-semibold text-violet-300 hover:bg-violet-500/20 transition-colors"
        >
          <Plus className="w-3.5 h-3.5" />
          Child
        </button>
        {!isRoot && (
          <button
            onClick={onDelete}
            className="ml-auto flex items-center gap-1.5 rounded-lg border border-rose-500/[0.18] bg-rose-500/[0.07] px-3 py-1.5 text-[12px] font-semibold text-rose-300/80 hover:bg-rose-500/15 transition-colors"
          >
            <Trash2 className="w-3.5 h-3.5" />
            Löschen
          </button>
        )}
      </div>
    </aside>
  );
}

// ─── Canvas ───────────────────────────────────────────────────────────────────

interface CanvasProps {
  map: MindMap;
  selectedId: string | null;
  editingId: string | null;
  editingTitle: string;
  onSelect: (id: string | null) => void;
  onEditStart: (id: string) => void;
  onEditChange: (v: string) => void;
  onEditCommit: () => void;
  onMoveNode: (id: string, pos: DrawingPoint) => void;
  onAddChild: (parentId: string) => void;
  drawMode: DrawMode;
  drawColor: NodeColor;
  onCreateDrawing: (d: MindDrawing) => void;
}

function MindMapCanvas({
  map, selectedId, editingId, editingTitle,
  onSelect, onEditStart, onEditChange, onEditCommit,
  onMoveNode, onAddChild, drawMode, drawColor, onCreateDrawing,
}: CanvasProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const positions = computeLayout(map);
  const { width, height } = canvasSize(positions);

  // Drawing state
  const [draftDrawing, setDraftDrawing] = useState<MindDrawing | null>(null);
  const [isDrawing, setIsDrawing] = useState(false);

  // Node drag state
  const [dragNodeId, setDragNodeId] = useState<string | null>(null);
  const [dragPosition, setDragPosition] = useState<DrawingPoint | null>(null);
  const dragOffsetRef = useRef<DrawingPoint | null>(null);
  const dragMovedRef = useRef(false);

  // Pan state
  const panningRef = useRef(false);
  const panStartRef = useRef<{ cx: number; cy: number; sl: number; st: number } | null>(null);
  const panMovedRef = useRef(false);
  const [isPanning, setIsPanning] = useState(false);

  const resolvedPositions = dragNodeId && dragPosition
    ? { ...positions, [dragNodeId]: dragPosition }
    : positions;

  const connections: { parentId: string; childId: string }[] = [];
  for (const node of Object.values(map.nodes)) {
    for (const cid of node.children) connections.push({ parentId: node.id, childId: cid });
  }

  const getPoint = useCallback((clientX: number, clientY: number): DrawingPoint | null => {
    const c = containerRef.current;
    if (!c) return null;
    const r = c.getBoundingClientRect();
    return { x: clientX - r.left + c.scrollLeft, y: clientY - r.top + c.scrollTop };
  }, []);

  // ── Pan handlers (container background) ─────────────────────────────────
  const handleContainerPointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    if (drawMode !== "select") return;
    if (e.button !== 0) return;
    panningRef.current = true;
    panMovedRef.current = false;
    setIsPanning(true);
    const c = containerRef.current;
    panStartRef.current = { cx: e.clientX, cy: e.clientY, sl: c?.scrollLeft || 0, st: c?.scrollTop || 0 };
    e.currentTarget.setPointerCapture(e.pointerId);
  }, [drawMode]);

  const handleContainerPointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    if (!panningRef.current || !panStartRef.current) return;
    const dx = e.clientX - panStartRef.current.cx;
    const dy = e.clientY - panStartRef.current.cy;
    if (Math.abs(dx) > 4 || Math.abs(dy) > 4) panMovedRef.current = true;
    if (containerRef.current) {
      containerRef.current.scrollLeft = panStartRef.current.sl - dx;
      containerRef.current.scrollTop = panStartRef.current.st - dy;
    }
  }, []);

  const handleContainerPointerUp = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    if (!panningRef.current) return;
    panningRef.current = false;
    setIsPanning(false);
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch { /* noop */ }
    if (!panMovedRef.current) onSelect(null);
    panMovedRef.current = false;
    panStartRef.current = null;
  }, [onSelect]);

  // ── Drawing handlers (SVG) ───────────────────────────────────────────────
  const handleSvgPointerDown = useCallback((e: React.PointerEvent<SVGSVGElement>) => {
    if (drawMode === "select") return;
    const p = getPoint(e.clientX, e.clientY);
    if (!p) return;
    e.preventDefault(); e.stopPropagation();
    const draft: MindDrawing = drawMode === "sketch"
      ? { id: uid(), type: "sketch", color: drawColor, points: [p] }
      : { id: uid(), type: drawMode, color: drawColor, start: p, end: p };
    setDraftDrawing(draft);
    setIsDrawing(true);
    e.currentTarget.setPointerCapture(e.pointerId);
  }, [drawColor, drawMode, getPoint]);

  const handleSvgPointerMove = useCallback((e: React.PointerEvent<SVGSVGElement>) => {
    if (!isDrawing || !draftDrawing) return;
    const p = getPoint(e.clientX, e.clientY);
    if (!p) return;
    setDraftDrawing((cur) => {
      if (!cur) return cur;
      if (cur.type === "sketch") return { ...cur, points: [...cur.points, p] };
      return { ...cur, end: p };
    });
  }, [draftDrawing, getPoint, isDrawing]);

  const finishDrawing = useCallback((ptId?: number, tgt?: EventTarget & SVGSVGElement) => {
    if (ptId !== undefined && tgt?.hasPointerCapture(ptId)) tgt.releasePointerCapture(ptId);
    if (draftDrawing && (
      (draftDrawing.type === "sketch" && draftDrawing.points.length > 1) ||
      (draftDrawing.type !== "sketch" && (Math.abs(draftDrawing.start.x - draftDrawing.end.x) > 4 || Math.abs(draftDrawing.start.y - draftDrawing.end.y) > 4))
    )) onCreateDrawing(draftDrawing);
    setDraftDrawing(null);
    setIsDrawing(false);
  }, [draftDrawing, onCreateDrawing]);

  // ── Node drag handlers ───────────────────────────────────────────────────
  const handleNodePointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>, nodeId: string) => {
    if (drawMode !== "select") return;
    e.stopPropagation();
    const p = getPoint(e.clientX, e.clientY);
    const origin = positions[nodeId];
    if (!p || !origin) return;
    dragOffsetRef.current = { x: p.x - origin.x, y: p.y - origin.y };
    dragMovedRef.current = false;
    setDragNodeId(nodeId);
    setDragPosition(origin);
    e.currentTarget.setPointerCapture(e.pointerId);
  }, [drawMode, getPoint, positions]);

  const handleNodePointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    if (!dragNodeId || !dragOffsetRef.current) return;
    const p = getPoint(e.clientX, e.clientY);
    if (!p) return;
    dragMovedRef.current = true;
    setDragPosition({ x: p.x - dragOffsetRef.current.x, y: p.y - dragOffsetRef.current.y });
  }, [dragNodeId, getPoint]);

  const finishNodeDrag = useCallback((ptId?: number, tgt?: EventTarget & HTMLDivElement) => {
    if (ptId !== undefined && tgt?.hasPointerCapture(ptId)) tgt.releasePointerCapture(ptId);
    if (dragNodeId && dragPosition && dragMovedRef.current) onMoveNode(dragNodeId, dragPosition);
    setDragNodeId(null);
    setDragPosition(null);
    dragOffsetRef.current = null;
    dragMovedRef.current = false;
  }, [dragNodeId, dragPosition, onMoveNode]);

  const handleNodePointerUp = useCallback((e: React.PointerEvent<HTMLDivElement>, nodeId: string) => {
    const moved = dragMovedRef.current;
    finishNodeDrag(e.pointerId, e.currentTarget);
    if (!moved) onSelect(nodeId);
  }, [finishNodeDrag, onSelect]);

  return (
    <div
      ref={containerRef}
      className={cn("relative w-full h-full overflow-auto select-none",
        drawMode === "select"
          ? isPanning ? "cursor-grabbing" : "cursor-grab"
          : "cursor-crosshair"
      )}
      onPointerDown={handleContainerPointerDown}
      onPointerMove={handleContainerPointerMove}
      onPointerUp={handleContainerPointerUp}
    >
      <div style={{ width, height, position: "relative" }}>
        {/* SVG layer */}
        <svg
          className={cn("absolute inset-0", drawMode === "select" ? "pointer-events-none" : "z-20")}
          width={width} height={height}
          onPointerDown={handleSvgPointerDown}
          onPointerMove={handleSvgPointerMove}
          onPointerUp={(e) => { if (isDrawing) { e.preventDefault(); finishDrawing(e.pointerId, e.currentTarget); } }}
          onPointerLeave={(e) => { if (isDrawing) finishDrawing(e.pointerId, e.currentTarget); }}
        >
          {connections.map(({ parentId, childId }) => {
            const p = resolvedPositions[parentId], c = resolvedPositions[childId];
            const pn = map.nodes[parentId];
            if (!p || !c || !pn) return null;
            return <Connection key={`${parentId}-${childId}`} x1={p.x} y1={p.y} x2={c.x} y2={c.y} color={pn.color} />;
          })}

          {map.drawings.map((d) => {
            const stroke = STROKE_COLORS[d.color];
            if (d.type === "sketch") return (
              <path key={d.id} d={pointsToPath(d.points)} fill="none" stroke={stroke} strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round" opacity={0.95} />
            );
            return (
              <g key={d.id}>
                <line x1={d.start.x} y1={d.start.y} x2={d.end.x} y2={d.end.y} stroke={stroke} strokeWidth={2.5} strokeLinecap="round" opacity={0.95} />
                {d.type === "arrow" && <polygon points={arrowHead(d.start, d.end)} fill={stroke} opacity={0.95} />}
              </g>
            );
          })}

          {draftDrawing && (draftDrawing.type === "sketch" ? (
            <path d={pointsToPath(draftDrawing.points)} fill="none" stroke={STROKE_COLORS[draftDrawing.color]} strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round" opacity={0.80} />
          ) : (
            <g>
              <line x1={draftDrawing.start.x} y1={draftDrawing.start.y} x2={draftDrawing.end.x} y2={draftDrawing.end.y} stroke={STROKE_COLORS[draftDrawing.color]} strokeWidth={2.5} strokeLinecap="round" opacity={0.80} />
              {draftDrawing.type === "arrow" && <polygon points={arrowHead(draftDrawing.start, draftDrawing.end)} fill={STROKE_COLORS[draftDrawing.color]} opacity={0.80} />}
            </g>
          ))}
        </svg>

        {/* Node layer */}
        {Object.values(map.nodes).map((node) => {
          const pos = resolvedPositions[node.id];
          if (!pos) return null;
          const s = COLOR_STYLES[node.color];
          const isSelected = selectedId === node.id;
          const isEditing = editingId === node.id;
          const preview = node.description ? stripHtml(node.description).slice(0, 50) : "";

          return (
            <div
              key={node.id}
              className={cn(
                "absolute group/node rounded-2xl border transition-all duration-150",
                drawMode === "select" ? "cursor-pointer" : "pointer-events-none",
                s.border, s.bg,
                isSelected && `ring-2 ${s.ring} scale-[1.04]`,
                !isSelected && "hover:scale-[1.02]",
              )}
              style={{
                left: pos.x, top: pos.y,
                width: NODE_W,
                minHeight: NODE_H,
                padding: "10px 14px",
                boxShadow: isSelected ? `0 0 20px ${s.glow}` : undefined,
                zIndex: isSelected ? 10 : 1,
              }}
              onPointerDown={(e) => handleNodePointerDown(e, node.id)}
              onPointerMove={handleNodePointerMove}
              onPointerUp={(e) => handleNodePointerUp(e, node.id)}
              onPointerCancel={(e) => finishNodeDrag(e.pointerId, e.currentTarget)}
              onDoubleClick={(e) => {
                if (drawMode !== "select" || dragMovedRef.current) return;
                e.stopPropagation();
                onEditStart(node.id);
              }}
            >
              <div className="flex items-start gap-2">
                <div className={cn("w-2 h-2 rounded-full flex-shrink-0 mt-1", s.dot)} />
                <div className="flex-1 min-w-0">
                  {isEditing ? (
                    <input
                      autoFocus
                      value={editingTitle}
                      onChange={(e) => onEditChange(e.target.value)}
                      onBlur={onEditCommit}
                      onKeyDown={(e) => { if (e.key === "Enter" || e.key === "Escape") onEditCommit(); }}
                      className="bg-transparent border-none outline-none text-[13px] font-semibold text-white w-full"
                      onClick={(e) => e.stopPropagation()}
                    />
                  ) : (
                    <span className={cn("text-[13px] font-semibold block truncate", s.text)}>
                      {node.title}
                    </span>
                  )}
                  {preview && !isEditing && (
                    <span className="text-[11px] text-white/30 block truncate mt-0.5 leading-tight">
                      {preview}
                    </span>
                  )}
                  {!preview && node.children.length > 0 && (
                    <div className="flex items-center gap-1 mt-0.5">
                      <ChevronRight className="w-2.5 h-2.5 text-white/20" />
                      <span className="text-[10px] text-white/25">{node.children.length}</span>
                    </div>
                  )}
                </div>
                {node.description && (
                  <AlignLeft className="w-3 h-3 text-white/20 flex-shrink-0 mt-0.5" />
                )}
              </div>

              {drawMode === "select" && (
                <button
                  className="absolute -right-3 top-1/2 -translate-y-1/2 w-6 h-6 rounded-full border border-white/[0.14] bg-[#0a0a0d] flex items-center justify-center text-white/35 hover:text-white hover:border-white/[0.28] opacity-0 group-hover/node:opacity-100 transition-all z-20"
                  onPointerDown={(e) => e.stopPropagation()}
                  onClick={(e) => { e.stopPropagation(); onAddChild(node.id); }}
                  title="Child hinzufügen"
                >
                  <Plus className="w-3 h-3" />
                </button>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Modals ───────────────────────────────────────────────────────────────────

function Modal({ children, onClose }: { children: React.ReactNode; onClose: () => void }) {
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" onClick={onClose}>
      <div className="w-full max-w-md" onClick={(e) => e.stopPropagation()}>{children}</div>
    </div>
  );
}

function NewMapModal({ onSave, onClose }: { onSave: (t: string) => void; onClose: () => void }) {
  const [title, setTitle] = useState("");
  return (
    <Modal onClose={onClose}>
      <div className="rounded-[28px] border border-violet-500/[0.20] bg-[#0d0b18] p-7 shadow-2xl">
        <div className="flex items-center justify-between mb-5">
          <h3 className="text-lg font-bold text-white">Neue Mind Map</h3>
          <button onClick={onClose} className="text-white/40 hover:text-white/70 transition-colors"><X className="w-5 h-5" /></button>
        </div>
        <input
          autoFocus placeholder="Titel der Map…"
          value={title} onChange={(e) => setTitle(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && title.trim()) onSave(title.trim()); if (e.key === "Escape") onClose(); }}
          className="w-full rounded-xl border border-violet-500/[0.18] bg-violet-500/[0.06] px-4 py-3 text-sm text-white placeholder:text-white/30 outline-none focus:border-violet-500/[0.35] transition-colors"
        />
        <div className="flex gap-3 mt-5">
          <button onClick={onClose} className="flex-1 rounded-xl border border-white/[0.08] bg-white/[0.03] py-2.5 text-sm text-white/60 hover:text-white/80 transition-colors">Abbrechen</button>
          <button disabled={!title.trim()} onClick={() => title.trim() && onSave(title.trim())} className="flex-1 rounded-xl bg-violet-600/80 hover:bg-violet-600 border border-violet-500/[0.30] py-2.5 text-sm font-semibold text-white transition-colors disabled:opacity-40">Erstellen</button>
        </div>
      </div>
    </Modal>
  );
}

function AddChildModal({ onSave, onClose, defaultColor }: { onSave: (t: string, c: NodeColor) => void; onClose: () => void; defaultColor: NodeColor }) {
  const [title, setTitle] = useState("");
  const [color, setColor] = useState<NodeColor>(defaultColor);
  return (
    <Modal onClose={onClose}>
      <div className="rounded-[28px] border border-violet-500/[0.20] bg-[#0d0b18] p-7 shadow-2xl">
        <div className="flex items-center justify-between mb-5">
          <h3 className="text-lg font-bold text-white">Child Node</h3>
          <button onClick={onClose} className="text-white/40 hover:text-white/70 transition-colors"><X className="w-5 h-5" /></button>
        </div>
        <input
          autoFocus placeholder="Titel…"
          value={title} onChange={(e) => setTitle(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && title.trim()) onSave(title.trim(), color); if (e.key === "Escape") onClose(); }}
          className="w-full rounded-xl border border-violet-500/[0.18] bg-violet-500/[0.06] px-4 py-3 text-sm text-white placeholder:text-white/30 outline-none focus:border-violet-500/[0.35] transition-colors mb-4"
        />
        <div className="flex items-center gap-3 mb-5">
          {NODE_COLORS.map((c) => (
            <button key={c} onClick={() => setColor(c)}
              className={cn("w-7 h-7 rounded-full transition-all", COLOR_STYLES[c].dot, color === c ? "ring-2 ring-white/40 scale-110" : "opacity-55 hover:opacity-85")}
            />
          ))}
        </div>
        <div className="flex gap-3">
          <button onClick={onClose} className="flex-1 rounded-xl border border-white/[0.08] bg-white/[0.03] py-2.5 text-sm text-white/60 hover:text-white/80 transition-colors">Abbrechen</button>
          <button disabled={!title.trim()} onClick={() => title.trim() && onSave(title.trim(), color)} className="flex-1 rounded-xl bg-violet-600/80 hover:bg-violet-600 border border-violet-500/[0.30] py-2.5 text-sm font-semibold text-white transition-colors disabled:opacity-40">Hinzufügen</button>
        </div>
      </div>
    </Modal>
  );
}

// ─── Main Page ────────────────────────────────────────────────────────────────

function MindMapPage() {
  const { maps, loading: mapsLoading, createMap: dbCreateMap, updateMap: dbUpdateMap, deleteMap: dbDeleteMap } = useMindMaps();
  const [activeMindMapId, setActiveMindMapId] = useState<string | null>(null);
  const [selectedNodeId, setSelectedNodeId] = useState<string | null | undefined>(undefined);
  const [editingNodeId, setEditingNodeId] = useState<string | null>(null);
  const [editingTitle, setEditingTitle] = useState("");
  const [showNewMapModal, setShowNewMapModal] = useState(false);
  const [showAddChildModal, setShowAddChildModal] = useState(false);
  const [drawMode, setDrawMode] = useState<DrawMode>("select");
  const [drawColor, setDrawColor] = useState<NodeColor>("violet");
  const [mapError, setMapError] = useState<string | null>(null);
  const [mapsListOpen, setMapsListOpen] = useState(false);

  const currentMindMapId = activeMindMapId ?? maps[0]?.id ?? null;
  const activeMap = maps.find((m) => m.id === currentMindMapId) ?? null;
  const currentSelectedNodeId = selectedNodeId === undefined ? activeMap?.rootId ?? null : selectedNodeId;
  const selectedNode = activeMap && currentSelectedNodeId ? activeMap.nodes[currentSelectedNodeId] ?? null : null;

  // Tab shortcut
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key !== "Tab" || !currentSelectedNodeId || !activeMap) return;
      const tag = (e.target as HTMLElement).tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || (e.target as HTMLElement).contentEditable === "true") return;
      e.preventDefault();
      setShowAddChildModal(true);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [currentSelectedNodeId, activeMap]);

  // Debounced save helper to avoid hammering Supabase on every node drag
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const handleMapActionError = useCallback((error: unknown, fallback: string) => {
    const message =
      error instanceof Error && error.message
        ? error.message
        : error && typeof error === "object" && "message" in error && typeof (error as { message?: unknown }).message === "string"
        ? (error as { message: string }).message
        : fallback;

    setMapError(message);
  }, []);

  const updateMap = useCallback((updater: (m: MindMap) => MindMap) => {
    if (!currentMindMapId) return;
    const current = maps.find((m) => m.id === currentMindMapId);
    if (!current) return;
    const updated = updater(current);
    setMapError(null);
    if (saveTimer.current) clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => {
      void dbUpdateMap(currentMindMapId, { nodes: updated.nodes, drawings: updated.drawings }).catch((error: unknown) => {
        handleMapActionError(error, "Mind map could not be saved.");
      });
    }, 400);
    // Optimistic update via the hook's local state isn't available here,
    // but dbUpdateMap already updates local state immediately
    void dbUpdateMap(currentMindMapId, { nodes: updated.nodes, drawings: updated.drawings }).catch((error: unknown) => {
      handleMapActionError(error, "Mind map could not be saved.");
    });
  }, [currentMindMapId, maps, dbUpdateMap, handleMapActionError]);

  async function handleCreateMap(title: string) {
    try {
      setMapError(null);
      const rootId = uid();
      const rootNode: MindNode = { id: rootId, title, description: "", color: "violet", parentId: null, children: [], manualPosition: null };
      const newMap = await dbCreateMap(title, rootId, rootNode);
      setActiveMindMapId(newMap.id);
      setSelectedNodeId(newMap.rootId);
      setShowNewMapModal(false);
    } catch (error) {
      handleMapActionError(error, "Mind map could not be created.");
    }
  }

  async function handleDeleteMap(mapId: string) {
    try {
      setMapError(null);
      await dbDeleteMap(mapId);
      if (currentMindMapId === mapId) {
        const remaining = maps.filter((m) => m.id !== mapId);
        setActiveMindMapId(remaining[0]?.id ?? null);
        setSelectedNodeId(remaining[0]?.rootId ?? null);
      }
    } catch (error) {
      handleMapActionError(error, "Mind map could not be deleted.");
    }
  }

  function handleAddChild(title: string, color: NodeColor) {
    if (!currentSelectedNodeId || !activeMap) return;
    const childId = uid();
    updateMap((m) => ({
      ...m,
      nodes: {
        ...m.nodes,
        [currentSelectedNodeId]: { ...m.nodes[currentSelectedNodeId], children: [...m.nodes[currentSelectedNodeId].children, childId] },
        [childId]: { id: childId, title, description: "", color, parentId: currentSelectedNodeId, children: [], manualPosition: null },
      },
    }));
    setShowAddChildModal(false);
  }

  function handleDeleteNode() {
    if (!currentSelectedNodeId || !activeMap) return;
    const node = activeMap.nodes[currentSelectedNodeId];
    if (!node?.parentId) return;
    function collectIds(id: string): string[] {
      const n = activeMap!.nodes[id];
      return n ? [id, ...n.children.flatMap(collectIds)] : [];
    }
    const toDelete = new Set(collectIds(currentSelectedNodeId));
    updateMap((m) => {
      const newNodes = { ...m.nodes };
      toDelete.forEach((id) => delete newNodes[id]);
      if (node.parentId && newNodes[node.parentId]) {
        newNodes[node.parentId] = { ...newNodes[node.parentId], children: newNodes[node.parentId].children.filter((c) => c !== currentSelectedNodeId) };
      }
      return { ...m, nodes: newNodes };
    });
    setSelectedNodeId(null);
  }

  function handleUpdateTitle(nodeId: string, title: string) {
    updateMap((m) => ({ ...m, nodes: { ...m.nodes, [nodeId]: { ...m.nodes[nodeId], title } } }));
  }

  function handleUpdateDescription(nodeId: string, description: string) {
    updateMap((m) => ({ ...m, nodes: { ...m.nodes, [nodeId]: { ...m.nodes[nodeId], description } } }));
  }

  function handleUpdateColor(nodeId: string, color: NodeColor) {
    updateMap((m) => ({ ...m, nodes: { ...m.nodes, [nodeId]: { ...m.nodes[nodeId], color } } }));
  }

  function handleEditStart(nodeId: string) {
    const node = activeMap?.nodes[nodeId];
    if (!node) return;
    setEditingNodeId(nodeId);
    setEditingTitle(node.title);
    setSelectedNodeId(nodeId);
  }

  function handleEditCommit() {
    if (!editingNodeId || !editingTitle.trim()) { setEditingNodeId(null); return; }
    handleUpdateTitle(editingNodeId, editingTitle.trim());
    setEditingNodeId(null);
  }

  const nodeCount = activeMap ? Object.keys(activeMap.nodes).length : 0;
  const drawingCount = activeMap?.drawings.length ?? 0;

  return (
    <div className="flex flex-col" style={{ height: "calc(100dvh - 72px)" }}>

      {/* ── Header ──────────────────────────────────────────────────────── */}
      <div className="flex items-center justify-between px-1 py-3 shrink-0 border-b border-white/[0.05]">
        <div className="flex items-center gap-2.5">
          <button
            onClick={() => setMapsListOpen(true)}
            className="rounded-lg p-1.5 text-white/40 transition-colors hover:text-white/70 md:hidden"
            title="Maps"
          >
            <List className="w-4 h-4" />
          </button>
          <div className="w-7 h-7 rounded-lg border border-violet-500/[0.20] bg-violet-500/10 flex items-center justify-center">
            <Brain className="w-3.5 h-3.5 text-violet-300" />
          </div>
          <span className="text-[15px] font-semibold tracking-tight">Mind Map</span>
          {activeMap && <span className="text-[12px] text-white/30">{nodeCount} Nodes</span>}
        </div>

        {/* Toolbar */}
        <div className="flex items-center gap-2">
          {/* Draw modes */}
          <div className="flex items-center gap-0.5 rounded-xl border border-white/[0.06] bg-white/[0.02] p-0.5">
            {(["select", "sketch", "line", "arrow"] as DrawMode[]).map((mode) => (
              <button
                key={mode}
                onClick={() => setDrawMode(mode)}
                className={cn(
                  "rounded-lg px-2.5 py-1 text-[11px] font-semibold transition-colors capitalize",
                  drawMode === mode
                    ? "bg-white/[0.09] text-white/90"
                    : "text-white/35 hover:text-white/65"
                )}
              >{mode}</button>
            ))}
          </div>

          {/* Draw colors */}
          <div className="flex items-center gap-1.5 rounded-xl border border-white/[0.06] bg-white/[0.02] px-2.5 py-1.5">
            {NODE_COLORS.map((c) => (
              <button
                key={c}
                onClick={() => setDrawColor(c)}
                className={cn("w-4 h-4 rounded-full transition-all", COLOR_STYLES[c].dot, drawColor === c ? "scale-125 ring-2 ring-white/35" : "opacity-50 hover:opacity-80")}
              />
            ))}
          </div>

          {activeMap && drawingCount > 0 && (
            <>
              <button
                onClick={() => updateMap((m) => ({ ...m, drawings: m.drawings.slice(0, -1) }))}
                className="rounded-lg border border-sky-500/[0.18] bg-sky-500/[0.07] px-2.5 py-1.5 text-[11px] font-medium text-sky-300 hover:bg-sky-500/15 transition-colors"
              >Undo</button>
              <button
                onClick={() => updateMap((m) => ({ ...m, drawings: [] }))}
                className="rounded-lg border border-rose-500/[0.18] bg-rose-500/[0.07] px-2.5 py-1.5 text-[11px] font-medium text-rose-300 hover:bg-rose-500/15 transition-colors"
              >Clear</button>
            </>
          )}

          <button
            onClick={() => setShowNewMapModal(true)}
            className="flex items-center gap-1.5 rounded-xl border border-violet-500/[0.22] bg-violet-500/10 px-3 py-1.5 text-[12px] font-semibold text-violet-300 hover:bg-violet-500/20 transition-colors"
          >
            <Plus className="w-3.5 h-3.5" /> Neue Map
          </button>
        </div>
      </div>

      {mapError && (
        <div className="mx-1 mt-3 rounded-xl border border-rose-500/[0.22] bg-rose-500/[0.08] px-4 py-3 text-sm text-rose-200">
          {mapError}
        </div>
      )}

      {/* ── Body ──────────────────────────────────────────────────────────── */}
      <div className="flex flex-1 min-h-0">

        {/* Left: maps list */}
        {mapsListOpen && (
          <div
            className="fixed inset-0 z-40 bg-black/50 md:hidden"
            onClick={() => setMapsListOpen(false)}
          />
        )}
        <aside className={cn(
          "flex-col overflow-hidden bg-[#0a0a0d] md:static md:z-auto md:flex md:w-[200px] md:shrink-0 md:border-r md:border-white/[0.05]",
          mapsListOpen ? "fixed inset-y-0 left-0 z-50 flex w-[240px]" : "hidden"
        )}>
          <div className="flex items-center justify-between px-3 py-2 md:hidden">
            <span className="text-[11px] font-bold uppercase tracking-[0.14em] text-white/30">Maps</span>
            <button onClick={() => setMapsListOpen(false)} className="text-white/30 hover:text-white/60 transition-colors">
              <X className="w-4 h-4" />
            </button>
          </div>
          <div className="flex-1 overflow-y-auto py-2">
            {mapsLoading ? (
              <div className="px-4 py-8 text-center">
                <div className="w-4 h-4 rounded-full border-2 border-violet-500/30 border-t-violet-400 animate-spin mx-auto" />
              </div>
            ) : maps.length === 0 ? (
              <div className="px-4 py-8 text-center">
                <Sparkles className="w-5 h-5 text-white/15 mx-auto mb-2" />
                <p className="text-[12px] text-white/25">Noch keine Maps</p>
                <button onClick={() => setShowNewMapModal(true)} className="mt-3 text-[12px] text-violet-400 hover:text-violet-300 transition-colors">
                  + Erste Map erstellen
                </button>
              </div>
            ) : maps.map((m) => (
              <div
                key={m.id}
                className={cn(
                  "group mx-2 mb-1 px-3 py-2.5 rounded-xl cursor-pointer transition-all",
                  m.id === currentMindMapId
                    ? "border border-violet-500/[0.20] bg-violet-500/[0.07]"
                    : "border border-transparent hover:border-white/[0.06] hover:bg-white/[0.03]"
                )}
                onClick={() => { setActiveMindMapId(m.id); setSelectedNodeId(m.rootId); setMapsListOpen(false); }}
              >
                <div className="flex items-center justify-between gap-2">
                  <span className={cn("text-[13px] font-medium truncate", m.id === currentMindMapId ? "text-violet-200" : "text-white/55")}>
                    {m.title}
                  </span>
                  <button
                    onClick={(e) => { e.stopPropagation(); void handleDeleteMap(m.id); }}
                    className="opacity-0 group-hover:opacity-100 text-white/25 hover:text-rose-400 transition-all"
                  ><X className="w-3.5 h-3.5" /></button>
                </div>
                <p className="text-[10px] text-white/25 mt-0.5">{Object.keys(m.nodes).length} Nodes</p>
              </div>
            ))}
          </div>
        </aside>

        {/* Center: canvas */}
        <div className="flex-1 min-w-0 overflow-hidden">
          {!activeMap ? (
            <div className="flex flex-col items-center justify-center h-full gap-4">
              <div className="w-14 h-14 rounded-2xl border border-violet-500/[0.14] bg-violet-500/[0.06] flex items-center justify-center">
                <Brain className="w-6 h-6 text-violet-400/50" />
              </div>
              <p className="text-[14px] text-white/35 text-center">Keine Map ausgewählt<br /><span className="text-[12px] text-white/20">Erstelle eine neue Map links</span></p>
              <button onClick={() => setShowNewMapModal(true)} className="flex items-center gap-2 px-4 py-2 rounded-xl border border-violet-500/[0.20] bg-violet-500/10 text-violet-300 text-[13px] font-medium hover:bg-violet-500/20 transition-colors">
                <Plus className="w-3.5 h-3.5" /> Neue Mind Map
              </button>
            </div>
          ) : (
            <MindMapCanvas
              map={activeMap}
              selectedId={currentSelectedNodeId}
              editingId={editingNodeId}
              editingTitle={editingTitle}
              onSelect={setSelectedNodeId}
              onEditStart={handleEditStart}
              onEditChange={setEditingTitle}
              onEditCommit={handleEditCommit}
              onMoveNode={(id, pos) => updateMap((m) => ({ ...m, nodes: { ...m.nodes, [id]: { ...m.nodes[id], manualPosition: pos } } }))}
              onAddChild={(parentId) => { setSelectedNodeId(parentId); setShowAddChildModal(true); }}
              drawMode={drawMode}
              drawColor={drawColor}
              onCreateDrawing={(d) => updateMap((m) => ({ ...m, drawings: [...m.drawings, d] }))}
            />
          )}
        </div>

        {/* Right: node detail panel */}
        {selectedNode && (
          <NodeDetailPanel
            node={selectedNode}
            isRoot={!selectedNode.parentId}
            onUpdateTitle={handleUpdateTitle}
            onUpdateDescription={handleUpdateDescription}
            onUpdateColor={handleUpdateColor}
            onAddChild={() => setShowAddChildModal(true)}
            onDelete={() => handleDeleteNode()}
            onClose={() => setSelectedNodeId(null)}
          />
        )}
      </div>

      {showNewMapModal && <NewMapModal onSave={(t) => void handleCreateMap(t)} onClose={() => setShowNewMapModal(false)} />}
      {showAddChildModal && selectedNode && (
        <AddChildModal onSave={handleAddChild} onClose={() => setShowAddChildModal(false)} defaultColor={selectedNode.color} />
      )}
    </div>
  );
}

export default dynamic(() => Promise.resolve(MindMapPage), { ssr: false });
