/**
 * Semantic exit codes for the Phoenix CLI.
 *
 * These codes enable scripting and CI integration by providing meaningful
 * signals about why a command failed. Consumers can use these codes in shell
 * scripts or CI pipelines to distinguish between different failure modes.
 *
 * | Code | Meaning          | Description                                         |
 * |------|------------------|-----------------------------------------------------|
 * |  0   | Success          | Command completed successfully                      |
 * |  1   | Failure          | Unspecified or unexpected error                     |
 * |  2   | Cancelled        | User cancelled (e.g. declined a confirmation)       |
 * |  3   | Invalid argument | Bad CLI flags, missing required args, invalid input |
 * |  4   | Auth required    | Not authenticated or insufficient permissions       |
 * |  5   | Network error    | Failed to connect to server or network request      |
 * |  6   | Not verified     | The command ran, but could not confirm its effect   |
 */
export const ExitCode = {
  /** Command completed successfully */
  SUCCESS: 0,
  /** Unspecified or unexpected error */
  FAILURE: 1,
  /** User cancelled the operation */
  CANCELLED: 2,
  /** Bad CLI flags, missing required args, or invalid input */
  INVALID_ARGUMENT: 3,
  /** Not authenticated or insufficient permissions */
  AUTH_REQUIRED: 4,
  /** Failed to connect to server or network request failed */
  NETWORK_ERROR: 5,
  /**
   * The command did the work it was asked to do, but could not verify the
   * result it exists to produce. Distinct from FAILURE so a caller can tell
   * "ran, no effect observed" from "did not run" — `px setup --instrument`
   * returns it when no trace ever arrived.
   */
  NOT_VERIFIED: 6,
} as const;

export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];

export class InvalidArgumentError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "InvalidArgumentError";
  }
}

export class AuthRequiredError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "AuthRequiredError";
  }
}

export class NetworkError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "NetworkError";
  }
}

/**
 * Infer a semantic exit code from an unknown error value.
 *
 * - `InvalidArgumentError` (and subclasses like `ProfileResolutionError`)
 *   map to INVALID_ARGUMENT.
 * - The Fetch API throws a `TypeError` for low-level network failures such
 *   as connection refused or DNS resolution errors.
 * - All other errors fall back to the general {@link ExitCode.FAILURE} code.
 */
export function getExitCodeForError(error: unknown): ExitCode {
  if (error instanceof InvalidArgumentError) {
    return ExitCode.INVALID_ARGUMENT;
  }

  if (error instanceof AuthRequiredError) {
    return ExitCode.AUTH_REQUIRED;
  }

  if (error instanceof NetworkError) {
    return ExitCode.NETWORK_ERROR;
  }

  // TypeError is thrown by the Fetch API for network-level failures
  // (e.g. ECONNREFUSED, ETIMEDOUT, DNS errors).
  if (error instanceof TypeError) {
    return ExitCode.NETWORK_ERROR;
  }
  return ExitCode.FAILURE;
}
