/**
 * Stable run failure shapes when Sentinel denies (kills) a run.
 * Used by the queue processor and POST /api/runs/:id/cancel.
 */

export type SentinelFailureAction = 'deny';

export interface SentinelRunFailureOutput {
  /** Stable top-level message for clients (TUI, API consumers). */
  error: string;
  source: 'sentinel';
  action: SentinelFailureAction;
  /** Machine reason: policy_violation:<policy> | manual_cancellation | operator string */
  reason: string;
  /** Present when reason is policy_violation:<name> */
  policy?: string;
}

export type RunLogEntry = {
  timestamp: string;
  level: string;
  message: string;
};

export const SENTINEL_KILL_ERROR_PREFIX = 'Run killed by Sentinel:';

export function parsePolicyFromKillReason(reason: string): string | undefined {
  const match = reason.match(/^policy_violation:([^:]+)$/);
  return match?.[1];
}

export function buildSentinelFailureOutput(reason: string): SentinelRunFailureOutput {
  const policy = parsePolicyFromKillReason(reason);
  return {
    error: 'Run terminated by Sentinel',
    source: 'sentinel',
    action: 'deny',
    reason,
    ...(policy !== undefined && { policy }),
  };
}

export function buildSentinelFailureLog(reason: string): string {
  return `Sentinel denied run: ${reason}`;
}

/** Processor throw message — keep in sync with extractSentinelKillReason. */
export function formatSentinelKillError(reason: string): string {
  return `${SENTINEL_KILL_ERROR_PREFIX} ${reason}`;
}

export function extractSentinelKillReason(errorMessage: string): string | null {
  if (!errorMessage.startsWith(SENTINEL_KILL_ERROR_PREFIX)) {
    return null;
  }
  const reason = errorMessage.slice(SENTINEL_KILL_ERROR_PREFIX.length).trim();
  return reason.length > 0 ? reason : null;
}

export function isSentinelKillError(errorMessage: string): boolean {
  return extractSentinelKillReason(errorMessage) !== null;
}

export function appendRunLog(
  existing: RunLogEntry[] | undefined,
  entry: RunLogEntry
): RunLogEntry[] {
  return [...(existing ?? []), entry];
}

export function emitSentinelKilledSse(
  connection: { write: (chunk: string) => void },
  runId: string,
  reason: string
): void {
  const output = buildSentinelFailureOutput(reason);
  connection.write(`event: end\ndata: ${JSON.stringify({
    type: 'run_killed',
    runId,
    status: 'killed',
    error: output.error,
    output,
    timestamp: new Date().toISOString(),
  })}\n\n`);
}
