/* panel.jsx — a station tile floating on the galaxy.
 * Signature rounded-octagonal shape. Locked by default (so left-drag pans
 * the world). Unlock to drag (header or body) and resize (edges + corners),
 * with ~8px edge-snap to neighbours. Click raises. X closes.
 *
 * Controlled: App owns position/lock/z and the layout-persistence seam.
 * Zoom-aware: divides pointer deltas by window.__nbaZoom.scale so a 1px
 * mouse move = 1 world px at any zoom.
 */
const { useState: usePState, useRef: usePRef } = React;

const SNAP = 8, MINW = 180, MINH = 96;
const zoomScale = () => (window.__nbaZoom && window.__nbaZoom.scale) || 1;

function siblingRects(selfEl) {
  const out = [];
  const parent = selfEl && selfEl.offsetParent;
  document.querySelectorAll(".dpanel[data-panel-id]").forEach((el) => {
    if (el === selfEl || el.offsetParent !== parent) return;
    out.push({ left: el.offsetLeft, right: el.offsetLeft + el.offsetWidth, top: el.offsetTop, bottom: el.offsetTop + el.offsetHeight });
  });
  return out;
}
function snapBox(c, sibs) {
  let { x, y, w, h } = c; const snapped = {};
  const tX = (cand, anchor, edge) => { if (Math.abs(cand - anchor) <= SNAP) { x = edge === "l" ? anchor : anchor - w; snapped[edge] = true; } };
  const tY = (cand, anchor, edge) => { if (Math.abs(cand - anchor) <= SNAP) { y = edge === "t" ? anchor : anchor - h; snapped[edge] = true; } };
  for (const s of sibs) {
    tX(x, s.left, "l"); tX(x + w, s.right, "r"); tX(x, s.right, "l"); tX(x + w, s.left, "r");
    tY(y, s.top, "t"); tY(y + h, s.bottom, "b"); tY(y, s.bottom, "t"); tY(y + h, s.top, "b");
  }
  return { x, y, snapped };
}

