// atelier-board.jsx — Interactive Atelier chess board.
// Props:
//   palette         (object from atelierPalette)
//   layers          { heatmap, hero, trajectory, tension } booleans
//   heatmapStyle    'wash' | 'dotted' | 'contour'
//   heatmapIntensity 0..1
//   trajectoryStyle 'painterly' | 'clean'
//   hovered, selected, onHover, onSelect (square strings or null)
//   focus           square to spotlight (lesson screen)
//   size            board pixel size (default 640)
//   showCoords      bool

const B_FILES = ['a','b','c','d','e','f','g','h'];
const B_RANKS = [1,2,3,4,5,6,7,8];
const B_SQ = 80;
const B_BOARD = B_SQ * 8;
const B_PAD = 24;
const B_WIDTH = B_BOARD + B_PAD * 2;

const B_GLYPH = {
  wK:'♔',wQ:'♕',wR:'♖',wB:'♗',wN:'♘',wP:'♙',
  bK:'♚',bQ:'♛',bR:'♜',bB:'♝',bN:'♞',bP:'♟',
};
function sqXY(sq) {
  const f = B_FILES.indexOf(sq[0]);
  const r = parseInt(sq[1], 10);
  return { x: B_PAD + f * B_SQ, y: B_PAD + (8 - r) * B_SQ };
}
function sqCenter(sq) {
  const { x, y } = sqXY(sq);
  return { cx: x + B_SQ / 2, cy: y + B_SQ / 2 };
}

