/**
 * Gestapeltes Balkendiagramm: DATA-Einträge mit gleichem groupKey bilden eine Säule,
 * Segmente stapeln sich entlang Y (Reihenfolge = Tabellenreihenfolge innerhalb der Gruppe).
 * @param {(v: number) => number} valueToNorm — gleiche Skala wie die Balkenhöhen (axisY min/max).
 */
/** Etwas breiter als Straight-Spanne: nutzt seitlichen Rand im Plot und lässt größere Gruppenlücken zu. */
const STACKED_SPAN_STRETCH = 1.2;

function scaleToStraightSpan(layout, barWidth, straightSpacing, barCount) {
  const span = layout.chartSpan;
  if (!(span > 1e-8)) return;
  const base = barCount > 1 ? (barCount - 1) * straightSpacing + barWidth : barWidth;
  const target = base * STACKED_SPAN_STRETCH;
  const k = target / span;
  if (!(k > 0) || !Number.isFinite(k)) return;
  const n = layout.barX.length;
  for (let i = 0; i < n; i++) layout.barX[i] *= k;
  for (let g = 0; g < layout.G; g++) layout.groupCenterX[g] *= k;
  layout.chartSpan *= k;
  layout.groupPitch *= k;
}

export function buildStackedLayout(DATA, barWidth, barScale, straightSpacing, valueToNorm, maxBarHeight = 4) {
  const n = DATA.length;
  const keyOrder = [];
  const keyToIndices = new Map();
  for (let i = 0; i < n; i++) {
    const raw = DATA[i].groupKey;
    const key = raw != null && String(raw).trim() !== '' ? String(raw).trim() : `__singleton_${i}`;
    if (!keyToIndices.has(key)) {
      keyToIndices.set(key, []);
      keyOrder.push(key);
    }
    keyToIndices.get(key).push(i);
  }
  const G = keyOrder.length;
  const groupGap = 0.52 * Math.max(0.25, barScale);
  const groupWidths = keyOrder.map(() => barWidth);
  const groupCenterX = new Array(G);
  groupCenterX[0] = 0;
  for (let g = 1; g < G; g++) {
    groupCenterX[g] = groupCenterX[g - 1] + groupWidths[g - 1] / 2 + groupGap + groupWidths[g] / 2;
  }
  const firstLeft = groupCenterX[0] - groupWidths[0] / 2;
  const lastRight = groupCenterX[G - 1] + groupWidths[G - 1] / 2;
  const midX = (firstLeft + lastRight) / 2;
  for (let g = 0; g < G; g++) groupCenterX[g] -= midX;
  const maxGroupW = Math.max(...groupWidths);
  const groupPitch = maxGroupW + groupGap;

  const barX = new Array(n);
  const stackBottomY = new Array(n);
  const groupDisplayLabels = [];
  const slotInGroup = new Array(n);

  for (let g = 0; g < G; g++) {
    const key = keyOrder[g];
    const indices = keyToIndices.get(key);
    const cx = groupCenterX[g];
    let bottom = 0;
    for (let s = 0; s < indices.length; s++) {
      const dataIdx = indices[s];
      barX[dataIdx] = cx;
      stackBottomY[dataIdx] = bottom;
      slotInGroup[dataIdx] = s;
      const segH = valueToNorm(DATA[dataIdx].value) * maxBarHeight;
      bottom += segH;
    }
    const isSingleton = key.startsWith('__singleton_');
    const label0 = DATA[indices[0]]?.label || '';
    groupDisplayLabels.push(isSingleton ? label0 : key);
  }

  let minX = Infinity;
  let maxX = -Infinity;
  for (let i = 0; i < n; i++) {
    const x = barX[i];
    minX = Math.min(minX, x - barWidth / 2);
    maxX = Math.max(maxX, x + barWidth / 2);
  }
  if (!Number.isFinite(minX)) minX = -1;
  if (!Number.isFinite(maxX)) maxX = 1;

  const counts = keyOrder.map((k) => keyToIndices.get(k).length);
  const Smax = Math.max(1, ...counts);
  const groupRowIndices = keyOrder.map((k) => keyToIndices.get(k).slice());

  const layout = {
    G,
    Smax,
    barX,
    stackBottomY,
    groupCenterX,
    groupDisplayLabels,
    groupPitch,
    chartSpan: maxX - minX,
    slotInGroup,
    groupRowIndices,
  };
  /* Match straight-bar width feel using **group count** (one X slot per stack), not row count — else few stacks are stretched to a “virtual” many-bar span. */
  scaleToStraightSpan(layout, barWidth, straightSpacing, Math.max(layout.G, 1));
  return layout;
}