function StationPanel({ station, pos, locked, z, focused, zoom = 1, onPos, onLock, onClose, onRaise, onExpand, onZoom, children }) {
  const [drag, setDrag] = usePState(false);
  const [resize, setResize] = usePState(false);
  const ref = usePRef(null);
  const accent = station.accent;

  // Per-window zoom via explicit buttons (no wheel — wheel collides with the
  // body scroll). Steps between 1× and 4×. Functional updater so rapid clicks
  // accumulate even before a re-render.
  const ZSTEP = 0.25, ZMINP = 1, ZMAXP = 4;
  const zoomBy = (dir) => onZoom && onZoom((cur) => +Math.max(ZMINP, Math.min(ZMAXP, (cur || 1) + dir * ZSTEP)).toFixed(3));

  // Locked card = a launch target: a clean click/tap expands it; a drag pans
  // the world (we don't preventDefault so the world's pan handler runs).
  const watchClick = (sx, sy) => {
    const t0 = Date.now(); let moved = false;
    const mv = (ev) => { if (Math.hypot(ev.clientX - sx, ev.clientY - sy) > 5) moved = true; };
    const up = () => {
      window.removeEventListener("mousemove", mv); window.removeEventListener("mouseup", up);
      if (!moved && Date.now() - t0 < 500 && onExpand) onExpand();
    };
    window.addEventListener("mousemove", mv); window.addEventListener("mouseup", up);
  };

  const beginDrag = (e) => {
    if (e.button !== 0) return;
    if (e.target.closest(".panel-ctl, .panel-resize")) return;
    if (locked) { onRaise(); watchClick(e.clientX, e.clientY); return; }   // click→expand / drag→pan
    e.preventDefault();
    const sx = e.clientX, sy = e.clientY;
    const x0 = ref.current.offsetLeft, y0 = ref.current.offsetTop;
    const w = ref.current.offsetWidth, h = ref.current.offsetHeight;
    setDrag(true); onRaise();
    const sibs = siblingRects(ref.current);
    const onMove = (ev) => {
      const s = zoomScale();
      const { x, y } = snapBox({ x: x0 + (ev.clientX - sx) / s, y: y0 + (ev.clientY - sy) / s, w, h }, sibs);
      onPos({ x: Math.round(x), y: Math.round(y), w, h });
    };
    const onUp = () => { setDrag(false); window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); };
    window.addEventListener("mousemove", onMove); window.addEventListener("mouseup", onUp);
  };

  const beginResize = (e, edge) => {
    if (e.button !== 0 || locked) return;
    e.preventDefault(); e.stopPropagation();
    const sx = e.clientX, sy = e.clientY;
    const x0 = ref.current.offsetLeft, y0 = ref.current.offsetTop;
    const w0 = ref.current.offsetWidth, h0 = ref.current.offsetHeight;
    setResize(true); onRaise();
    const sibs = siblingRects(ref.current);
    const N = edge.includes("n"), S = edge.includes("s"), E = edge.includes("e"), W = edge.includes("w");
    const onMove = (ev) => {
      const s = zoomScale();
      const dx = (ev.clientX - sx) / s, dy = (ev.clientY - sy) / s;
      let x = x0, y = y0, w = w0, h = h0;
      if (E) w = Math.max(MINW, w0 + dx);
      if (S) h = Math.max(MINH, h0 + dy);
      if (W) { w = Math.max(MINW, w0 - dx); x = x0 + (w0 - w); }
      if (N) { h = Math.max(MINH, h0 - dy); y = y0 + (h0 - h); }
      for (const sib of sibs) {
        if (E) { if (Math.abs(x + w - sib.left) <= SNAP) w = sib.left - x; if (Math.abs(x + w - sib.right) <= SNAP) w = sib.right - x; }
        if (S) { if (Math.abs(y + h - sib.top) <= SNAP) h = sib.top - y; if (Math.abs(y + h - sib.bottom) <= SNAP) h = sib.bottom - y; }
        if (W) { if (Math.abs(x - sib.right) <= SNAP) { const r = x + w; x = sib.right; w = r - x; } if (Math.abs(x - sib.left) <= SNAP) { const r = x + w; x = sib.left; w = r - x; } }
        if (N) { if (Math.abs(y - sib.bottom) <= SNAP) { const b = y + h; y = sib.bottom; h = b - y; } if (Math.abs(y - sib.top) <= SNAP) { const b = y + h; y = sib.top; h = b - y; } }
      }
      w = Math.max(MINW, w); h = Math.max(MINH, h);
      onPos({ x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h) });
    };
    const onUp = () => { setResize(false); window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); };
    window.addEventListener("mousemove", onMove); window.addEventListener("mouseup", onUp);
  };

  const touchRef = usePRef(null);
  const onTouchStart = (e) => {
    if (!locked || e.touches.length !== 1) return;
    if (e.target.closest(".panel-ctl, .panel-resize")) return;
    const t = e.touches[0];
    touchRef.current = { x: t.clientX, y: t.clientY, t0: Date.now(), moved: false };
  };
  const onTouchMove = (e) => {
    const r = touchRef.current; if (!r) return;
    const t = e.touches[0]; if (t && Math.hypot(t.clientX - r.x, t.clientY - r.y) > 8) r.moved = true;
  };
  const onTouchEnd = () => {
    const r = touchRef.current; touchRef.current = null;
    if (r && !r.moved && Date.now() - r.t0 < 500 && onExpand) onExpand();
  };

  const cls = ["dpanel", locked ? "is-locked" : "is-unlocked", drag ? "is-dragging" : "", resize ? "is-resizing" : "", focused ? "is-focused" : "", zoom > 1 ? "is-zoomed" : "", station.center ? "is-center" : ""].join(" ");
  const zStyle = (drag || resize || zoom > 1) ? 9990 : z;
  const tf = zoom !== 1 ? { transform: `scale(${zoom})`, transformOrigin: "center center" } : null;

  return (
    <div ref={ref}
         className={cls}
         data-panel-id={station.id}
         onMouseDownCapture={onRaise}
         onMouseDown={beginDrag}
         onTouchStart={onTouchStart}
         onTouchMove={onTouchMove}
         onTouchEnd={onTouchEnd}
         style={{ left: pos.x, top: pos.y, width: pos.w, height: pos.h, zIndex: zStyle, "--ac": accent, ...tf }}>
      <span className="tick tl"/><span className="tick tr"/><span className="tick bl"/><span className="tick br"/>

      <div className="dpanel__head">
        <span className="dpanel__n">{station.n}</span>
        <div className="dpanel__titles">
          <span className="dpanel__title">{station.title}</span>
          <span className="dpanel__sub">{station.sub}</span>
        </div>
        <div className="zoomgrp" title="Zoom this window">
          <button className="panel-ctl zoomgrp__btn" onClick={(e) => { e.stopPropagation(); onRaise && onRaise(); zoomBy(-1); }} disabled={zoom <= 1} title={zoom > 1 ? "Zoom out (" + Math.round(zoom * 100) + "%)" : "Zoom out"}>
            <svg width="11" height="11" viewBox="0 0 12 12"><path d="M2.5 6h7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>
          </button>
          <button className="panel-ctl zoomgrp__btn" onClick={(e) => { e.stopPropagation(); onRaise && onRaise(); zoomBy(1); }} disabled={zoom >= 4} title="Zoom in">
            <svg width="11" height="11" viewBox="0 0 12 12"><path d="M6 2.5v7M2.5 6h7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>
          </button>
        </div>
        <button className="panel-ctl panel-expand" onClick={(e) => { e.stopPropagation(); onExpand && onExpand(); }} title="Open station">
          <svg width="12" height="12" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5.5 2.5H2.5V5.5"/><path d="M8.5 11.5H11.5V8.5"/><path d="M2.5 2.5 6 6"/><path d="M11.5 11.5 8 8"/></svg>
        </button>
        <button className="panel-ctl panel-lock" onClick={(e) => { e.stopPropagation(); onLock(!locked); }}
                title={locked ? "Locked — click to unlock, then drag to move" : "Unlocked — drag to move, drag edges to resize"}>
          {locked
            ? <svg width="12" height="12" viewBox="0 0 14 14"><path d="M3.5 6V4.5a3.5 3.5 0 1 1 7 0V6" stroke="currentColor" strokeWidth="1.2" fill="none"/><rect x="2.5" y="6" width="9" height="6.5" rx="1.4" stroke="currentColor" strokeWidth="1.2" fill="rgba(255,198,143,0.14)"/></svg>
            : <svg width="12" height="12" viewBox="0 0 14 14"><path d="M3.5 6V4.5a3.5 3.5 0 0 1 6.6-1.5" stroke="currentColor" strokeWidth="1.2" fill="none"/><rect x="2.5" y="6" width="9" height="6.5" rx="1.4" stroke="currentColor" strokeWidth="1.2" fill="rgba(122,216,255,0.16)"/></svg>}
        </button>
        <button className="panel-ctl panel-close" onClick={(e) => { e.stopPropagation(); onClose(); }} title="Close (reopen from the WINDOWS tray)">
          <svg width="12" height="12" viewBox="0 0 14 14"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
        </button>
      </div>

      <div className="dpanel__body allow-scroll">{children}</div>

      {!locked && (
        <>
          <span className="panel-resize r-n" onMouseDown={(e) => beginResize(e, "n")}/>
          <span className="panel-resize r-s" onMouseDown={(e) => beginResize(e, "s")}/>
          <span className="panel-resize r-e" onMouseDown={(e) => beginResize(e, "e")}/>
          <span className="panel-resize r-w" onMouseDown={(e) => beginResize(e, "w")}/>
          <span className="panel-resize r-nw" onMouseDown={(e) => beginResize(e, "nw")}/>
          <span className="panel-resize r-ne" onMouseDown={(e) => beginResize(e, "ne")}/>
          <span className="panel-resize r-sw" onMouseDown={(e) => beginResize(e, "sw")}/>
          <span className="panel-resize r-se" onMouseDown={(e) => beginResize(e, "se")}/>
        </>
      )}
    </div>
  );
}