function AtelierBoard({
  palette = window.atelierPalette(0.5),
  layers = { heatmap: true, hero: true, trajectory: false, tension: false },
  heatmapStyle = 'wash',
  heatmapIntensity = 1,
  trajectoryStyle = 'painterly',
  hovered = null, selected = null,
  onHover, onSelect,
  focus = null,
  size = 720,
  showCoords = true,
  showTrajectoriesFor = null, // explicit list of pieces to draw trajectories for
  onSquareClick = null,        // fires when user clicks an empty square rect
  markedSquares = null,        // array of { sq, label } — A/B/C markers
  hiddenPieces = null,         // Set of square strings — render at opacity 0.08
  highlightSquare = null,      // single square — dashed destination ring
  noTrajectoryOnSelect = false, // suppress trajectory auto-show on hover/select
  positionOverride = null,      // {square: pieceCode} — render this instead of window.A_POSITION
}) {
  const C = palette;
  // Decide which piece's trajectory we're showing.
  const trajectoryPiece = (noTrajectoryOnSelect ? null : (selected || hovered))
    || focus
    || (layers.trajectory ? window.A_HERO : null);
  const trajectory = trajectoryPiece ? window.A_TRAJECTORIES[trajectoryPiece] : null;

  // Heatmap source: board influence; if a piece is selected, isolate to that piece.
  const isolatedInfluence = (selected && window.A_INFLUENCE_BY_PIECE[selected]) || null;
  const heatmapData = isolatedInfluence || window.A_INFLUENCE_BOARD;

  return (
    <svg
      viewBox={`0 0 ${B_WIDTH} ${B_WIDTH}`}
      style={{ width: size, height: size, display: 'block', maxWidth: '100%' }}
      role="img"
      aria-label="Chess board with influence visualisations"
    >
      <defs>
        <filter id="ab-bleed" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="11" />
        </filter>
        <filter id="ab-bleed-soft" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" />
        </filter>
        <linearGradient id="ab-spot" x1="50%" y1="0%" x2="50%" y2="100%">
          <stop offset="0%"  stopColor="#fff5d7" stopOpacity="0.95" />
          <stop offset="55%" stopColor={C.brassLight} stopOpacity="0.55" />
          <stop offset="100%" stopColor={C.brass} stopOpacity="0.0" />
        </linearGradient>
        <radialGradient id="ab-shadow" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="rgba(42,34,27,0.32)" />
          <stop offset="100%" stopColor="rgba(42,34,27,0)" />
        </radialGradient>
        <linearGradient id="ab-brush" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%"  stopColor={C.umber} stopOpacity="0.95" />
          <stop offset="70%" stopColor={C.umber} stopOpacity="0.55" />
          <stop offset="100%" stopColor={C.umber} stopOpacity="0.0" />
        </linearGradient>
      </defs>

      {/* Board surface */}
      <rect x="0" y="0" width={B_WIDTH} height={B_WIDTH} fill={C.boneDeep} />

      {/* Squares */}
      <g>
        {B_RANKS.map((r) =>
          B_FILES.map((f) => {
            const sq = f + r;
            const { x, y } = sqXY(sq);
            const isLight = (B_FILES.indexOf(f) + r) % 2 === 1;
            const dim = focus && sq !== focus ? 0.55 : 1;
            return (
              <rect
                key={sq} x={x} y={y} width={B_SQ} height={B_SQ}
                fill={isLight ? C.squareLight : C.squareDark}
                opacity={dim}
                onClick={() => onSquareClick && onSquareClick(sq)}
                style={onSquareClick ? { cursor: 'pointer' } : undefined}
              />
            );
          })
        )}
      </g>

      {/* Heatmap layer */}
      {layers.heatmap && (
        <HeatmapLayer data={heatmapData} palette={C} style={heatmapStyle} intensity={heatmapIntensity} />
      )}

      {/* Heatmap hover tooltip — only when heatmap is on and a square is hovered */}
      {layers.heatmap && hovered && (
        <HeatmapTooltipLayer
          square={hovered}
          palette={C}
          position={positionOverride || window.A_POSITION}
          influenceByPiece={window.A_INFLUENCE_BY_PIECE}
        />
      )}

      {/* Marked squares (A/B/C quiz labels) — above heatmap, below pieces */}
      {markedSquares && markedSquares.length > 0 && (
        <g pointerEvents="none">
          {markedSquares.map(({ sq, label }) => {
            const { cx, cy } = sqCenter(sq);
            return (
              <g key={'mark-' + sq}>
                <circle cx={cx} cy={cy} r="16" fill={C.brassDeep} opacity="0.88" />
                <text
                  x={cx} y={cy + 5}
                  fontFamily={window.ATELIER_TYPE.ui}
                  fontSize="14" fontWeight="700"
                  textAnchor="middle" fill={C.cream}
                  pointerEvents="none"
                >{label}</text>
              </g>
            );
          })}
        </g>
      )}

      {/* Coordinates */}
      {showCoords && (
        <g fontFamily={window.ATELIER_TYPE.ui} fontSize="10" fill={C.umberFaint} style={{ letterSpacing: 3 }}>
          {B_FILES.map((f, i) => (
            <text key={'f' + f} x={B_PAD + i * B_SQ + B_SQ / 2} y={B_PAD + B_BOARD + 16} textAnchor="middle">
              {f.toUpperCase()}
            </text>
          ))}
          {B_RANKS.map((r) => (
            <text key={'r' + r} x={B_PAD - 10} y={B_PAD + (8 - r) * B_SQ + B_SQ / 2 + 3.5} textAnchor="middle">
              {r}
            </text>
          ))}
        </g>
      )}

      {/* Hero illumination */}
      {layers.hero && <HeroLayer square={window.A_HERO} palette={C} />}

      {/* Selection highlight ring */}
      {selected && <SelectionRing square={selected} palette={C} />}

      {/* Destination hint ring (dashed brass ring on highlightSquare) */}
      {highlightSquare && <HighlightRing square={highlightSquare} palette={C} />}

      {/* Pieces */}
      <PiecesLayer
        palette={C} hovered={hovered} selected={selected}
        onHover={onHover} onSelect={onSelect}
        heroSquare={layers.hero ? window.A_HERO : null}
        hiddenPieces={hiddenPieces}
        positionOverride={positionOverride}
      />

      {/* Trajectory */}
      {(layers.trajectory || trajectoryPiece) && trajectory && trajectoryPiece !== null && (
        <TrajectoryLayer path={trajectory} palette={C} style={trajectoryStyle} />
      )}

      {/* Tension */}
      {layers.tension && <TensionLayer list={window.A_TENSIONS} palette={C} />}
    </svg>
  );
}

