/**
 * Enhanced API Error with detailed classification and context
 */

export type ApiErrorType = 
  | 'NETWORK_ERROR'          // Connection failed, timeout, CORS
  | 'AUTH_ERROR'             // 401, 403 - Authentication/Authorization issues
  | 'VALIDATION_ERROR'       // 400 - Bad request, invalid data
  | 'NOT_FOUND'              // 404 - Resource not found
  | 'SERVER_ERROR'           // 500, 502, 503 - Server-side issues
  | 'RATE_LIMIT_ERROR'       // 429 - Too many requests
  | 'CANCELED_ERROR'         // Request was canceled
  | 'TIMEOUT_ERROR'          // Request timeout
  | 'UNKNOWN_ERROR';         // Everything else

export interface ApiErrorContext {
  endpoint?: string;
  method?: string;
  requestBody?: unknown;
  timestamp: string;
  statusCode?: number;
  responseText?: string;
  retryCount?: number;
  wasRetry?: boolean;
  troubleshooting?: string;
  source?: string;
  componentStack?: string;
  isRetryable?: boolean;
  isConnectivity?: boolean;
  developerHint?: string;
}

export class ApiError extends Error {
  public readonly type: ApiErrorType;
  public readonly context: ApiErrorContext;
  public readonly originalError?: Error;

  constructor(
    message: string,
    type: ApiErrorType,
    context: ApiErrorContext,
    originalError?: Error
  ) {
    super(message);
    this.name = 'ApiError';
    this.type = type;
    this.context = context;
    this.originalError = originalError;

    // Maintains proper stack trace for where our error was thrown
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, ApiError);
    }
  }

  /**
   * User-friendly message for production environments
   */
  getUserMessage(): string {
    switch (this.type) {
      case 'NETWORK_ERROR':
        return 'Unable to connect to the server. Please check your internet connection.';
      case 'AUTH_ERROR':
        return 'Authentication required. Please log in again.';
      case 'VALIDATION_ERROR':
        return 'The data you provided is invalid. Please check your input.';
      case 'NOT_FOUND':
        return 'The requested resource was not found.';
      case 'SERVER_ERROR':
        return 'Server is experiencing issues. Please try again later.';
      case 'RATE_LIMIT_ERROR':
        return 'Too many requests. Please wait a moment and try again.';
      case 'TIMEOUT_ERROR':
        return 'Request timed out. Please try again.';
      case 'CANCELED_ERROR':
        return 'Request was canceled.';
      default:
        return 'An unexpected error occurred. Please try again.';
    }
  }

  /**
   * Developer-friendly message with full context
   */
  getDeveloperMessage(): string {
    const base = `[${this.type}] ${this.message}`;
    const ctx = [
      this.context.method,
      this.context.endpoint,
      this.context.statusCode ? `status:${this.context.statusCode}` : null,
      this.context.retryCount ? `retry:${this.context.retryCount}` : null,
    ]
      .filter(Boolean)
      .join(' ');

    return ctx ? `${base} ${ctx}` : base;
  }

  /**
   * Check if this error is retryable
   */
  isRetryable(): boolean {
    const retryableTypes: ApiErrorType[] = [
      'NETWORK_ERROR',
      'SERVER_ERROR',
      'RATE_LIMIT_ERROR',
      'TIMEOUT_ERROR',
    ];
    return retryableTypes.includes(this.type);
  }

  /**
   * Check if this error indicates connectivity issues
   */
  isConnectivityIssue(): boolean {
    return this.type === 'NETWORK_ERROR' || this.type === 'TIMEOUT_ERROR';
  }

  /**
   * Get error severity for logging
   */
  getSeverity(): 'error' | 'warn' | 'info' {
    switch (this.type) {
      case 'AUTH_ERROR':
      case 'VALIDATION_ERROR':
      case 'NOT_FOUND':
        return 'warn';
      case 'RATE_LIMIT_ERROR':
      case 'CANCELED_ERROR':
        return 'info';
      default:
        return 'error';
    }
  }
}

/**
 * Type guards for error classification
 */
export function isApiError(error: unknown): error is ApiError {
  return error instanceof ApiError;
}

export function isNetworkError(error: unknown): boolean {
  return isApiError(error) && error.type === 'NETWORK_ERROR';
}

export function isAuthError(error: unknown): boolean {
  return isApiError(error) && error.type === 'AUTH_ERROR';
}

export function isRetryableError(error: unknown): boolean {
  return isApiError(error) && error.isRetryable();
}
