/**
 * SET 37b — optional re-mine of solution strata from stored run records.
 * When no corpus is available, keeps the seeded solutionStrata.generated.ts.
 *
 *   Run:  npx tsx scripts/gen-solution-strata.ts [path-to-runs.json]
 *
 * Expected JSON shape (array):
 *   { classKey: string, strategy: string, resolved: boolean }[]
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const OUT = path.join(ROOT, 'src', 'tier1', 'solutionStrata.generated.ts');

type Strategy =
  | 'mechanical'
  | 'oracle-hint-repair'
  | 'draws'
  | 'lessons-repair'
  | 'surgery'
  | 'reset';

const STRATEGIES: Strategy[] = [
  'mechanical', 'oracle-hint-repair', 'draws', 'lessons-repair', 'surgery', 'reset',
];

interface MineRow {
  classKey: string;
  strategy: string;
  resolved: boolean;
}

function normalizeStrategy(s: string): Strategy | null {
  const k = s.toLowerCase().replace(/_/g, '-');
  if ((STRATEGIES as string[]).includes(k)) return k as Strategy;
  if (k === 'draw' || k === 'llm' || k === 'permutation') return 'draws';
  if (k === 'oracle' || k === 'hint') return 'oracle-hint-repair';
  if (k === 'lesson' || k === 'lessons') return 'lessons-repair';
  return null;
}

function familyOf(classKey: string): string {
  if (
    classKey === 'undefined-symbol|jest'
    || classKey === 'undefined-symbol|fn'
    || classKey === 'is-not-a-function|spyOn'
  ) return classKey;
  return classKey.split('|')[0] || classKey;
}

function main() {
  const corpusPath = process.argv[2];
  if (!corpusPath || !fs.existsSync(corpusPath)) {
    console.log('no run corpus provided — keeping seeded solutionStrata.generated.ts');
    console.log(`usage: npx tsx scripts/gen-solution-strata.ts <runs.json>`);
    process.exit(0);
  }
  const rows = JSON.parse(fs.readFileSync(corpusPath, 'utf8')) as MineRow[];
  if (!Array.isArray(rows) || !rows.length) {
    console.log('empty corpus — keeping seed');
    process.exit(0);
  }

  const byFamily = new Map<string, { total: number; byStrat: Map<Strategy, { hit: number; n: number }> }>();
  for (const r of rows) {
    const fam = familyOf(r.classKey);
    const strat = normalizeStrategy(r.strategy);
    if (!strat) continue;
    let g = byFamily.get(fam);
    if (!g) {
      g = { total: 0, byStrat: new Map() };
      byFamily.set(fam, g);
    }
    g.total++;
    const cell = g.byStrat.get(strat) ?? { hit: 0, n: 0 };
    cell.n++;
    if (r.resolved) cell.hit++;
    g.byStrat.set(strat, cell);
  }

  const grand = [...byFamily.values()].reduce((a, b) => a + b.total, 0) || 1;
  const mined = [...byFamily.entries()].map(([classFamily, g]) => {
    const resolutionByStrategy: Partial<Record<Strategy, number>> = {};
    for (const s of STRATEGIES) {
      const cell = g.byStrat.get(s);
      resolutionByStrategy[s] = cell && cell.n ? cell.hit / cell.n : 0;
    }
    return {
      classFamily,
      findingShare: g.total / grand,
      resolutionByStrategy,
      note: `mined from ${path.basename(corpusPath)} (${g.total} findings)`,
    };
  });

  // Preserve surgery baseline when invoked (~70%) if corpus never recorded it.
  for (const row of mined) {
    if (row.resolutionByStrategy.surgery == null || row.resolutionByStrategy.surgery === 0) {
      row.resolutionByStrategy.surgery = 0.70;
    }
  }

  const src = `/** AUTO-GENERATED by scripts/gen-solution-strata.ts — DO NOT EDIT. */
export type Strategy =
  | 'mechanical'
  | 'oracle-hint-repair'
  | 'draws'
  | 'lessons-repair'
  | 'surgery'
  | 'reset';

export interface SolutionStratumRow {
  classFamily: string;
  findingShare: number;
  resolutionByStrategy: Partial<Record<Strategy, number>>;
  note?: string;
}

export const SOLUTION_STRATA: SolutionStratumRow[] = ${JSON.stringify(mined, null, 2)};

const DEFAULT_ORDER: Strategy[] = [
  'mechanical', 'oracle-hint-repair', 'lessons-repair', 'draws', 'surgery', 'reset',
];

function rowForClass(classKey: string): SolutionStratumRow | undefined {
  if (!classKey) return undefined;
  const exact = SOLUTION_STRATA.find(r => r.classFamily === classKey);
  if (exact) return exact;
  const prefix = classKey.split('|')[0]!;
  return SOLUTION_STRATA.find(r => r.classFamily === prefix);
}

export function strategyOrderForClass(classKey: string): Strategy[] {
  const row = rowForClass(classKey);
  if (!row) return [...DEFAULT_ORDER];
  const rates = row.resolutionByStrategy;
  return [...DEFAULT_ORDER].sort((a, b) => (rates[b] ?? 0) - (rates[a] ?? 0));
}
`;
  fs.writeFileSync(OUT, src);
  console.log(`mined ${mined.length} families → ${OUT}`);
}

void main();
