import type {
  AgentRunStatus,
  AttemptStatus,
  ChangeSetStatus,
  FactoryRunStatus,
  ProjectStatus,
  ReviewStatus,
  TaskStatus,
} from "./model.js";

export class IllegalTransitionError extends Error {
  constructor(
    readonly entity: string,
    readonly from: string,
    readonly to: string,
  ) {
    super(`Illegal ${entity} transition: ${from} -> ${to}`);
  }
}
const allowed = <T extends string>(
  entity: string,
  graph: Readonly<Record<T, readonly T[]>>,
  from: T,
  to: T,
): T => {
  if (!graph[from]?.includes(to)) throw new IllegalTransitionError(entity, from, to);
  return to;
};
const projectTransitions: Readonly<Record<ProjectStatus, readonly ProjectStatus[]>> = {
  draft: ["onboarding", "active", "archived"],
  onboarding: ["active", "paused", "archived"],
  active: ["paused", "archived"],
  paused: ["active", "archived"],
  archived: [],
};
const taskTransitions: Readonly<Record<TaskStatus, readonly TaskStatus[]>> = {
  planned: ["ready", "blocked", "cancelled"],
  ready: ["queued", "blocked", "cancelled"],
  queued: ["executing", "blocked", "cancelled"],
  executing: ["waiting", "blocked", "review", "completed", "failed", "cancelled"],
  waiting: ["executing", "blocked", "cancelled", "failed"],
  blocked: ["ready", "queued", "executing", "cancelled", "failed"],
  review: ["correction", "completed", "blocked", "cancelled"],
  correction: ["queued", "executing", "review", "cancelled", "failed"],
  completed: [],
  cancelled: [],
  failed: [],
};
const factoryTransitions: Readonly<Record<FactoryRunStatus, readonly FactoryRunStatus[]>> = {
  planned: ["queued", "cancelled"],
  queued: ["starting", "cancelling", "blocked"],
  starting: ["running", "waiting", "blocked", "failed", "retryable", "cancelling"],
  running: ["waiting", "blocked", "cancelling", "failed", "retryable", "resolving", "completed"],
  waiting: ["running", "blocked", "cancelling", "failed", "retryable", "resolving"],
  blocked: ["queued", "running", "cancelling", "failed", "retryable", "resolving"],
  cancelling: ["cancelled", "failed"],
  cancelled: [],
  failed: ["retryable", "resolving"],
  retryable: ["queued", "starting", "resolving", "cancelled"],
  resolving: ["queued", "running", "failed", "completed", "cancelled"],
  completed: [],
};
const agentTransitions: Readonly<Record<AgentRunStatus, readonly AgentRunStatus[]>> = {
  planned: ["queued", "cancelled"],
  queued: ["active", "waiting", "cancelled", "failed"],
  active: ["waiting", "completed", "cancelled", "failed"],
  waiting: ["active", "completed", "cancelled", "failed"],
  completed: [],
  cancelled: [],
  failed: [],
};

const attemptTransitions: Readonly<Record<AttemptStatus, readonly AttemptStatus[]>> = {
  created: ["provisioning", "terminal"],
  provisioning: ["running", "terminal"],
  running: ["checkpointing", "terminal"],
  checkpointing: ["running", "terminal"],
  terminal: [],
};

const changeTransitions: Readonly<Record<ChangeSetStatus, readonly ChangeSetStatus[]>> = {
  collecting: ["candidate-ready", "cancelled"],
  "candidate-ready": ["publishing", "reviewing", "verifying", "cancelled"],
  publishing: ["published", "publication-failed", "cancelled"],
  published: ["reviewing", "verifying", "cancelled"],
  reviewing: ["verifying", "ready-to-merge", "changes-requested", "superseded", "cancelled"],
  verifying: ["reviewing", "ready-to-merge", "changes-requested", "superseded", "cancelled"],
  "ready-to-merge": ["merged", "changes-requested", "superseded", "cancelled"],
  merged: [],
  "changes-requested": ["collecting", "candidate-ready", "superseded", "cancelled"],
  "publication-failed": ["publishing", "superseded", "cancelled"],
  superseded: [],
  cancelled: [],
};
const reviewTransitions: Readonly<Record<ReviewStatus, readonly ReviewStatus[]>> = {
  requested: ["reviewing"],
  reviewing: ["submitted"],
  submitted: [],
};
export const transitionProject = (from: ProjectStatus, to: ProjectStatus) =>
  allowed("Project", projectTransitions, from, to);
export const transitionTask = (from: TaskStatus, to: TaskStatus) =>
  allowed("Task", taskTransitions, from, to);
export const transitionFactoryRun = (from: FactoryRunStatus, to: FactoryRunStatus) =>
  allowed("FactoryRun", factoryTransitions, from, to);
export const transitionAgentRun = (from: AgentRunStatus, to: AgentRunStatus) =>
  allowed("AgentRun", agentTransitions, from, to);
export const transitionAttempt = (from: AttemptStatus, to: AttemptStatus) =>
  allowed("Attempt", attemptTransitions, from, to);
export const transitionChangeSet = (from: ChangeSetStatus, to: ChangeSetStatus) =>
  allowed("ChangeSet", changeTransitions, from, to);
export const transitionReview = (from: ReviewStatus, to: ReviewStatus) =>
  allowed("Review", reviewTransitions, from, to);
