import { mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';

import type { AdapterName } from './options.js';

export type OutcomeStatus = 'PASS' | 'FAIL' | 'NOT-IMPLEMENTED' | 'BLOCKED-ON-INFRA' | 'DROPPED';

export interface CapabilityOutcome {
  capability: string;
  gates: string[];
  status: OutcomeStatus;
  reason?: string;
}

export class NotImplementedError extends Error {}
export class BlockedOnInfraError extends Error {}

const outcomes = new Map<string, CapabilityOutcome>();

export function record(outcome: CapabilityOutcome): void {
  const existing = outcomes.get(outcome.capability);
  outcomes.set(outcome.capability, existing?.reason && outcome.status === 'PASS'
    ? { ...outcome, reason: existing.reason }
    : outcome);
}

export function resetOutcomes(): void {
  outcomes.clear();
}

export function notImplemented(reason: string): never {
  throw new NotImplementedError(reason);
}

export function blockedOnInfra(reason: string): never {
  throw new BlockedOnInfraError(reason);
}

export async function writeGapReports(adapter: AdapterName, baseUrl: string): Promise<void> {
  const rows = [...outcomes.values()].sort((a, b) => a.capability.localeCompare(b.capability));
  const report = { adapter, baseUrl, generatedAt: new Date().toISOString(), capabilities: rows };
  const directory = path.resolve('tests/capability/reports');
  await mkdir(directory, { recursive: true });
  await writeFile(path.join(directory, `${adapter}.json`), `${JSON.stringify(report, null, 2)}\n`);
  const markdown = [
    `# Capability gap report: ${adapter}`,
    '',
    `Base URL: \`${baseUrl}\``,
    '',
    '| Capability | Gates | Outcome | Reason |',
    '|---|---|---|---|',
    ...rows.map((row) => `| ${row.capability} | ${row.gates.join(', ')} | ${row.status} | ${row.reason?.replaceAll('|', '\\|') ?? ''}${row.status === 'PASS' ? '' : ` Leaves ${row.gates.join(', ')} unearned.`} |`),
    '',
  ].join('\n');
  await writeFile(path.join(directory, `${adapter}.md`), markdown);
}
