import { useTranslation } from 'react-i18next';

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;
}

function isLowCardinalityHistogram(counts: number[], p1: number, p99: number): boolean {
  return counts.length > 0 && counts.length <= 10 && p99 > p1 && p99 - p1 <= 10;
}

export function compactHistogramLabel(
  index: number,
  barCount: number,
  p1: number,
  p99: number,
  center: number,
  formatLabel: (value: number) => string,
  integerLabels = false
): string {
  const formatAxisValue = (value: number) =>
    integerLabels ? Math.round(value).toLocaleString() : formatLabel(value);

  if (barCount <= 1) return formatAxisValue(center);

  const middleBins = barCount - 2;
  if (index === 0) {
    if (!integerLabels) return `<${formatLabel(p1)}`;
    const firstBoundary = Math.ceil(p1);
    // This outlier bin holds values strictly below p1. When p1 <= 0 there are no
    // (non-negative) integers below it, so the value 0 lives in the first middle
    // bin instead. Labelling this empty bin "0" too would show "0" twice.
    if (firstBoundary <= 0) return '';
    return firstBoundary === 1 ? '0' : `<${firstBoundary.toLocaleString()}`;
  }
  if (index === barCount - 1) {
    if (!integerLabels) return `${formatLabel(p99)}+`;
    return `${Math.ceil(p99).toLocaleString()}+`;
  }

  const middleWidth = middleBins > 0 ? (p99 - p1) / middleBins : 0;
  if (Math.abs(middleWidth - 1) < 0.001) {
    return formatAxisValue(p1 + index - 1);
  }
  return formatAxisValue(center);
}

export function DualHistogram({
  localCounts,
  globalCounts,
  p1,
  p99,
  globalMean,
  meanLabel,
  formatLabel,
  compact = false,
  integerAxisLabels = false,
}: {
  localCounts: number[];
  globalCounts: number[];
  p1: number;
  p99: number;
  globalMean?: number;
  meanLabel?: string;
  formatLabel?: (value: number) => string;
  compact?: boolean;
  integerAxisLabels?: boolean;
}) {
  const { t } = useTranslation();
  const showCompactAxisLabels =
    compact &&
    isLowCardinalityHistogram(localCounts, p1, p99) &&
    isLowCardinalityHistogram(globalCounts, p1, p99);
  const targetBars = compact ? (showCompactAxisLabels ? localCounts.length : 16) : 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)));

  if (barCount === 0) return null;

  // 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;
  const showMeanMarker = meanPct != null && meanPct >= 0 && meanPct <= 100;
  const meanLabelStyle =
    showMeanMarker && meanPct < 12
      ? { left: 0 }
      : showMeanMarker && meanPct > 88
        ? { right: 0 }
        : { left: '50%', transform: 'translateX(-50%)' };

  if (compact) {
    const axisLabels = showCompactAxisLabels
      ? barCenters.map((center, index) =>
          compactHistogramLabel(index, barCount, p1, p99, center, fmt, integerAxisLabels)
        )
      : [];
    const chartTitle = [
      `${fmt(p1)} - ${fmt(p99)}`,
      globalMean != null ? `${meanLabel ?? t('areaPane.nationalAvg')}: ${fmt(globalMean)}` : null,
    ]
      .filter(Boolean)
      .join('\n');

    return (
      <div className={showCompactAxisLabels ? 'h-10' : 'h-7'} title={chartTitle}>
        <div
          className={`${showCompactAxisLabels ? 'h-7' : 'h-full'} relative flex items-end gap-[2px]`}
        >
          {Array.from({ length: barCount }).map((_, index) => {
            const globalHeight = (globalBars[index] / globalMax) * 100;
            const localHeight = (localBars[index] / localMax) * 100;
            return (
              <div key={index} className="relative flex h-full min-w-[2px] flex-1 items-end">
                <div
                  className="absolute bottom-0 left-0 right-0 rounded-t-[2px] bg-warm-300/45 dark:bg-warm-600/50"
                  style={{ height: `${Math.max(globalHeight, globalBars[index] > 0 ? 8 : 0)}%` }}
                />
                {localBars[index] > 0 && (
                  <div
                    className="absolute bottom-0 left-[22%] right-[22%] rounded-t-[2px] bg-teal-600 dark:bg-teal-400"
                    style={{ height: `${Math.max(localHeight, 12)}%` }}
                  />
                )}
              </div>
            );
          })}
        </div>
        {showCompactAxisLabels && (
          <div className="mt-0.5 flex gap-[2px]">
            {axisLabels.map((label, index) => (
              <span
                key={`${label}-${index}`}
                className="min-w-[2px] flex-1 truncate text-center text-[8px] font-medium leading-none text-warm-500 dark:text-warm-400"
                title={label}
              >
                {label}
              </span>
            ))}
          </div>
        )}
      </div>
    );
  }

  return (
    <div className="mt-1">
      <div className={showMeanMarker ? 'relative pt-5' : 'relative'}>
        <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>
            );
          })}
        </div>
        {showMeanMarker && (
          <div className="pointer-events-none absolute inset-y-0" style={{ left: `${meanPct}%` }}>
            <div
              className="absolute top-0 max-w-[7rem] truncate rounded-sm border border-warm-300 bg-white px-1 py-0.5 text-[9px] font-medium leading-none text-warm-600 shadow-sm dark:border-warm-600 dark:bg-navy-900 dark:text-warm-300"
              style={meanLabelStyle}
            >
              {meanLabel ?? t('areaPane.nationalAvg')}
            </div>
            <div className="absolute bottom-0 top-5 w-px border-l border-dashed border-warm-400 dark:border-warm-500" />
          </div>
        )}
      </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="grid min-h-10 animate-pulse grid-cols-[minmax(0,1fr)_6.5rem_minmax(3.5rem,auto)] items-center gap-3 py-1.5">
      <div className="h-3 w-24 rounded bg-warm-200 dark:bg-warm-700" />
      <div className="flex h-7 items-end gap-[2px]">
        {Array.from({ length: 12 }).map((_, i) => (
          <div
            key={i}
            className="min-w-[2px] flex-1 rounded-t-[2px] bg-warm-200 dark:bg-warm-700"
            style={{ height: `${22 + Math.sin(i * 0.7) * 28 + 30}%` }}
          />
        ))}
      </div>
      <div className="h-3 w-10 justify-self-end rounded bg-warm-200 dark:bg-warm-700" />
    </div>
  );
}

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