function downsampleBars(counts: number[], targetBars: number): number[] {
  const step = Math.max(1, Math.floor(counts.length / targetBars));
  const bars: number[] = [];
  for (let index = 0; index < counts.length; index += step) {
    let sum = 0;
    for (let offset = 0; offset < step && index + offset < counts.length; offset++) {
      sum += counts[index + offset];
    }
    bars.push(sum);
  }
  return bars;
}

function pickTicks(min: number, max: number, count: number): number[] {
  if (max <= min) return [min];
  const range = max - min;
  const rawStep = range / (count - 1);
  const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
  const nice = [1, 2, 2.5, 3, 4, 5, 10].find((n) => n * magnitude >= rawStep) ?? 10;
  const step = nice * magnitude;
  const start = Math.ceil(min / step) * step;
  const ticks: number[] = [];
  for (let v = start; v <= max + step * 0.01; v += step) {
    ticks.push(v);
  }
  // Ensure at least min and max are represented
  if (ticks.length === 0) return [min, max];
  return ticks;
}

export function DualHistogram({
  localCounts,
  globalCounts,
  p1,
  p99,
  globalMean,
  formatLabel,
}: {
  localCounts: number[];
  globalCounts: number[];
  p1: number;
  p99: number;
  globalMean?: number;
  formatLabel?: (value: number) => string;
}) {
  const targetBars = 25;
  const localBars = downsampleBars(localCounts, targetBars);
  const globalBars = downsampleBars(globalCounts, targetBars);

  const barCount = Math.min(localBars.length, globalBars.length);
  const localMax = Math.max(...localBars, 1);
  const globalMax = Math.max(...globalBars, 1);

  const fmt =
    formatLabel ?? ((v: number) => (Number.isInteger(v) ? v.toLocaleString() : v.toFixed(1)));

  // Compute center value for each bar.
  // Bar 0 = low outlier, bars 1..n-2 = middle (p1 to p99), bar n-1 = high outlier.
  const middleBins = Math.max(barCount - 2, 0);
  const middleWidth = middleBins > 0 && p99 > p1 ? (p99 - p1) / middleBins : 0;
  const barCenters: number[] = Array.from({ length: barCount }, (_, i) => {
    if (i === 0) return p1; // outlier bin, label as p1
    if (i === barCount - 1) return p99; // outlier bin, label as p99
    return p1 + (i - 1 + 0.5) * middleWidth;
  });

  // Pick nice tick values and assign each to the nearest bar
  const ticks = p99 > p1 ? pickTicks(p1, p99, 6) : [];
  const tickBars = new Map<number, string>(); // bar index → label
  for (const v of ticks) {
    let bestBar = 1;
    let bestDist = Infinity;
    for (let i = 1; i < barCount - 1; i++) {
      const dist = Math.abs(barCenters[i] - v);
      if (dist < bestDist) {
        bestDist = dist;
        bestBar = i;
      }
    }
    if (!tickBars.has(bestBar)) tickBars.set(bestBar, fmt(v));
  }

  // Mean line: position as fraction across the bar area
  const meanFrac = globalMean != null && p99 > p1 ? (globalMean - p1) / (p99 - p1) : null;
  // Account for outlier bins: middle region spans bars 1..n-2
  const meanPct = meanFrac != null ? ((1 + meanFrac * middleBins) / barCount) * 100 : null;

  return (
    <div className="mt-1">
      <div className="relative flex items-end gap-px h-10">
        {Array.from({ length: barCount }).map((_, index) => {
          const globalHeight = (globalBars[index] / globalMax) * 100;
          const localHeight = (localBars[index] / localMax) * 100;
          return (
            <div key={index} className="flex-1 relative min-w-[2px] h-full flex items-end">
              <div
                className="absolute bottom-0 left-0 right-0 bg-warm-300/40 dark:bg-warm-600/40 rounded-t-sm"
                style={{ height: `${globalHeight}%` }}
              />
              <div
                className="absolute bottom-0 left-0 right-0 bg-teal-500 dark:bg-teal-400 rounded-t-sm"
                style={{
                  height: `${localHeight}%`,
                  opacity: localBars[index] > 0 ? 1 : 0.1,
                }}
              />
            </div>
          );
        })}
        {meanPct != null && meanPct >= 0 && meanPct <= 100 && (
          <div
            className="absolute bottom-0 top-0 w-px border-l border-dashed border-warm-400 dark:border-warm-500"
            style={{ left: `${meanPct}%` }}
          />
        )}
      </div>
      {tickBars.size > 0 && (
        <div className="flex gap-px mt-0.5">
          {Array.from({ length: barCount }).map((_, index) => (
            <div key={index} className="flex-1 min-w-[2px] text-center">
              {tickBars.has(index) && (
                <span className="text-[9px] leading-none text-warm-400 dark:text-warm-500">
                  {tickBars.get(index)}
                </span>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function SkeletonHistogram() {
  return (
    <div className="bg-warm-50 dark:bg-warm-800 rounded p-2 animate-pulse">
      <div className="flex justify-between items-baseline">
        <div className="h-3 w-24 bg-warm-200 dark:bg-warm-700 rounded" />
        <div className="h-3 w-10 bg-warm-200 dark:bg-warm-700 rounded" />
      </div>
      <div className="flex items-end gap-px h-10 mt-2">
        {Array.from({ length: 15 }).map((_, i) => (
          <div
            key={i}
            className="flex-1 bg-warm-200 dark:bg-warm-700 rounded-t-sm min-w-[2px]"
            style={{ height: `${20 + Math.sin(i * 0.7) * 30 + 30}%` }}
          />
        ))}
      </div>
      <div className="flex justify-between mt-1">
        <div className="h-2 w-6 bg-warm-200 dark:bg-warm-700 rounded" />
        <div className="h-2 w-6 bg-warm-200 dark:bg-warm-700 rounded" />
      </div>
    </div>
  );
}

export function LoadingSkeleton() {
  return (
    <div className="p-3 space-y-4">
      {[0, 1, 2].map((groupIdx) => (
        <div key={groupIdx}>
          <div className="h-3 w-20 bg-warm-200 dark:bg-warm-700 rounded animate-pulse mb-2" />
          <div className="space-y-3">
            {Array.from({ length: groupIdx === 0 ? 3 : 2 }).map((_, i) => (
              <SkeletonHistogram key={i} />
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}
