import {
  pearsonR,
  spearmanR
} from "./chunk-PJQFMIOX.js";

// src/meta-eval/rubric-predictive-validity.ts
async function rubricPredictiveValidity(input) {
  const minSamples = input.minSamples ?? 8;
  const reduction = input.reduction ?? "latest";
  const resamples = input.bootstrapResamples ?? 500;
  const rng = makeRng(input.seed);
  const outcomes = await input.outcomes.list();
  const outcomesByRun = /* @__PURE__ */ new Map();
  for (const o of outcomes) {
    const arr = outcomesByRun.get(o.runId) ?? [];
    arr.push(o);
    outcomesByRun.set(o.runId, arr);
  }
  const observedRubrics = /* @__PURE__ */ new Set();
  for (const r of input.runs) {
    for (const k of Object.keys(r.outcome.raw)) observedRubrics.add(k);
  }
  const rubrics = input.rubrics ?? [...observedRubrics];
  const buckets = [];
  for (const r of rubrics) {
    for (const o of input.outcomeMetrics) {
      buckets.push({ rubric: r, outcome: o, xs: [], ys: [] });
    }
  }
  let joined = 0;
  let skipped = 0;
  for (const run of input.runs) {
    const os = outcomesByRun.get(run.runId);
    if (!os || os.length === 0) {
      skipped++;
      continue;
    }
    let joinedThisRun = false;
    for (const r of rubrics) {
      const x = run.outcome.raw[r];
      if (typeof x !== "number" || !Number.isFinite(x)) continue;
      for (const o of input.outcomeMetrics) {
        const values = os.map((row) => row.metrics[o]).filter((v) => typeof v === "number" && Number.isFinite(v));
        if (values.length === 0) continue;
        const y = reduce(values, os, o, reduction);
        if (y === null) continue;
        const bucket = buckets.find((b) => b.rubric === r && b.outcome === o);
        bucket.xs.push(x);
        bucket.ys.push(y);
        joinedThisRun = true;
      }
    }
    if (joinedThisRun) joined++;
  }
  const pairs = [];
  for (const b of buckets) {
    if (b.xs.length < minSamples) continue;
    const pearson = pearsonR(b.xs, b.ys);
    const spearman = spearmanR(b.xs, b.ys);
    const ci = bootstrapCi(b.xs, b.ys, resamples, rng);
    const verdict = Math.abs(spearman) >= 0.7 ? "load_bearing" : Math.abs(spearman) >= 0.4 ? "informative" : "decorative";
    pairs.push({
      rubric: b.rubric,
      outcome: b.outcome,
      n: b.xs.length,
      pearson,
      spearman,
      ci95: ci,
      verdict
    });
  }
  const byRubric = /* @__PURE__ */ new Map();
  for (const p of pairs) {
    const arr = byRubric.get(p.rubric) ?? [];
    arr.push(p);
    byRubric.set(p.rubric, arr);
  }
  const ranked = [...byRubric.entries()].map(([rubric, ps]) => {
    const best = ps.reduce((a, b) => Math.abs(b.spearman) > Math.abs(a.spearman) ? b : a);
    return {
      rubric,
      bestOutcome: best.outcome,
      spearman: best.spearman,
      pearson: best.pearson,
      n: best.n,
      verdict: best.verdict
    };
  }).sort((a, b) => Math.abs(b.spearman) - Math.abs(a.spearman));
  const rubricsWithoutData = rubrics.filter((r) => !byRubric.has(r));
  return { pairs, ranked, joinedSamples: joined, skippedRuns: skipped, rubricsWithoutData };
}
function reduce(values, outcomes, metric, kind) {
  if (values.length === 0) return null;
  if (kind === "mean") return values.reduce((s, v) => s + v, 0) / values.length;
  if (kind === "max") return Math.max(...values);
  const sorted = [...outcomes].filter((o) => typeof o.metrics[metric] === "number").sort((a, b) => b.capturedAt - a.capturedAt);
  return sorted[0]?.metrics[metric] ?? null;
}
function bootstrapCi(xs, ys, iterations, rng) {
  const n = xs.length;
  if (n < 3) return { low: Number.NaN, high: Number.NaN };
  const samples = [];
  for (let b = 0; b < iterations; b++) {
    const rx = new Array(n);
    const ry = new Array(n);
    for (let i = 0; i < n; i++) {
      const idx = Math.floor(rng() * n);
      rx[i] = xs[idx];
      ry[i] = ys[idx];
    }
    const r = pearsonR(rx, ry);
    if (Number.isFinite(r)) samples.push(r);
  }
  samples.sort((a, b) => a - b);
  if (samples.length === 0) return { low: Number.NaN, high: Number.NaN };
  return {
    low: samples[Math.floor(0.025 * samples.length)],
    high: samples[Math.min(samples.length - 1, Math.floor(0.975 * samples.length))]
  };
}
function makeRng(seed) {
  if (seed === void 0) return Math.random;
  let s = seed >>> 0;
  return () => {
    s = s + 1831565813 >>> 0;
    let t = s;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}

export {
  rubricPredictiveValidity
};
//# sourceMappingURL=chunk-X4UCIOTZ.js.map