window.StationPanel = StationPanel;

/* ---------- FocusWindow: the expanded station (screen-space) ----------
 * A larger focused window the visitor can move (drag header) and resize
 * (edges/corners). X / Esc / browser-back close it. Lives above the world
 * in screen coords so it stays readable regardless of canvas zoom.
 */
const FW_MINW = 300, FW_MINH = 220;
function FocusWindow({ station, onClose, children }) {
  const init = () => {
    const W = Math.min(760, window.innerWidth - 32);
    const H = Math.min(Math.round(window.innerHeight * 0.66), window.innerHeight - 158);
    return { x: Math.max(16, Math.round((window.innerWidth - W) / 2)), y: Math.max(20, Math.round((window.innerHeight - 132 - H) / 2)), w: W, h: H };
  };
  const [pos, setPos] = usePState(init);
  const [zoom, setZoom] = usePState(1);
  const accent = station.accent;

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  // Per-window zoom via explicit buttons (1×–3×); content scales, body scrolls.
  const ZSTEP = 0.25;
  const zoomBy = (dir) => setZoom((z) => +Math.max(1, Math.min(3, z + dir * ZSTEP)).toFixed(3));
  const resetZoom = () => setZoom(1);

  const beginDrag = (e) => {
    if (e.button !== 0) return;
    if (e.target.closest("button, a, input, textarea, select, .fw-close")) return;
    e.preventDefault();
    const sx = e.clientX, sy = e.clientY, x0 = pos.x, y0 = pos.y;
    const mv = (ev) => setPos((p) => ({ ...p, x: x0 + (ev.clientX - sx), y: y0 + (ev.clientY - sy) }));
    const up = () => { window.removeEventListener("mousemove", mv); window.removeEventListener("mouseup", up); };
    window.addEventListener("mousemove", mv); window.addEventListener("mouseup", up);
  };
  const beginTouchDrag = (e) => {
    if (e.target.closest("button, a, input, textarea, select, .fw-close, .allow-scroll")) return;
    const t = e.touches[0], sx = t.clientX, sy = t.clientY, x0 = pos.x, y0 = pos.y;
    const mv = (ev) => { const tt = ev.touches[0]; if (tt) { ev.preventDefault(); setPos((p) => ({ ...p, x: x0 + (tt.clientX - sx), y: y0 + (tt.clientY - sy) })); } };
    const end = () => { window.removeEventListener("touchmove", mv); window.removeEventListener("touchend", end); };
    window.addEventListener("touchmove", mv, { passive: false }); window.addEventListener("touchend", end);
  };
  const beginResize = (e, edge) => {
    if (e.button !== 0) return;
    e.preventDefault(); e.stopPropagation();
    const sx = e.clientX, sy = e.clientY, { x: x0, y: y0, w: w0, h: h0 } = pos;
    const N = edge.includes("n"), S = edge.includes("s"), E = edge.includes("e"), W = edge.includes("w");
    const mv = (ev) => {
      const dx = ev.clientX - sx, dy = ev.clientY - sy;
      let x = x0, y = y0, w = w0, h = h0;
      if (E) w = Math.max(FW_MINW, w0 + dx);
      if (S) h = Math.max(FW_MINH, h0 + dy);
      if (W) { w = Math.max(FW_MINW, w0 - dx); x = x0 + (w0 - w); }
      if (N) { h = Math.max(FW_MINH, h0 - dy); y = y0 + (h0 - h); }
      setPos({ x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h) });
    };
    const up = () => { window.removeEventListener("mousemove", mv); window.removeEventListener("mouseup", up); };
    window.addEventListener("mousemove", mv); window.addEventListener("mouseup", up);
  };

  return (
    <div className="fw" role="dialog" aria-label={station.title}
         style={{ left: pos.x, top: pos.y, width: pos.w, height: pos.h, "--ac": accent }}>
      <div className="fw__head" onMouseDown={beginDrag} onTouchStart={beginTouchDrag}>
        <span className="fw__n">{station.n}</span>
        <div className="fw__titles">
          <span className="fw__title">{station.title}</span>
          <span className="fw__sub">{station.sub}</span>
        </div>
        <div className="zoomgrp" title="Zoom this window">
          <button className="panel-ctl zoomgrp__btn" onClick={() => zoomBy(-1)} disabled={zoom <= 1} title="Zoom out">
            <svg width="12" height="12" viewBox="0 0 12 12"><path d="M2.5 6h7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>
          </button>
          <button className="zoomgrp__val" onClick={resetZoom} title="Reset to 100%">{Math.round(zoom * 100)}%</button>
          <button className="panel-ctl zoomgrp__btn" onClick={() => zoomBy(1)} disabled={zoom >= 3} title="Zoom in">
            <svg width="12" height="12" viewBox="0 0 12 12"><path d="M6 2.5v7M2.5 6h7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>
          </button>
        </div>
        <button className="fw-close panel-ctl" onClick={onClose} title="Close (or press Esc / browser back)">
          <svg width="13" height="13" viewBox="0 0 14 14"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>
        </button>
      </div>
      <div className="fw__body allow-scroll">
        <div className="fw__zoom" style={zoom !== 1 ? { transform: `scale(${zoom})`, transformOrigin: "top left", width: `${100 / zoom}%` } : null}>{children}</div>
      </div>
      <span className="panel-resize r-n" onMouseDown={(e) => beginResize(e, "n")}/>
      <span className="panel-resize r-s" onMouseDown={(e) => beginResize(e, "s")}/>
      <span className="panel-resize r-e" onMouseDown={(e) => beginResize(e, "e")}/>
      <span className="panel-resize r-w" onMouseDown={(e) => beginResize(e, "w")}/>
      <span className="panel-resize r-nw" onMouseDown={(e) => beginResize(e, "nw")}/>
      <span className="panel-resize r-ne" onMouseDown={(e) => beginResize(e, "ne")}/>
      <span className="panel-resize r-sw" onMouseDown={(e) => beginResize(e, "sw")}/>
      <span className="panel-resize r-se" onMouseDown={(e) => beginResize(e, "se")}/>
    </div>
  );
}

window.FocusWindow = FocusWindow;