function PiecesLayer({ palette, hovered, selected, onHover, onSelect, heroSquare, hiddenPieces, positionOverride }) {
  const C = palette;
  const position = positionOverride || window.A_POSITION;
  return (
    <g
      fontFamily={'"Noto Sans Symbols 2","Segoe UI Symbol","Apple Symbols","DejaVu Sans",sans-serif'}
      fontSize={B_SQ * 0.78}
      textAnchor="middle"
      style={{ userSelect: 'none' }}
    >
      {Object.entries(position).map(([sq, code]) => {
        const { cx, cy } = sqCenter(sq);
        const isWhite = code[0] === 'w';
        const isHero = sq === heroSquare;
        const isHover = sq === hovered;
        const isSel = sq === selected;
        const isHidden = hiddenPieces && hiddenPieces.has(sq);
        const lift = isHover || isSel ? -2 : 0;
        return (
          <g
            key={sq}
            onMouseEnter={() => onHover && onHover(sq)}
            onMouseLeave={() => onHover && onHover(null)}
            onClick={(e) => { e.stopPropagation(); onSelect && onSelect(isSel ? null : sq); }}
            style={{ cursor: 'pointer', transition: 'transform 240ms ' + window.ATELIER_EASE }}
            transform={`translate(0 ${lift})`}
            opacity={isHidden ? 0.08 : 1}
          >
            <rect x={sqXY(sq).x} y={sqXY(sq).y} width={B_SQ} height={B_SQ} fill="transparent" />
            <text
              x={cx}
              y={cy + B_SQ * 0.26}
              fill={isWhite ? C.ivory : C.umber}
              stroke={isWhite ? C.umber : 'none'}
              strokeWidth={isWhite ? 1.1 : 0}
              paintOrder="stroke"
              style={{
                filter: isHero
                  ? `drop-shadow(0 2px 2px rgba(42,34,27,0.25)) drop-shadow(0 0 1px ${C.brassLight})`
                  : 'drop-shadow(0 1.5px 1.5px rgba(42,34,27,0.18))',
                fontWeight: isHero ? 500 : 400,
              }}
            >
              {B_GLYPH[code]}
            </text>
          </g>
        );
      })}
    </g>
  );
}

function SelectionRing({ square, palette }) {
  const { cx, cy } = sqCenter(square);
  return (
    <g pointerEvents="none">
      <circle cx={cx} cy={cy} r="36" fill="none" stroke={palette.brassDeep} strokeWidth="0.8" strokeDasharray="2 3" opacity="0.85" />
      <circle cx={cx} cy={cy} r="40" fill="none" stroke={palette.brassDeep} strokeWidth="0.4" opacity="0.5" />
    </g>
  );
}

function HighlightRing({ square, palette }) {
  const { cx, cy } = sqCenter(square);
  return (
    <g pointerEvents="none">
      <circle cx={cx} cy={cy} r="44" fill="none" stroke={palette.brass} strokeWidth="1.6" strokeDasharray="5 4" opacity="0.9" />
      <circle cx={cx} cy={cy} r="36" fill={palette.brass} fillOpacity="0.10" stroke="none" />
    </g>
  );
}

