/* ============================================================
   BATTLE BOARD — pan (drag empty), zoom (buttons/wheel/pinch),
   reset, token drag, tap-empty deselect
   ============================================================ */
const { useState: useStateBoard, useRef: useRefBoard, useCallback: useCbBoard } = React;

const Z_MIN = 0.5, Z_MAX = 2.2;
const clampZoom = (z) => Math.min(Z_MAX, Math.max(Z_MIN, z));

// Play field dimensions in world units (2:1 — standard Armada table proportion)
const FIELD_W = 1240, FIELD_H = 620, DEPLOY = 150, FPAD = 64;

function PlayField({ sides }) {
  const topF = sides[0] || "empire";
  const botF = sides[1] || (sides[0] === "rebel" ? "empire" : "rebel");
  const tc = FACTIONS[topF] ? FACTIONS[topF].color : "var(--empire)";
  const bc = FACTIONS[botF] ? FACTIONS[botF].color : "var(--rebel)";
  const W = FIELD_W, H = FIELD_H, P = FPAD;
  const svgW = W + P * 2, svgH = H + P * 2;
  const L = P, T = P, R = P + W, B = P + H, CX = P + W / 2, CY = P + H / 2;
  const cl = 30; // corner bracket length

  const ticks = [];
  for (let i = 1; i < 12; i++) {
    const x = L + (W / 12) * i;
    ticks.push(<line key={"tt" + i} className="field-tick" x1={x} y1={T} x2={x} y2={T + (i % 3 === 0 ? 14 : 8)} />);
    ticks.push(<line key={"tb" + i} className="field-tick" x1={x} y1={B} x2={x} y2={B - (i % 3 === 0 ? 14 : 8)} />);
  }
  for (let i = 1; i < 6; i++) {
    const y = T + (H / 6) * i;
    ticks.push(<line key={"tl" + i} className="field-tick" x1={L} y1={y} x2={L + (i % 3 === 0 ? 14 : 8)} y2={y} />);
    ticks.push(<line key={"tr" + i} className="field-tick" x1={R} y1={y} x2={R - (i % 3 === 0 ? 14 : 8)} y2={y} />);
  }

  const corner = (x, y, sx, sy, key) => (
    <path key={key} className="field-corner"
          d={`M ${x + sx * cl} ${y} L ${x} ${y} L ${x} ${y + sy * cl}`} />
  );

  return (
    <svg className="play-field" width={svgW} height={svgH} viewBox={`0 0 ${svgW} ${svgH}`}
         style={{ left: -(P + W / 2), top: -(P + H / 2) }}>
      {/* table surface */}
      <rect className="field-surface" x={L} y={T} width={W} height={H} rx="6" />
      {/* inner grid, clipped to the table */}
      <defs>
        <pattern id="fgrid" width="62" height="62" patternUnits="userSpaceOnUse">
          <path d="M62 0 H0 V62" fill="none" className="field-gridline" />
        </pattern>
        <clipPath id="fclip"><rect x={L} y={T} width={W} height={H} rx="6" /></clipPath>
      </defs>
      <rect x={L} y={T} width={W} height={H} fill="url(#fgrid)" clipPath="url(#fclip)" />

      {/* deployment zones */}
      <g clipPath="url(#fclip)">
        <rect className="deploy-zone" style={{ "--zc": tc }} x={L} y={T} width={W} height={DEPLOY} />
        <rect className="deploy-zone" style={{ "--zc": bc }} x={L} y={B - DEPLOY} width={W} height={DEPLOY} />
        <line className="deploy-edge" style={{ "--zc": tc }} x1={L} y1={T + DEPLOY} x2={R} y2={T + DEPLOY} />
        <line className="deploy-edge" style={{ "--zc": bc }} x1={L} y1={B - DEPLOY} x2={R} y2={B - DEPLOY} />
      </g>

      {/* center line + medallion */}
      <line className="field-center" x1={L} y1={CY} x2={R} y2={CY} />
      <circle className="field-center-ring" cx={CX} cy={CY} r="22" />
      <circle className="field-center-dot" cx={CX} cy={CY} r="2.5" />

      {/* table border + corner brackets */}
      <rect className="field-border" x={L} y={T} width={W} height={H} rx="6" />
      {corner(L, T, 1, 1, "c1")}{corner(R, T, -1, 1, "c2")}
      {corner(L, B, 1, -1, "c3")}{corner(R, B, -1, -1, "c4")}
      {ticks}

      {/* labels */}
      <text className="deploy-label" style={{ "--zc": tc }} x={L + 16} y={T + 26} textAnchor="start">{(FACTIONS[topF] || {}).name || ""} · DEPLOYMENT</text>
      <text className="deploy-label" style={{ "--zc": bc }} x={R - 16} y={B - 14} textAnchor="end">{(FACTIONS[botF] || {}).name || ""} · DEPLOYMENT</text>
      <text className="field-dim" x={CX} y={T - 18} textAnchor="middle">SET-UP AREA · 6′ × 3′</text>
    </svg>
  );
}

