import type { ClassifiedRun } from "../../../../core/src/classify/classifier.js";

export const FAULT_BOUNDARIES = [
  "persistence",
  "network",
  "queue",
  "webhook",
  "clock",
  "cache",
  "process",
] as const;

export const FAULT_KINDS = ["error", "timeout", "partial", "crash-between"] as const;

export type FaultBoundary = (typeof FAULT_BOUNDARIES)[number];
export type FaultKind = (typeof FAULT_KINDS)[number];

export type FaultSpec = {
  boundary: FaultBoundary;
  kind: FaultKind;
  at: string;
};

export type OwnedResourceRecord = {
  id: string;
  kind: string;
  released: boolean;
};

export type PostTerminalEvent = {
  kind: string;
  at: string;
};

export type LifecycleEvidence = {
  ownedResources: OwnedResourceRecord[];
  terminalReached: boolean;
  terminalEvent?: string;
  postTerminalEvents: PostTerminalEvent[];
  retryAttempts: number;
  consumptionPeaks: Record<string, number>;
};

export type AtomicityOracleVerdict = {
  holds: boolean;
  evidence: Record<string, unknown>;
  violated?: string[];
};

export type AtomicityOracleContext = {
  fault: FaultSpec;
  lifecycle: LifecycleEvidence;
  outcome: unknown;
  error?: string;
};

export type AtomicityOracle = {
  id: string;
  assertAtomicity: (context: AtomicityOracleContext) => AtomicityOracleVerdict;
};

export type FaultBoundaryCall = {
  boundary: FaultBoundary;
  at: string;
  proceed: () => Promise<unknown>;
};

export type FaultRunContext<TState> = {
  state: TState;
  callBoundary: (call: FaultBoundaryCall) => Promise<unknown>;
  signalTerminal: (event: string) => void;
  acquireResource: (id: string, kind: string) => void;
  releaseResource: (id: string) => void;
  recordPostTerminalEvent: (kind: string) => void;
  recordRetryAttempt: () => void;
  recordConsumptionPeak: (dimension: string, value: number) => void;
  isTerminal: () => boolean;
};

export type FaultInjectableSystem<TState> = {
  id: string;
  createState: () => TState | Promise<TState>;
  run: (ctx: FaultRunContext<TState>) => Promise<unknown>;
  snapshotLifecycle: (state: TState) => LifecycleEvidence;
};

export type FaultCoverageGap = {
  reason: "missing-atomicity-oracle";
};

export type FaultInjectionResult = {
  id: string;
  fault: FaultSpec;
  holds: boolean;
  lifecycle: LifecycleEvidence;
  outcome?: unknown;
  oracleVerdict?: AtomicityOracleVerdict;
  coverageGap?: FaultCoverageGap;
  classified: ClassifiedRun;
  laneEligibility: "advisory" | "blocking-eligible" | "blocking";
  error?: string;
};