// === HEATMAP STYLES ==========================================================
function HeatmapLayer({ data, palette, style, intensity }) {
  const C = palette;
  const k = Math.max(0, Math.min(1.5, intensity));
  if (style === 'wash') {
    return (
      <g pointerEvents="none" style={{ mixBlendMode: 'multiply' }} filter="url(#ab-bleed)" opacity={0.78 * k}>
        {Object.entries(data).map(([sq, inf]) => {
          const v = Math.abs(inf);
          if (v < 0.08) return null;
          const { cx, cy } = sqCenter(sq);
          const color = inf > 0 ? C.sienna : C.celadon;
          const r = 28 + v * 32;
          return (
            <circle key={sq} cx={cx} cy={cy} r={r} fill={color} opacity={(0.18 + v * 0.42)} />
          );
        })}
      </g>
    );
  }
  if (style === 'dotted') {
    const dots = [];
    Object.entries(data).forEach(([sq, inf]) => {
      const v = Math.abs(inf);
      if (v < 0.1) return;
      const { x, y } = sqXY(sq);
      const color = inf > 0 ? C.sienna : C.celadon;
      // density grid inside square: 1 to 6 dots per side based on v*intensity
      const n = Math.max(1, Math.min(6, Math.round(v * 6 * k)));
      // pseudo-random offsets stable per square
      let seed = sq.charCodeAt(0) * 131 + sq.charCodeAt(1) * 17;
      const rand = () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; };
      const margin = 12;
      const range = B_SQ - margin * 2;
      const count = n * n;
      for (let i = 0; i < count; i++) {
        dots.push(
          <circle
            key={sq + '-' + i}
            cx={x + margin + rand() * range}
            cy={y + margin + rand() * range}
            r={1.3 + rand() * 0.9}
            fill={color}
            opacity={0.55 + rand() * 0.3}
          />
        );
      }
    });
    return <g pointerEvents="none" opacity={0.9 * k}>{dots}</g>;
  }
  // contour
  const rings = [];
  Object.entries(data).forEach(([sq, inf]) => {
    const v = Math.abs(inf);
    if (v < 0.18) return;
    const { cx, cy } = sqCenter(sq);
    const color = inf > 0 ? C.sienna : C.celadon;
    const ringCount = Math.max(1, Math.round(v * 5 * k));
    for (let i = 0; i < ringCount; i++) {
      const r = 12 + i * 10;
      rings.push(
        <circle
          key={sq + '-' + i}
          cx={cx} cy={cy} r={r}
          fill="none"
          stroke={color}
          strokeWidth={i === 0 ? 1.1 : 0.55}
          opacity={(0.55 - i * 0.06) * k}
        />
      );
    }
  });
  return <g pointerEvents="none" style={{ mixBlendMode: 'multiply' }}>{rings}</g>;
}

// === HERO ====================================================================
function HeroLayer({ square, palette }) {
  const C = palette;
  const { cx, cy } = sqCenter(square);
  const topY = B_PAD - 4;
  const wTop = 26;
  const wBot = B_SQ * 0.7;
  return (
    <g pointerEvents="none">
      <path
        d={`M ${cx - wTop} ${topY} L ${cx + wTop} ${topY} L ${cx + wBot} ${cy + 4} L ${cx - wBot} ${cy + 4} Z`}
        fill="url(#ab-spot)" opacity="0.85"
        style={{ mixBlendMode: 'screen' }}
      />
      <ellipse cx={cx} cy={cy + 22} rx={B_SQ * 0.36} ry={B_SQ * 0.10} fill="url(#ab-shadow)" />
      <circle cx={cx} cy={cy} r="30" fill="none" stroke={C.brass} strokeWidth="0.6" opacity="0.6" />
    </g>
  );
}

