import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { api, fileUrl } from './api.js';
import Netdisk, { FilePreview } from './Netdisk.jsx';

const COLORS = ['#27d8ff', '#36f0b0', '#f2b85f', '#9d73ff', '#ff7a9c'];
const uid = () => `n${Math.random().toString(36).slice(2, 8)}`;

// Mind-map whiteboard. Nodes are draggable; nodes can link to a netdisk file,
// which is previewed (and openable to edit) in the side panel.
export default function Whiteboard({ boardId, onClose }) {
  const [name, setName] = useState('');
  const [nodes, setNodes] = useState([]);
  const [edges, setEdges] = useState([]);
  const [sel, setSel] = useState(null);
  const [connectFrom, setConnectFrom] = useState(null);
  const [picking, setPicking] = useState(false);
  const [previewFile, setPreviewFile] = useState(null);
  const [dirty, setDirty] = useState(false);
  const [saved, setSaved] = useState('');
  const svgRef = useRef(null);
  const drag = useRef(null);

  useEffect(() => {
    api(`/boards/${boardId}`).then((d) => {
      setName(d.board.name);
      let data = {}; try { data = JSON.parse(d.board.data || '{}'); } catch {}
      setNodes(data.nodes || []); setEdges(data.edges || []);
    }).catch(() => {});
  }, [boardId]);

  const save = useCallback(async () => {
    const links = nodes.filter((n) => n.link).map((n) => ({ nodeId: n.id, ...n.link }));
    await api(`/boards/${boardId}`, { method: 'PATCH', body: { name, data: JSON.stringify({ nodes, edges }), links } });
    setDirty(false); setSaved('saved ' + new Date().toLocaleTimeString()); setTimeout(() => setSaved(''), 2500);
  }, [boardId, name, nodes, edges]);

  // autosave when dirty
  useEffect(() => { if (!dirty) return; const t = setTimeout(save, 1200); return () => clearTimeout(t); }, [dirty, save]);
  const mutate = (fn) => { fn(); setDirty(true); };

  const svgPoint = (e) => {
    const r = svgRef.current.getBoundingClientRect();
    return { x: e.clientX - r.left + svgRef.current.parentNode.scrollLeft, y: e.clientY - r.top + svgRef.current.parentNode.scrollTop };
  };
  const addNode = (e) => {
    if (e.target !== svgRef.current) return;
    const p = svgPoint(e);
    const n = { id: uid(), x: p.x, y: p.y, text: 'idea', color: COLORS[nodes.length % COLORS.length] };
    mutate(() => setNodes((ns) => [...ns, n])); setSel(n.id);
  };
  const onNodeDown = (e, n) => {
    e.stopPropagation();
    if (connectFrom && connectFrom !== n.id) {
      mutate(() => setEdges((es) => es.some((x) => x.from === connectFrom && x.to === n.id) ? es : [...es, { from: connectFrom, to: n.id }]));
      setConnectFrom(null); return;
    }
    setSel(n.id);
    const p = svgPoint(e);
    drag.current = { id: n.id, dx: p.x - n.x, dy: p.y - n.y, moved: false };
  };
  const onMove = (e) => {
    if (!drag.current) return;
    const p = svgPoint(e); drag.current.moved = true;
    setNodes((ns) => ns.map((n) => n.id === drag.current.id ? { ...n, x: p.x - drag.current.dx, y: p.y - drag.current.dy } : n));
  };
  const onUp = () => { if (drag.current?.moved) setDirty(true); drag.current = null; };

  const selNode = nodes.find((n) => n.id === sel) || null;
  const setSelField = (k, v) => mutate(() => setNodes((ns) => ns.map((n) => n.id === sel ? { ...n, [k]: v } : n)));
  const delSel = () => { if (!sel) return; mutate(() => { setNodes((ns) => ns.filter((n) => n.id !== sel)); setEdges((es) => es.filter((x) => x.from !== sel && x.to !== sel)); }); setSel(null); };
  const linkFile = (f) => { setSelField('link', { type: 'file', id: f.id, label: f.name, mime: f.mime, kind: f.kind }); setPicking(false); };
  const openLinked = (n) => { if (n.link?.type === 'file') api(`/files/${n.link.id}`).then((d) => setPreviewFile(d.file)).catch(() => {}); };

  return (
    <div className="wb">
      <div className="wb-toolbar">
        <button className="cs-btn" onClick={onClose}>← Boards</button>
        <input className="wb-name" value={name} onChange={(e) => { setName(e.target.value); setDirty(true); }} />
        <span className="wb-hint">double-click canvas = new node · drag to move</span>
        <button className={`cs-btn${connectFrom ? ' cs-primary' : ''}`} onClick={() => setConnectFrom(connectFrom ? null : sel)} disabled={!sel && !connectFrom}>{connectFrom ? 'click target…' : 'Connect'}</button>
        <button className="cs-btn" onClick={delSel} disabled={!sel}>Delete</button>
        <button className="cs-btn" onClick={() => setPicking(true)} disabled={!sel}>Link file</button>
        <button className="cs-btn cs-primary" onClick={save}>Save</button>
        <span className="wb-saved">{saved || (dirty ? 'unsaved…' : '')}</span>
      </div>
      <div className="wb-body">
        <div className="wb-canvas-wrap">
          <svg ref={svgRef} className="wb-canvas" width="2400" height="1600" onDoubleClick={addNode} onMouseMove={onMove} onMouseUp={onUp} onMouseLeave={onUp}>
            {edges.map((ed, i) => {
              const a = nodes.find((n) => n.id === ed.from), b = nodes.find((n) => n.id === ed.to);
              if (!a || !b) return null;
              return <line key={i} x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke="#3a4a6a" strokeWidth="2" />;
            })}
            {nodes.map((n) => (
              <g key={n.id} transform={`translate(${n.x},${n.y})`} onMouseDown={(e) => onNodeDown(e, n)} onDoubleClick={(e) => { e.stopPropagation(); openLinked(n); }} style={{ cursor: 'grab' }}>
                <rect x={-Math.max(46, n.text.length * 4.6)} y={-20} width={Math.max(92, n.text.length * 9.2)} height={40} rx={10}
                  fill="#101626" stroke={sel === n.id ? '#fff' : n.color} strokeWidth={sel === n.id ? 3 : 2} />
                <text textAnchor="middle" y={n.link ? -2 : 5} fill="#eaf2ff" fontSize="13" style={{ pointerEvents: 'none' }}>{n.text}</text>
                {n.link && <text textAnchor="middle" y={13} fill={n.color} fontSize="9" style={{ pointerEvents: 'none' }}>🔗 {n.link.label.slice(0, 22)}</text>}
              </g>
            ))}
          </svg>
        </div>
        <aside className="wb-side">
          {selNode ? (
            <>
              <h4>Node</h4>
              <label className="cs-field"><span>Text</span><input value={selNode.text} onChange={(e) => setSelField('text', e.target.value)} /></label>
              <label className="cs-field"><span>Color</span>
                <div className="wb-colors">{COLORS.map((c) => <button key={c} className={selNode.color === c ? 'on' : ''} style={{ background: c }} onClick={() => setSelField('color', c)} />)}</div>
              </label>
              {selNode.link ? (
                <div className="wb-link">
                  <span>🔗 {selNode.link.label}</span>
                  <button className="cs-btn" onClick={() => openLinked(selNode)}>Preview / edit</button>
                  <button className="cs-btn" onClick={() => setSelField('link', undefined)}>Unlink</button>
                </div>
              ) : <button className="cs-btn" onClick={() => setPicking(true)}>Link netdisk file</button>}
            </>
          ) : <p className="cs-hint">Select a node, or double-click the canvas to add one. Link nodes to netdisk files to build a visual map of your docs and assets.</p>}
        </aside>
      </div>
      {picking && (
        <div className="cs-modal" onClick={() => setPicking(false)}>
          <div className="cs-modal-box" onClick={(e) => e.stopPropagation()}>
            <div className="cs-modal-head"><strong>Link a netdisk file to this node</strong><button onClick={() => setPicking(false)}>✕</button></div>
            <Netdisk compact onPick={linkFile} />
          </div>
        </div>
      )}
      {previewFile && (
        <div className="cs-modal" onClick={() => setPreviewFile(null)}>
          <div className="cs-modal-box cs-modal-wide" onClick={(e) => e.stopPropagation()}>
            <div className="cs-modal-head"><strong>{previewFile.name}</strong><button onClick={() => setPreviewFile(null)}>✕</button></div>
            <FilePreview file={previewFile} onChange={() => {}} />
          </div>
        </div>
      )}
    </div>
  );
}
