export interface HttpErrorOptions {
  /** The minimum number of seconds to wait before retrying the request. */
  retryAfter?: number;
  /** The raw response headers from the provider, kept so error/log handling can extract further signals. */
  responseHeaders?: Record<string, string>;
}

/**
 * Error class meant to be returned by integrations in case of exceptions. These errors will be caught and handled
 * appropriately. Any other error would result in an unhandled server error accompanied by a 500 status code.
 *
 * @field message - The error message
 * @field status - The HTTP status code to return
 * @field retryAfter - The minimum number of seconds to wait before retrying the request.
 * @field responseHeaders - The raw response headers from the provider, when the error came from a provider response.
 */
export class HttpError extends Error {
  readonly status: number;
  // Not readonly: the SDK populates these centrally in Provider.handleError after the error is built/returned.
  retryAfter: number | undefined = undefined;
  responseHeaders: Record<string, string> | undefined = undefined;

  constructor(message: string, status: number, options: HttpErrorOptions = {}) {
    super(message);
    this.status = status;
    this.name = this.constructor.name;
    this.retryAfter = options.retryAfter;
    this.responseHeaders = options.responseHeaders;
  }
}

/**
 * Used to generate a 400 Bad Request. Usually used when something is missing to properly handle the request.
 */
export class BadRequestError extends HttpError {
  constructor(message?: string) {
    super(message || 'Bad request', 400);
  }
}

/**
 * Used to generate a 401 Unauthorized. Usually used when the credentials are missing or invalid.
 */
export class UnauthorizedError extends HttpError {
  constructor(message?: string) {
    super(message || 'Unauthorized', 401);
  }
}

/**
 * Used to generate a 403 Forbidden. Usually used when user lacks sufficient permission to access a ressource.
 */
export class ForbiddenError extends HttpError {
  constructor(message?: string) {
    super(message || 'Forbidden', 403);
  }
}

/**
 * Used to generate a 404 Not Found. Usually used when the requested `Item` is not found.
 */
export class NotFoundError extends HttpError {
  constructor(message?: string) {
    super(message || 'Not found', 404);
  }
}

/**
 * Used to generate a 408 Timeout Error. Usually used when the call length exceeds the received Operation Deadline.
 */
export class TimeoutError extends HttpError {
  constructor(message?: string) {
    super(message || 'Not found', 408);
  }
}

/**
 * Used to generate a 410 Resource Gone.
 *
 * @deprecated Use ProviderInstanceLockedError instead when the provider instance is locked or unavailable.
 */
export class ResourceGoneError extends HttpError {
  constructor(message?: string) {
    super(message || 'Resource Gone', 410);
  }
}

/**
 * Used to generate a 422 Unprocessable Entity. Usually used when an operation is invalid.
 */
export class UnprocessableEntityError extends HttpError {
  constructor(message?: string) {
    super(message || 'Unprocessable Entity', 422);
  }
}

/**
 * Used to generate a 423 Provider Instance Locked.
 */
export class ProviderInstanceLockedError extends HttpError {
  constructor(message?: string, options: HttpErrorOptions = {}) {
    super(message || 'Provider instance locked or unavailable', 423, options);
  }
}

/**
 * Used to generate a 429 Rate Limit Exceeded. Usually used when an operation triggers or would trigger a rate limit
 * error on the provider's side.
 */
export class RateLimitExceededError extends HttpError {
  constructor(message?: string, options: HttpErrorOptions = {}) {
    super(message || 'Rate Limit Exceeded', 429, options);
  }
}
