import type { ApiError, ValidationDetail, ErrorCodeType } from './types';

/**
 * API client error with typed error information.
 * Provides convenient methods for accessing validation errors.
 */
export class ApiClientError extends Error {
  /** HTTP status code */
  readonly status: number;
  /** Machine-readable error code */
  readonly code: string;
  /** Optional validation details or other error details */
  readonly details?: ValidationDetail[] | Record<string, unknown>;
  /** The original response */
  readonly response?: Response;

  constructor(apiError: ApiError, response?: Response) {
    super(apiError.message);
    this.name = 'ApiClientError';
    this.status = apiError.status;
    this.code = apiError.code;
    this.details = apiError.details;
    this.response = response;

    // Maintains proper stack trace in V8 environments
    if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {
      Error.captureStackTrace(this, ApiClientError);
    }
  }

  /**
   * Check if this is a validation error.
   */
  isValidationError(): boolean {
    return this.code === 'VALIDATION_ERROR' || this.code === 'BAD_REQUEST';
  }

  /**
   * Check if this is an authentication error.
   */
  isAuthError(): boolean {
    return this.status === 401 || this.code === 'UNAUTHORIZED';
  }

  /**
   * Check if this is a permission error.
   */
  isForbiddenError(): boolean {
    return this.status === 403 || this.code === 'FORBIDDEN';
  }

  /**
   * Check if this is a not found error.
   */
  isNotFoundError(): boolean {
    return this.status === 404 || this.code === 'NOT_FOUND';
  }

  /**
   * Check if the error has a specific code.
   */
  hasCode(code: ErrorCodeType | string): boolean {
    return this.code === code;
  }

  /**
   * Get validation errors as an array.
   * Returns empty array if no validation details are present.
   */
  getValidationErrors(): ValidationDetail[] {
    if (!this.details) return [];
    if (Array.isArray(this.details)) return this.details;
    return [];
  }

  /**
   * Get validation errors mapped by field name.
   * Useful for displaying errors next to form fields.
   *
   * @example
   * const errors = error.getFieldErrors();
   * // { email: 'email is required', password: 'password must be at least 8 characters' }
   */
  getFieldErrors(): Record<string, string> {
    const errors: Record<string, string> = {};
    for (const detail of this.getValidationErrors()) {
      errors[detail.field] = detail.message;
    }
    return errors;
  }

  /**
   * Get the error message for a specific field.
   * Returns undefined if no error for that field.
   */
  getFieldError(field: string): string | undefined {
    const errors = this.getValidationErrors();
    return errors.find((e) => e.field === field)?.message;
  }

  /**
   * Check if a specific field has an error.
   */
  hasFieldError(field: string): boolean {
    return this.getFieldError(field) !== undefined;
  }
}

/**
 * Type guard for ApiClientError.
 */
export function isApiClientError(error: unknown): error is ApiClientError {
  return error instanceof ApiClientError;
}
