// LineCanvas — a standalone, read-only station-graph canvas for the line-setup
// workspace (Slice 4). COMPOSITE tier: pure renderer, no fetching. Deliberately a
// SIMPLER cousin of assembly-canvas.jsx — auto-layout only (via window.layoutGraph),
// no node drag, no localStorage. Pan with the wheel, zoom with ⌘/ctrl + wheel,
// Fit recomputes from the layout bounds. See COMPONENTS.md.
//
// Props: { graph, selectedId, onSelect(idOrNull), issuesByNode /* {[id]: count} */, readOnly }

const LC_NODE_W = 208; // card width (world units)
const LC_NODE_H = 76;  // nominal card height, for edge anchors
const LC_MIN_K = 0.35;
const LC_MAX_K = 1.5;

function LineCanvas({ graph, selectedId, onSelect, issuesByNode, readOnly }) {
  const { useRef, useState, useMemo, useEffect, useCallback } = React;
  const containerRef = useRef(null);
  const [view, setView] = useState({ x: 0, y: 0, k: 0.75 });

  const layout = useMemo(() => window.layoutGraph(graph || { nodes: [], edges: [] }), [graph]);
  const nodes = (graph && Array.isArray(graph.nodes)) ? graph.nodes : [];
  const edges = (graph && Array.isArray(graph.edges)) ? graph.edges : [];
  const issues = issuesByNode || {};

  // Fit: scale + offset so the whole graph sits centered in the container.
  const fit = useCallback(() => {
    const el = containerRef.current;
    if (!el) return;
    const rect = el.getBoundingClientRect();
    const w = Math.max(1, layout.width);
    const h = Math.max(1, layout.height);
    const k = Math.max(LC_MIN_K, Math.min(LC_MAX_K, Math.min(rect.width / w, rect.height / h)));
    setView({ k, x: (rect.width - w * k) / 2, y: (rect.height - h * k) / 2 });
  }, [layout.width, layout.height]);

  // Fit once the graph identity changes (new proposal / first render).
  useEffect(() => {
    const t = setTimeout(fit, 30);
    return () => clearTimeout(t);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [graph]);

  // Wheel: pan by default, ⌘/ctrl + wheel = zoom toward the cursor (clamped).
  const onWheel = useCallback((e) => {
    e.preventDefault();
    if (e.ctrlKey || e.metaKey) {
      const rect = containerRef.current.getBoundingClientRect();
      const mx = e.clientX - rect.left, my = e.clientY - rect.top;
      setView((v) => {
        const k = Math.max(LC_MIN_K, Math.min(LC_MAX_K, v.k * Math.exp(-e.deltaY * 0.01)));
        const wx = (mx - v.x) / v.k, wy = (my - v.y) / v.k;
        return { k, x: mx - wx * k, y: my - wy * k };
      });
    } else {
      setView((v) => ({ ...v, x: v.x - e.deltaX, y: v.y - e.deltaY }));
    }
  }, []);
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, [onWheel]);

  return (
    <div
      ref={containerRef}
      onClick={() => onSelect && onSelect(null)}
      className="w-full h-full relative overflow-hidden bg-neutral-100"
      style={{
        backgroundImage: 'radial-gradient(circle, #cbd5e1 1px, transparent 1px)',
        backgroundSize: `${24 * view.k}px ${24 * view.k}px`,
        backgroundPosition: `${view.x}px ${view.y}px`,
      }}
    >
      {/* World layer — translated/scaled by view */}
      <div
        style={{
          position: 'absolute', left: 0, top: 0, width: 0, height: 0,
          transform: `translate3d(${view.x}px, ${view.y}px, 0) scale(${view.k})`,
          transformOrigin: '0 0', willChange: 'transform',
        }}
      >
        {/* SVG underlay via window.edgePath: flow = slate solid elbows, rework = orange dashed below-row channel. */}
        <svg
          width={layout.width} height={layout.height}
          style={{ position: 'absolute', left: 0, top: 0, overflow: 'visible', pointerEvents: 'none', zIndex: 1 }}
        >
          <defs>
            <marker id="lc-arrow-flow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
              <path d="M 0 0 L 10 5 L 0 10 z" fill="#94a3b8" />
            </marker>
            <marker id="lc-arrow-rework" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
              <path d="M 0 0 L 10 5 L 0 10 z" fill="#fb923c" />
            </marker>
          </defs>
          {edges.map((e, i) => {
            const a = layout.positions[e.from], b = layout.positions[e.to];
            if (!a || !b) return null;
            const rework = e.kind === 'rework';
            return (
              <path
                key={`${e.from}->${e.to}:${e.kind || 'flow'}:${i}`}
                d={window.edgePath(a, b, { w: LC_NODE_W, h: LC_NODE_H, kind: e.kind })}
                fill="none"
                stroke={rework ? '#fb923c' : '#94a3b8'}
                strokeWidth="2"
                strokeLinecap="round"
                strokeDasharray={rework ? '6 4' : undefined}
                markerEnd={rework ? 'url(#lc-arrow-rework)' : 'url(#lc-arrow-flow)'}
                opacity="0.85"
              />
            );
          })}
        </svg>

        {/* Nodes — absolutely positioned from layoutGraph. */}
        {nodes.map((n) => {
          const p = layout.positions[n.id];
          if (!p) return null;
          const selected = n.id === selectedId;
          const count = issues[n.id] || 0;
          return (
            <div
              key={n.id}
              onClick={(e) => { e.stopPropagation(); onSelect && onSelect(n.id); }}
              className={`absolute rounded-xl border bg-white shadow-sm cursor-pointer transition-shadow hover:shadow-md ${selected ? 'border-indigo-300 ring-2 ring-indigo-500' : 'border-neutral-200'}`}
              style={{ left: p.x, top: p.y, width: LC_NODE_W, zIndex: selected ? 20 : 10 }}
            >
              <div className="p-3">
                <div className="text-[13px] font-bold text-neutral-800 truncate">{n.title || n.id}</div>
                <div className="mt-1.5 flex items-center gap-1.5">
                  <span className="text-[10px] font-semibold uppercase tracking-wide bg-neutral-100 text-neutral-500 rounded px-1.5 py-0.5 truncate max-w-[9rem]">
                    {n.templateId || '—'}
                  </span>
                  {n.humanReview && (
                    <span title="Human review" className="text-[9px] font-bold text-amber-700 bg-amber-100 rounded px-1 py-0.5">HR</span>
                  )}
                </div>
              </div>
              {count > 0 && (
                <span className="absolute -top-2 -right-2 min-w-[18px] h-[18px] px-1 rounded-full bg-red-500 text-white text-[10px] font-bold flex items-center justify-center shadow">
                  {count}
                </span>
              )}
            </div>
          );
        })}
      </div>

      {/* Toolbar: zoom + Fit */}
      <div className="absolute bottom-4 left-4 flex items-center gap-1 bg-white border border-neutral-200 rounded-xl shadow-md p-1 z-30">
        <button
          onClick={(e) => { e.stopPropagation(); setView((v) => ({ ...v, k: Math.max(LC_MIN_K, v.k - 0.1) })); }}
          className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-neutral-100 text-neutral-600 font-bold text-lg"
          title="Zoom out"
        >−</button>
        <div className="px-2 text-xs font-mono font-bold text-neutral-600 tabular-nums w-12 text-center">{Math.round(view.k * 100)}%</div>
        <button
          onClick={(e) => { e.stopPropagation(); setView((v) => ({ ...v, k: Math.min(LC_MAX_K, v.k + 0.1) })); }}
          className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-neutral-100 text-neutral-600 font-bold text-lg"
          title="Zoom in"
        >+</button>
        <div className="w-px h-5 bg-neutral-200 mx-1" />
        <button
          onClick={(e) => { e.stopPropagation(); fit(); }}
          className="px-2.5 h-8 flex items-center gap-1.5 rounded-lg hover:bg-neutral-100 text-neutral-600 text-xs font-bold"
          title="Fit to view"
        >
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="4 14 4 20 10 20" />
            <polyline points="20 10 20 4 14 4" />
            <line x1="14" y1="10" x2="21" y2="3" />
            <line x1="3" y1="21" x2="10" y2="14" />
          </svg>
          Fit
        </button>
      </div>

      <div className="absolute bottom-4 right-4 bg-white/90 border border-neutral-200 rounded-lg px-3 py-1.5 text-[10px] font-bold uppercase tracking-widest text-neutral-500 z-30 shadow-sm pointer-events-none">
        {readOnly ? 'Read-only' : 'Scroll to pan · ⌘ + scroll to zoom'}
      </div>
    </div>
  );
}

window.LineCanvas = LineCanvas;
