import type {
  BenchmarkMetricSummary,
  BenchmarkWorkerSample,
  MetricStatistics,
} from "./analysis.types";

function finite(values: readonly (number | undefined)[]): number[] {
  return values.filter(
    (value): value is number =>
      value !== undefined && Number.isFinite(value) && value >= 0,
  );
}

export function metricStatistics(
  values: readonly (number | undefined)[],
): MetricStatistics | undefined {
  const ordered = finite(values).sort((left, right) => left - right);
  if (ordered.length === 0) return undefined;
  const middle = Math.floor(ordered.length / 2);
  const median =
    ordered.length % 2 === 0
      ? ((ordered[middle - 1] ?? 0) + (ordered[middle] ?? 0)) / 2
      : (ordered[middle] ?? 0);
  const p95Index = Math.max(0, Math.ceil(ordered.length * 0.95) - 1);
  return {
    count: ordered.length,
    minimum: ordered[0] ?? 0,
    median,
    p95: ordered[p95Index] ?? ordered.at(-1) ?? 0,
    maximum: ordered.at(-1) ?? 0,
  };
}

export function summarizeBenchmarkSamples(
  samples: readonly BenchmarkWorkerSample[],
): BenchmarkMetricSummary {
  const metric = (
    select: (sample: BenchmarkWorkerSample) => number | undefined,
  ) => metricStatistics(samples.map(select));
  return {
    ...(metric((sample) => sample.compileMs)
      ? { compileMs: metric((sample) => sample.compileMs) }
      : {}),
    ...(metric((sample) => sample.firstMatchMs)
      ? { firstMatchMs: metric((sample) => sample.firstMatchMs) }
      : {}),
    ...(metric((sample) => sample.allMatchesMs)
      ? { allMatchesMs: metric((sample) => sample.allMatchesMs) }
      : {}),
    ...(metric((sample) => sample.replacementMs)
      ? { replacementMs: metric((sample) => sample.replacementMs) }
      : {}),
    ...(metric((sample) => sample.throughputBytesPerSecond)
      ? {
          throughputBytesPerSecond: metric(
            (sample) => sample.throughputBytesPerSecond,
          ),
        }
      : {}),
  };
}