// generic, numbered objective-token marker — free-standing, no game-state effect;
// players place/drag/remove these for whatever the chosen objective needs.
function ObjectiveMarker({ token, selected, zoom, onSelect, onMove, onRemove, onDragState }) {
  const drag = useRefBoard(null);
  const onPointerDown = (e) => {
    e.stopPropagation();
    e.currentTarget.setPointerCapture(e.pointerId);
    drag.current = { x0: e.clientX, y0: e.clientY, sx: token.x, sy: token.y, moved: false };
  };
  const onPointerMove = (e) => {
    const d = drag.current; if (!d) return;
    const dx = e.clientX - d.x0, dy = e.clientY - d.y0;
    if (!d.moved && Math.hypot(dx, dy) > 3) { d.moved = true; onDragState && onDragState(true); }
    if (d.moved) onMove(token.id, d.sx + dx / zoom, d.sy + dy / zoom);
  };
  const onPointerUp = (e) => {
    const d = drag.current; drag.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
    if (d && d.moved) onDragState && onDragState(false);
    else onSelect(token.id);
  };
  return (
    <div className="obj-token" data-selected={selected} style={{ left: token.x, top: token.y }}
         onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerCancel={onPointerUp}>
      <div className="obj-token-diamond"><span className="obj-token-num">{token.label}</span></div>
      {selected ? (
        <button className="obj-token-del" title="Verwijder objective-token"
                onPointerDown={(e) => e.stopPropagation()}
                onClick={(e) => { e.stopPropagation(); onRemove(token.id); }}>×</button>
      ) : null}
    </div>
  );
}

// obstakel-marker — asteroïde/debris/station, sleepbaar, met overlap-effecten in resolveMove (App.jsx)
const OBSTACLE_LABEL = { asteroid: "Asteroïde", debris: "Debris", station: "Station" };
function ObstacleMarker({ obstacle, selected, zoom, onSelect, onMove, onRemove, onDragState }) {
  const drag = useRefBoard(null);
  const onPointerDown = (e) => {
    e.stopPropagation();
    e.currentTarget.setPointerCapture(e.pointerId);
    drag.current = { x0: e.clientX, y0: e.clientY, sx: obstacle.x, sy: obstacle.y, moved: false };
  };
  const onPointerMove = (e) => {
    const d = drag.current; if (!d) return;
    const dx = e.clientX - d.x0, dy = e.clientY - d.y0;
    if (!d.moved && Math.hypot(dx, dy) > 3) { d.moved = true; onDragState && onDragState(true); }
    if (d.moved) onMove(obstacle.id, d.sx + dx / zoom, d.sy + dy / zoom);
  };
  const onPointerUp = (e) => {
    const d = drag.current; drag.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
    if (d && d.moved) onDragState && onDragState(false);
    else onSelect(obstacle.id);
  };
  const r = obstacle.r || 40;
  return (
    <div className="obstacle-token" data-type={obstacle.type} data-selected={selected}
         style={{ left: obstacle.x, top: obstacle.y, width: r * 2, height: r * 2 }}
         title={OBSTACLE_LABEL[obstacle.type] || obstacle.type}
         onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerCancel={onPointerUp}>
      {obstacle.type === "asteroid" ? (
        <svg className="obstacle-svg" viewBox="0 0 100 100"><polygon points="50,4 74,16 96,42 88,72 62,96 34,90 8,66 12,32 30,10" /></svg>
      ) : obstacle.type === "station" ? (
        <svg className="obstacle-svg" viewBox="0 0 100 100">
          <circle cx="50" cy="50" r="44" className="obstacle-station-ring" />
          <circle cx="50" cy="50" r="20" className="obstacle-station-core" />
          <line x1="50" y1="6" x2="50" y2="94" className="obstacle-station-strut" />
          <line x1="6" y1="50" x2="94" y2="50" className="obstacle-station-strut" />
        </svg>
      ) : (
        <svg className="obstacle-svg" viewBox="0 0 100 100">
          <circle cx="30" cy="34" r="18" />
          <circle cx="66" cy="30" r="12" />
          <circle cx="56" cy="66" r="20" />
          <circle cx="20" cy="68" r="10" />
        </svg>
      )}
      {selected ? (
        <button className="obstacle-del" title="Verwijder obstakel"
                onPointerDown={(e) => e.stopPropagation()}
                onClick={(e) => { e.stopPropagation(); onRemove(obstacle.id); }}>×</button>
      ) : null}
    </div>
  );
}

