/**
 * SEV1: algorithm breakdown — halt the run, never paper over as complete.
 * Writes data/sev1.jsonl and optionally POSTs to RHA_ALERT_WEBHOOK.
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { JobState } from './types';

export type Sev1Code =
  | 'no-test-cases'
  | 'zero-executed'
  | 'zero-features'
  | 'zero-ir-cases'
  | 'ir-escalation-flood'
  | 'spec-collapse'
  | 'spec-cascade';

export class Sev1Error extends Error {
  readonly code: Sev1Code;
  readonly context: Record<string, unknown>;
  constructor(code: Sev1Code, message: string, context: Record<string, unknown> = {}) {
    super(message);
    this.name = 'Sev1Error';
    this.code = code;
    this.context = context;
  }
}

export interface Sev1Row {
  ts: string;
  jobId: string;
  code: Sev1Code;
  message: string;
  feature?: string;
  ledgerNode?: string;
  context: Record<string, unknown>;
}

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
const LOG = path.join(ROOT, 'data', 'sev1.jsonl');

type Emit = (kind: string, data: Record<string, unknown>) => void;

function appendRow(row: Sev1Row): void {
  fs.mkdirSync(path.dirname(LOG), { recursive: true });
  fs.appendFileSync(LOG, JSON.stringify(row) + '\n');
}

async function postWebhook(row: Sev1Row): Promise<void> {
  const url = process.env.RHA_ALERT_WEBHOOK?.trim();
  if (!url) return;
  try {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `SEV1 ${row.code} on job ${row.jobId}: ${row.message}`,
        sev1: row,
      }),
    });
    if (!res.ok) {
      // eslint-disable-next-line no-console
      console.error(`RHA_ALERT_WEBHOOK HTTP ${res.status} for SEV1 ${row.code}`);
    }
  } catch (e) {
    // eslint-disable-next-line no-console
    console.error('RHA_ALERT_WEBHOOK post failed:', e instanceof Error ? e.message : e);
  }
}

/**
 * Halt the job as failed with a SEV1 reason. Never returns — throws Sev1Error
 * after recording + notifying so callers cannot continue to "complete".
 */
export function raiseSev1(
  job: JobState,
  code: Sev1Code,
  message: string,
  context: Record<string, unknown> = {},
  emit?: Emit,
): never {
  const row: Sev1Row = {
    ts: new Date().toISOString(),
    jobId: job.id,
    code,
    message,
    feature: typeof context.featureId === 'string' ? context.featureId
      : typeof context.feature === 'string' ? context.feature : undefined,
    ledgerNode: typeof context.ledgerNode === 'string' ? context.ledgerNode : undefined,
    context,
  };
  appendRow(row);
  job.phase = 'failed';
  job.error = `SEV1 [${code}]: ${message}`;
  emit?.('sev1', { code, message, ...context });
  void postWebhook(row);
  throw new Sev1Error(code, job.error, context);
}

export function listSev1(limit = 40): Sev1Row[] {
  if (!fs.existsSync(LOG)) return [];
  try {
    const lines = fs.readFileSync(LOG, 'utf8').trim().split('\n').filter(Boolean);
    const rows: Sev1Row[] = [];
    for (const line of lines.slice(-limit)) {
      try { rows.push(JSON.parse(line) as Sev1Row); } catch { /* skip */ }
    }
    return rows.reverse();
  } catch {
    return [];
  }
}

export function sev1Count(): number {
  if (!fs.existsSync(LOG)) return 0;
  try {
    return fs.readFileSync(LOG, 'utf8').split('\n').filter(l => l.trim()).length;
  } catch {
    return 0;
  }
}
