export type AgentRunState =
  | "queued"
  | "repo_discovery"
  | "running"
  | "awaiting_human"
  | "pr_retry_queued"
  | "blocked_no_github"
  | "complete"
  | "failed";

// States the worker actively ticks. `blocked_no_github` is intentionally
// excluded: those rows sit dormant until a GitHub install webhook or manual
// restart requeues them. `pr_retry_queued` is a failed run that a human asked
// to re-deliver: the tick re-runs PR delivery from the patch already on record
// (no re-investigation).
export const ACTIVE_STATES: readonly AgentRunState[] = [
  "queued",
  "repo_discovery",
  "running",
  "awaiting_human",
  "pr_retry_queued",
] as const;

export const TERMINAL_STATES: readonly AgentRunState[] = ["complete", "failed"] as const;

export const DORMANT_STATES: readonly AgentRunState[] = ["blocked_no_github"] as const;

export function isActiveState(state: AgentRunState | string): boolean {
  return (ACTIVE_STATES as readonly string[]).includes(state);
}

export type LifecycleEventKind =
  | "agent_run_queued"
  | "agent_run_started"
  | "awaiting_human"
  | "blocked_no_github"
  | "unblocked"
  | "resumed"
  | "terminal_failure"
  | "agent_run_pr_retry_queued"
  | "pr_opened"
  | "merged_into_incident"
  | "agent_run_completed"
  | "repo_selected"
  | "incident_context_changed";

export class IllegalAgentRunTransitionError extends Error {
  constructor(method: string, from: string, allowedFrom: readonly AgentRunState[]) {
    super(
      `${method}: cannot transition from "${from}"; allowed source states: ${allowedFrom.join(", ")}`,
    );
    this.name = "IllegalTransitionError";
  }
}

export function assertAgentRunSourceState(
  method: string,
  current: AgentRunState | string,
  allowed: readonly AgentRunState[],
): void {
  if (!(allowed as readonly string[]).includes(current)) {
    throw new IllegalAgentRunTransitionError(method, current, allowed);
  }
}
