/*
 * Priority Forge — storage-layer error types.
 *
 * Custom Error subclasses that HTTP route handlers can `instanceof`-check
 * to map to specific status codes. Keeps the storage layer free of
 * HTTP concerns while still giving callers enough signal to respond
 * correctly.
 *
 * CONC-002: TaskAlreadyExistsError (duplicate id on create → 409)
 * CONC-003: VersionConflictError (stale expectedVersion on update → 412) — future
 * CONC-004: TaskAlreadyClaimedError, TaskNotClaimableError — future
 */

export class TaskAlreadyExistsError extends Error {
  readonly code = 'TASK_ALREADY_EXISTS';
  readonly status = 409;
  constructor(taskId: string) {
    super(
      `Task with ID "${taskId}" already exists. Use updateTask to modify existing tasks.`,
    );
    this.name = 'TaskAlreadyExistsError';
  }
}

/**
 * CONC-003: thrown when an updateTask call's `expectedVersion` doesn't match
 * the current persisted version — indicating another writer beat us to the
 * record. Clients should re-read and retry with the current version.
 * Maps to HTTP 412 (Precondition Failed) when surfaced via the `If-Match`
 * header pattern.
 */
export class VersionConflictError extends Error {
  readonly code = 'VERSION_CONFLICT';
  readonly status = 412;
  constructor(
    readonly taskId: string,
    readonly expectedVersion: number,
    readonly currentVersion: number,
  ) {
    super(
      `Task "${taskId}" version conflict: expected ${expectedVersion}, actual ${currentVersion}. ` +
        `Re-read the task and retry with the current version.`,
    );
    this.name = 'VersionConflictError';
  }
}

/**
 * CONC-004: thrown by claimTask when the task is in a non-claimable terminal
 * state (complete / completed / cancelled). Distinct from
 * TaskAlreadyClaimedError (which signals contention, not a permanent state).
 */
export class TaskNotClaimableError extends Error {
  readonly code = 'TASK_NOT_CLAIMABLE';
  readonly status = 409;
  constructor(
    readonly taskId: string,
    readonly currentStatus: string,
  ) {
    super(
      `Task "${taskId}" is not claimable — current status is "${currentStatus}". ` +
        `Only not_started / waiting / blocked tasks can be claimed.`,
    );
    this.name = 'TaskNotClaimableError';
  }
}

/**
 * CONC-004: thrown by claimTask when the task is already claimed by a
 * different agent. The claiming agent ID is included so the caller can
 * decide whether to wait, pick a different task, or escalate.
 */
export class TaskAlreadyClaimedError extends Error {
  readonly code = 'TASK_ALREADY_CLAIMED';
  readonly status = 409;
  constructor(
    readonly taskId: string,
    readonly claimedBy: string,
  ) {
    super(
      `Task "${taskId}" is already claimed by agent "${claimedBy}". ` +
        `Pick a different task or wait for the claim to be released.`,
    );
    this.name = 'TaskAlreadyClaimedError';
  }
}