// === TRAJECTORY ==============================================================
function TrajectoryLayer({ path, palette, style }) {
  const C = palette;
  if (!path || path.length < 2) return null;
  const pts = path.map(sqCenter);
  if (style === 'clean') {
    const d = pts.map((p, i) => (i ? 'L' : 'M') + p.cx + ' ' + p.cy).join(' ');
    const last = pts[pts.length - 1];
    const before = pts[pts.length - 2];
    const ang = Math.atan2(last.cy - before.cy, last.cx - before.cx);
    const ah = 9;
    return (
      <g pointerEvents="none">
        <path d={d} fill="none" stroke={C.umber} strokeWidth="1.3" strokeLinecap="round" strokeDasharray="6 5" opacity="0.85" />
        {pts.map((p, i) => (
          <circle key={i} cx={p.cx} cy={p.cy} r={i === 0 ? 4 : 3} fill={C.umber} opacity={1 - i * 0.15} />
        ))}
        <path
          d={`M ${last.cx} ${last.cy}
             L ${last.cx - Math.cos(ang - 0.45) * ah} ${last.cy - Math.sin(ang - 0.45) * ah}
             L ${last.cx - Math.cos(ang + 0.45) * ah} ${last.cy - Math.sin(ang + 0.45) * ah} Z`}
          fill={C.umber} opacity="0.85"
        />
      </g>
    );
  }
  // painterly
  let d = `M ${pts[0].cx} ${pts[0].cy}`;
  for (let i = 1; i < pts.length; i++) {
    const prev = pts[i - 1];
    const curr = pts[i];
    const mx = (prev.cx + curr.cx) / 2;
    const my = (prev.cy + curr.cy) / 2 - 22;
    d += ` Q ${mx} ${my} ${curr.cx} ${curr.cy}`;
  }
  const last = pts[pts.length - 1];
  const before = pts[pts.length - 2];
  const ang = Math.atan2(last.cy - before.cy + 22, last.cx - before.cx);
  const ah = 9;
  return (
    <g pointerEvents="none">
      <path d={d} fill="none" stroke={C.cream} strokeWidth="9" strokeLinecap="round" opacity="0.5" />
      <path d={d} fill="none" stroke="url(#ab-brush)" strokeWidth="3.5" strokeLinecap="round" />
      {pts.map((p, i) => (
        <circle key={i} cx={p.cx} cy={p.cy} r={i === 0 ? 4 : 3} fill={C.umber} opacity={1 - i * 0.2} />
      ))}
      <path
        d={`M ${last.cx} ${last.cy}
           L ${last.cx - Math.cos(ang - 0.45) * ah} ${last.cy - Math.sin(ang - 0.45) * ah}
           L ${last.cx - Math.cos(ang + 0.45) * ah} ${last.cy - Math.sin(ang + 0.45) * ah} Z`}
        fill={C.umber} opacity="0.55"
      />
    </g>
  );
}

// === TENSION =================================================================
function TensionLayer({ list, palette }) {
  const C = palette;
  return (
    <g pointerEvents="none">
      {list.flatMap((t) => {
        const def = sqCenter(t.defender);
        const overloaded = t.load > t.support;
        return t.attackers.map((atk, i) => {
          const a = sqCenter(atk.from);
          const mx = (def.cx + a.cx) / 2;
          const my = (def.cy + a.cy) / 2 + 14;
          const d = `M ${a.cx} ${a.cy} Q ${mx} ${my} ${def.cx} ${def.cy}`;
          return (
            <g key={t.defender + '-' + atk.from + i}>
              <path
                d={d} fill="none"
                stroke={overloaded ? C.sienna : C.celadon}
                strokeWidth="1.4" strokeLinecap="round" opacity="0.85"
              />
              <circle cx={a.cx} cy={a.cy} r="3.2" fill={overloaded ? C.sienna : C.celadon} opacity="0.9" />
              <circle cx={def.cx} cy={def.cy} r="3.2" fill={overloaded ? C.sienna : C.celadon} opacity="0.9" />
            </g>
          );
        });
      })}
      {list.map((t, i) => {
        const def = sqCenter(t.defender);
        const overloaded = t.load > t.support;
        return (
          <g key={'badge-' + i} transform={`translate(${def.cx + 20}, ${def.cy + 24})`}>
            <rect x="0" y="0" width="48" height="18" rx="9"
              fill={C.cream}
              stroke={overloaded ? C.sienna : C.celadon}
              strokeWidth="0.6" />
            <text x="24" y="12.5"
              fontFamily={window.ATELIER_TYPE.display}
              fontSize="10" fontStyle="italic" textAnchor="middle"
              fill={overloaded ? C.sienna : C.celadon}>
              {t.load}/{t.support} strain
            </text>
          </g>
        );
      })}
    </g>
  );
}