function Board({ ships, squadrons = [], sides = [], selectedId, showArcs, tokenStyle, phase, activeFaction, guided, cpuActing, maneuver, objectiveTokens = [], selectedObjTokenId, obstacles = [], selectedObstacleId, onYaw, onSpeedDelta, onManeuverConfirm, onManeuverCancel, onSelect, onMove, onMoveSquadron, onAddObjectiveToken, onMoveObjectiveToken, onSelectObjectiveToken, onRemoveObjectiveToken, onAddObstacle, onMoveObstacle, onSelectObstacle, onRemoveObstacle }) {
  const [view, setView] = useStateBoard({ x: 0, y: 0, z: 1 });
  const [panning, setPanning] = useStateBoard(false);
  const tokenDragging = useRefBoard(false);
  const boardRef = useRefBoard(null);
  const pan = useRefBoard(null);
  const pointers = useRefBoard(new Map());
  const pinch = useRefBoard(null);

  const rectCenter = () => {
    const r = boardRef.current.getBoundingClientRect();
    return { cx: r.left + r.width / 2, cy: r.top + r.height / 2 };
  };

  const zoomAt = useCbBoard((clientX, clientY, nextZ) => {
    setView((v) => {
      const z2 = clampZoom(nextZ);
      const { cx, cy } = rectCenter();
      const sx = clientX - cx, sy = clientY - cy;
      const wx = (sx - v.x) / v.z, wy = (sy - v.y) / v.z;
      return { z: z2, x: sx - wx * z2, y: sy - wy * z2 };
    });
  }, []);

  const onWheel = (e) => {
    e.preventDefault();
    const factor = Math.exp(-e.deltaY * 0.0012);
    zoomAt(e.clientX, e.clientY, view.z * factor);
  };

  const onPointerDown = (e) => {
    pointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
    if (pointers.current.size === 2) {
      const pts = [...pointers.current.values()];
      const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
      pinch.current = { dist, z: view.z };
      pan.current = null;
      return;
    }
    // begin pan on empty space
    e.currentTarget.setPointerCapture(e.pointerId);
    pan.current = { x0: e.clientX, y0: e.clientY, vx: view.x, vy: view.y, moved: false };
  };

  const onPointerMove = (e) => {
    if (pointers.current.has(e.pointerId)) pointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
    if (pinch.current && pointers.current.size >= 2) {
      const pts = [...pointers.current.values()];
      const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
      const mx = (pts[0].x + pts[1].x) / 2, my = (pts[0].y + pts[1].y) / 2;
      zoomAt(mx, my, pinch.current.z * (dist / pinch.current.dist));
      return;
    }
    const p = pan.current; if (!p) return;
    const dx = e.clientX - p.x0, dy = e.clientY - p.y0;
    if (!p.moved && Math.hypot(dx, dy) > 3) { p.moved = true; setPanning(true); }
    if (p.moved) setView((v) => ({ ...v, x: p.vx + dx, y: p.vy + dy }));
  };

  const onPointerUp = (e) => {
    pointers.current.delete(e.pointerId);
    if (pointers.current.size < 2) pinch.current = null;
    const p = pan.current; pan.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
    setPanning(false);
    if (p && !p.moved && !tokenDragging.current) { onSelect(null); if (onSelectObjectiveToken) onSelectObjectiveToken(null); if (onSelectObstacle) onSelectObstacle(null); }
  };

  const reset = () => setView({ x: 0, y: 0, z: 1 });
  const stepZoom = (d) => { const { cx, cy } = rectCenter(); zoomAt(cx, cy, view.z + d); };

  // frame the whole field to the viewport
  const fitField = useCbBoard(() => {
    const r = boardRef.current.getBoundingClientRect();
    const z = clampZoom(Math.min(r.width / (FIELD_W + FPAD * 2), r.height / (FIELD_H + FPAD * 2)) * 0.96);
    setView({ x: 0, y: 0, z });
  }, []);

  React.useEffect(() => { fitField(); }, [fitField]);

  return (
    <div className="board" ref={boardRef} data-panning={panning}
         onWheel={onWheel} onPointerDown={onPointerDown} onPointerMove={onPointerMove}
         onPointerUp={onPointerUp} onPointerCancel={onPointerUp}>
      <div className="board-world"
           style={{ transform: `translate(${view.x}px, ${view.y}px) scale(${view.z})` }}>
        <div className="board-grid" />
        <PlayField sides={sides} />
        {/* zodra een schip zijn dial heeft onthuld is dat het enige interactieve schip */}
        {ships.map((s) => {
          const committed = phase === "ship" ? ships.find((x) => x.activationStarted && !x.activated) : null;
          const eligible = phase === "ship" && s.faction === activeFaction && !s.activated && !s.destroyed;
          const spent = phase === "ship" && s.activated && !s.destroyed;
          const inProgress = s.activationStarted && !s.activated;
          // vóór commit: eligible schepen selecteerbaar; na commit: alleen het actieve schip
          const selectable = phase === "ship" ? (committed ? s.id === committed.id : eligible) : false;
          const locked = cpuActing ? true : (guided && phase ? !(selectable || s.id === selectedId) : false);
          const movable = cpuActing ? false : (guided ? inProgress : true); // begeleid: alleen het actieve schip; CPU aan zet: niets
          return (
            <ShipToken key={s.id} ship={s} selected={s.id === selectedId} zoom={view.z}
                       showArcs={showArcs} tokenStyle={tokenStyle} eligible={eligible} spent={spent} locked={locked} movable={movable}
                       onSelect={onSelect} onMove={onMove}
                       onDragState={(on) => { tokenDragging.current = on; }} />
          );
        })}
        {squadrons.map((q) => {
          // ook klikbaar tijdens de ship-fase wanneer een schip met Squadron-dial nog activaties over heeft
          const sqCmdShip = phase === "ship" ? ships.find((s) => s.activationStarted && !s.activated && s.squadronPtsLeft > 0) : null;
          const eligible = !q.activated && !q.destroyed &&
            ((phase === "squadron" && q.faction === activeFaction) || (sqCmdShip && q.faction === sqCmdShip.faction));
          const locked = cpuActing ? true : (guided && phase ? !(eligible || q.id === selectedId) : false);
          const rogue = (q.keywords || []).some((k) => String(k).toLowerCase() === "rogue");
          const canMove = phase !== "squadron" || ((rogue || !q.attacked) && canSquadronMove(q, squadrons));
          return (
            <SquadronToken key={q.id} sq={q} selected={q.id === selectedId} zoom={view.z} eligible={eligible} locked={locked} movable={canMove}
                           onSelect={onSelect} onMove={onMoveSquadron}
                           onDragState={(on) => { tokenDragging.current = on; }} />
          );
        })}
        {objectiveTokens.map((tok) => (
          <ObjectiveMarker key={tok.id} token={tok} selected={tok.id === selectedObjTokenId} zoom={view.z}
                            onSelect={onSelectObjectiveToken} onMove={onMoveObjectiveToken} onRemove={onRemoveObjectiveToken}
                            onDragState={(on) => { tokenDragging.current = on; }} />
        ))}
        {obstacles.map((o) => (
          <ObstacleMarker key={o.id} obstacle={o} selected={o.id === selectedObstacleId} zoom={view.z}
                           onSelect={onSelectObstacle} onMove={onMoveObstacle} onRemove={onRemoveObstacle}
                           onDragState={(on) => { tokenDragging.current = on; }} />
        ))}
        {maneuver && ships.find((s) => s.id === maneuver.shipId) ? (
          <ManeuverLayer ship={ships.find((s) => s.id === maneuver.shipId)} yaw={maneuver.yaw} z={view.z}
                         nav={maneuver.nav} speedDelta={maneuver.speedDelta || 0} showArcs={showArcs} onSpeedDelta={onSpeedDelta}
                         onYaw={onYaw} onConfirm={onManeuverConfirm} onCancel={onManeuverCancel} />
        ) : null}
      </div>

      <div className="board-hint mono">{ships.length} schepen{objectiveTokens.length ? ` · ${objectiveTokens.length} objective-token${objectiveTokens.length > 1 ? "s" : ""}` : ""}{obstacles.length ? ` · ${obstacles.length} obstakel${obstacles.length > 1 ? "s" : ""}` : ""} · sleep leeg veld om te pannen</div>

      <div className="board-controls" onPointerDown={(e) => e.stopPropagation()}>
        <div className="zoom-stack">
          <button className="icon-btn" onClick={() => stepZoom(0.2)} aria-label="zoom in">+</button>
          <div className="zoom-readout">{Math.round(view.z * 100)}%</div>
          <button className="icon-btn" onClick={() => stepZoom(-0.2)} aria-label="zoom uit">{"−"}</button>
        </div>
        <button className="icon-btn" onClick={fitField} title="Heel veld tonen" aria-label="fit">
          <svg width="16" height="16" viewBox="0 0 16 16"><path d="M2 5 V2 H5 M11 2 H14 V5 M14 11 V14 H11 M5 14 H2 V11" fill="none" stroke="currentColor" strokeWidth="1.5"/></svg>
        </button>
        <button className="icon-btn" onClick={reset} title="Reset weergave (100%)" aria-label="reset">
          <svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 2.5 A5.5 5.5 0 1 0 13 6" fill="none" stroke="currentColor" strokeWidth="1.5"/><path d="M8 0.5 L8 4.5 L11.5 2.5 Z" fill="currentColor"/></svg>
        </button>
        {onAddObjectiveToken ? (
          <button className="icon-btn" onClick={onAddObjectiveToken} title="Objective-token toevoegen" aria-label="objective-token toevoegen">
            <svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 2 L14 8 L8 14 L2 8 Z" fill="none" stroke="currentColor" strokeWidth="1.4"/><path d="M8 5 V11 M5 8 H11" stroke="currentColor" strokeWidth="1.4"/></svg>
          </button>
        ) : null}
        {onAddObstacle ? (
          <div className="obstacle-toolbar">
            <button className="icon-btn" onClick={() => onAddObstacle("asteroid")} title="Asteroïde toevoegen" aria-label="asteroïde toevoegen">☄</button>
            <button className="icon-btn" onClick={() => onAddObstacle("debris")} title="Debris toevoegen" aria-label="debris toevoegen">✦</button>
            <button className="icon-btn" onClick={() => onAddObstacle("station")} title="Station toevoegen" aria-label="station toevoegen">◎</button>
          </div>
        ) : null}
      </div>
    </div>
  );
}

Object.assign(window, { Board });
