// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

/**
 * Base error class for all Gitea SDK errors.
 */
export class GiteaError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'GiteaError';
    Object.setPrototypeOf(this, GiteaError.prototype);
  }
}

/**
 * Raised when the API returns an error response.
 */
export class APIError extends GiteaError {
  public readonly code?: string;
  public readonly status: number;
  public readonly details?: Record<string, unknown>;

  constructor(
    message: string,
    code?: string,
    status: number = 0,
    details?: Record<string, unknown>
  ) {
    super(message);
    this.name = 'APIError';
    this.code = code;
    this.status = status;
    this.details = details;
    Object.setPrototypeOf(this, APIError.prototype);
  }

  toString(): string {
    if (this.code) {
      return `[${this.code}] ${this.message}`;
    }
    return this.message;
  }
}

/**
 * Raised when authentication fails.
 */
export class AuthenticationError extends APIError {
  constructor(message: string = 'Authentication failed') {
    super(message, 'AUTH_FAILED', 401);
    this.name = 'AuthenticationError';
    Object.setPrototypeOf(this, AuthenticationError.prototype);
  }
}

/**
 * Raised when a resource is not found.
 */
export class NotFoundError extends APIError {
  public readonly resource: string;
  public readonly identifier?: string;

  constructor(resource: string, identifier?: string) {
    const message = identifier
      ? `${resource} '${identifier}' not found`
      : `${resource} not found`;
    super(message, 'NOT_FOUND', 404);
    this.name = 'NotFoundError';
    this.resource = resource;
    this.identifier = identifier;
    Object.setPrototypeOf(this, NotFoundError.prototype);
  }
}

/**
 * Raised when request validation fails.
 */
export class ValidationError extends APIError {
  public readonly field?: string;

  constructor(message: string, field?: string) {
    super(message, 'VALIDATION_FAILED', 400);
    this.name = 'ValidationError';
    this.field = field;
    Object.setPrototypeOf(this, ValidationError.prototype);
  }
}

/**
 * Raised when an upload fails.
 */
export class UploadError extends GiteaError {
  public readonly sessionId?: string;
  public readonly chunk?: number;

  constructor(message: string, sessionId?: string, chunk?: number) {
    super(message);
    this.name = 'UploadError';
    this.sessionId = sessionId;
    this.chunk = chunk;
    Object.setPrototypeOf(this, UploadError.prototype);
  }

  toString(): string {
    const parts = [this.message];
    if (this.sessionId) {
      parts.push(`session=${this.sessionId}`);
    }
    if (this.chunk !== undefined) {
      parts.push(`chunk=${this.chunk}`);
    }
    return parts.join(' ');
  }
}

/**
 * Raised when rate limit is exceeded.
 */
export class RateLimitError extends APIError {
  public readonly retryAfter?: number;

  constructor(retryAfter?: number) {
    let message = 'Rate limit exceeded';
    if (retryAfter) {
      message += `. Retry after ${retryAfter} seconds`;
    }
    super(message, 'RATE_LIMIT_EXCEEDED', 429);
    this.name = 'RateLimitError';
    this.retryAfter = retryAfter;
    Object.setPrototypeOf(this, RateLimitError.prototype);
  }
}