// === HEATMAP HOVER TOOLTIP ====================================================
// Renders a small SVG panel near the hovered square showing per-side control
// counts and the pieces that contribute to that control. Reads
// window.A_INFLUENCE_BY_PIECE — every piece's influence map already lists which
// squares it bears on, so we just transpose that data per hovered square.
function HeatmapTooltipLayer({ square, palette, position, influenceByPiece }) {
  if (!square || !influenceByPiece || !position) return null;
  const C = palette;

  const kindName = (code) => {
    const k = { K: 'king', Q: 'queen', R: 'rook', B: 'bishop', N: 'knight', P: 'pawn' };
    return k[code[1]] || code[1];
  };

  // Find all pieces whose influence map touches `square`.
  const whiteAttackers = [];
  const blackAttackers = [];
  for (const [fromSq, infMap] of Object.entries(influenceByPiece)) {
    if (!infMap || !(square in infMap)) continue;
    const code = position[fromSq];
    if (!code) continue;
    const entry = { from: fromSq, code, kind: kindName(code) };
    if (code[0] === 'w') whiteAttackers.push(entry);
    else blackAttackers.push(entry);
  }
  const wN = whiteAttackers.length;
  const bN = blackAttackers.length;
  if (wN === 0 && bN === 0) return null;

  // Position the tooltip near the hovered square, snapping to whichever side
  // has room (right edge usually, flip to left if hovered square is on the
  // right half of the board).
  const { x: sqX, y: sqY } = sqXY(square);
  const isRight = sqX > B_WIDTH / 2;
  const tw = 220;
  const th = 32 + (wN + bN) * 14 + (wN && bN ? 8 : 0);
  let tx = sqX + B_SQ + 12;
  if (isRight) tx = sqX - tw - 12;
  let ty = sqY - 6;
  if (ty + th > B_WIDTH) ty = B_WIDTH - th - 4;
  if (ty < 4) ty = 4;

  const renderLine = (entry, color, idx) => (
    <text key={color + idx}
      x={tx + 10} y={ty + 30 + idx * 14}
      fontFamily={window.ATELIER_TYPE.ui}
      fontSize="10"
      fill={C.umberSoft}
    >
      <tspan fill={color === 'w' ? C.umber : C.umberSoft} fontWeight="500">
        {color === 'w' ? '■' : '□'}
      </tspan>
      <tspan dx="6">{entry.kind} on {entry.from}</tspan>
    </text>
  );

  let cursor = 0;
  return (
    <g pointerEvents="none">
      {/* Halo around the hovered square */}
      <rect x={sqX + 1} y={sqY + 1} width={B_SQ - 2} height={B_SQ - 2}
        fill="none" stroke={C.brassDeep} strokeWidth="1.2"
        strokeDasharray="3 3" opacity="0.7" rx="2" />

      {/* Tooltip panel */}
      <rect x={tx} y={ty} width={tw} height={th}
        fill={C.cream} stroke={C.umberHair} strokeWidth="0.6"
        rx="6" opacity="0.97"
        style={{ filter: 'drop-shadow(0 4px 10px rgba(42,34,27,0.18))' }}
      />
      <text x={tx + 10} y={ty + 14}
        fontFamily={window.ATELIER_TYPE.ui}
        fontSize="10" letterSpacing="2"
        fill={C.umberFaint}
      >SQUARE · {square.toUpperCase()}</text>
      <text x={tx + tw - 10} y={ty + 14}
        fontFamily={window.ATELIER_TYPE.display}
        fontSize="11" fontStyle="italic"
        fill={wN > bN ? C.sienna : bN > wN ? C.celadon : C.umberSoft}
        textAnchor="end"
      >
        {wN === bN ? `contested ${wN}/${bN}` : `${wN > bN ? 'white +' : 'black +'}${Math.abs(wN - bN)}`}
      </text>

      {/* Lines */}
      {(() => {
        const out = [];
        whiteAttackers.forEach((e) => { out.push(renderLine(e, 'w', cursor)); cursor++; });
        if (wN && bN) cursor += 0.5; // spacer handled below visually if needed
        blackAttackers.forEach((e) => { out.push(renderLine(e, 'b', cursor)); cursor++; });
        return out;
      })()}
    </g>
  );
}

Object.assign(window, { AtelierBoard, sqXY, sqCenter, B_FILES, B_RANKS, B_SQ, B_BOARD, B_PAD, B_WIDTH, B_GLYPH });
