import type {
  ClassifiedOutcome,
  LimitEvidence,
  LimitKind,
  ProcessOutcome,
  PublicFailureCode,
} from "./types.js";

interface LimitRule {
  readonly evidence: keyof LimitEvidence;
  readonly limit: LimitKind;
  readonly code: PublicFailureCode;
}

/**
 * Stable precedence for simultaneous observations. Runtime-independent facts
 * (output validation and explicit watchdog expiry) win over kernel heuristics.
 */
const LIMIT_PRECEDENCE: readonly LimitRule[] = [
  { evidence: "outputBytesExceeded", limit: "output_size", code: "RUNNER_OUTPUT_SIZE_LIMIT" },
  { evidence: "outputFilesExceeded", limit: "file_count", code: "RUNNER_FILE_COUNT_LIMIT" },
  { evidence: "tempBytesExceeded", limit: "temp_storage", code: "RUNNER_TEMP_LIMIT" },
  { evidence: "wallTimeExceeded", limit: "wall_time", code: "RUNNER_TIMEOUT" },
  { evidence: "memoryOom", limit: "memory", code: "RUNNER_MEMORY_LIMIT" },
  { evidence: "swapExceeded", limit: "swap", code: "RUNNER_SWAP_LIMIT" },
  { evidence: "pidsExceeded", limit: "pids", code: "RUNNER_PID_LIMIT" },
  { evidence: "cpuTimeExceeded", limit: "cpu", code: "RUNNER_CPU_LIMIT" },
];

export function classifyOutcome(outcome: ProcessOutcome): ClassifiedOutcome {
  const evidence = outcome.limits ?? {};
  for (const rule of LIMIT_PRECEDENCE) {
    if (evidence[rule.evidence] === true) {
      return { code: rule.code, limit: rule.limit, retryable: false };
    }
  }
  if (outcome.cancellationRequested === true) {
    return { code: "RUNNER_CANCELLED", limit: null, retryable: false };
  }
  if (outcome.exitCode === 0 && outcome.signal == null) {
    return { code: "OK", limit: null, retryable: false };
  }
  return { code: "RUNNER_FAILED", limit: null, retryable: false };
}
