/**
 * SDK Error Classes
 * 
 * Domain-specific error classes for client-side error handling
 * Matches the service's error types for consistency
 */

import type { ErrorCode, ErrorType, ApiError } from './types';

export abstract class ScrapingServiceError extends Error {
  abstract readonly code: ErrorCode;
  abstract readonly type: ErrorType;

  constructor(
    message: string,
    public override readonly cause?: unknown
  ) {
    super(message);
    this.name = this.constructor.name;
  }
}

// ===== Client-Side Errors =====

export class NetworkError extends ScrapingServiceError {
  readonly code = 'INTERNAL_SERVER_ERROR' as const;
  readonly type = 'SCRAPING_ERROR' as const;

  constructor(message: string, cause?: unknown) {
    super(`Network error: ${message}`, cause);
  }
}

export class TimeoutError extends ScrapingServiceError {
  readonly code = 'TIMEOUT_ERROR' as const;
  readonly type = 'TIMEOUT_ERROR' as const;

  constructor(message: string, cause?: unknown) {
    super(`Request timeout: ${message}`, cause);
  }
}

export class ValidationError extends ScrapingServiceError {
  readonly code = 'BAD_REQUEST' as const;
  readonly type = 'VALIDATION_ERROR' as const;

  constructor(message: string, cause?: unknown) {
    super(`Validation error: ${message}`, cause);
  }
}

// ===== Service-Side Errors (from API responses) =====

export class ChapterNotFoundError extends ScrapingServiceError {
  readonly code = 'NOT_FOUND' as const;
  readonly type = 'CHAPTER_NOT_FOUND' as const;

  constructor(url: string) {
    super(`Chapter not found at URL: ${url}`);
  }
}

export class ContentNotFoundError extends ScrapingServiceError {
  readonly code = 'NOT_FOUND' as const;
  readonly type = 'CONTENT_NOT_FOUND' as const;

  constructor(message: string) {
    super(`Content not found: ${message}`);
  }
}

export class UnsupportedSiteError extends ScrapingServiceError {
  readonly code = 'BAD_REQUEST' as const;
  readonly type = 'UNSUPPORTED_SITE' as const;

  constructor(hostname: string) {
    super(`Unsupported site: ${hostname}`);
  }
}

export class BrowserError extends ScrapingServiceError {
  readonly code = 'INTERNAL_SERVER_ERROR' as const;
  readonly type = 'BROWSER_ERROR' as const;

  constructor(message: string) {
    super(`Browser error: ${message}`);
  }
}

export class RateLimitError extends ScrapingServiceError {
  readonly code = 'RATE_LIMIT_EXCEEDED' as const;
  readonly type = 'RATE_LIMIT_ERROR' as const;

  constructor(message: string) {
    super(`Rate limit exceeded: ${message}`);
  }
}

export class ServiceError extends ScrapingServiceError {
  readonly code = 'INTERNAL_SERVER_ERROR' as const;
  readonly type = 'SCRAPING_ERROR' as const;

  constructor(message: string, cause?: unknown) {
    super(`Service error: ${message}`, cause);
  }
}

// ===== Error Mapping and Utilities =====

const errorTypeToClass = {
  SCRAPING_ERROR: ServiceError,
  CHAPTER_NOT_FOUND: ChapterNotFoundError,
  CONTENT_NOT_FOUND: ContentNotFoundError,
  UNSUPPORTED_SITE: UnsupportedSiteError,
  BROWSER_ERROR: BrowserError,
  TIMEOUT_ERROR: TimeoutError,
  VALIDATION_ERROR: ValidationError,
  RATE_LIMIT_ERROR: RateLimitError,
} as const;

/**
 * Creates appropriate error instance from API error response
 */
export function createErrorFromApiResponse(apiError: ApiError): ScrapingServiceError {
  const { code, message } = apiError.error;
  const ErrorClass = errorTypeToClass[code as ErrorType];
  
  if (ErrorClass) {
    return new ErrorClass(message);
  }
  
  // Fallback to generic service error
  return new ServiceError(`API Error [${code}]: ${message}`);
}

/**
 * Type guard to check if error is a scraping service error
 */
export function isScrapingServiceError(error: unknown): error is ScrapingServiceError {
  return error instanceof ScrapingServiceError;
}

/**
 * Type guard to check if error is a specific error type
 */
export function isErrorOfType<T extends ScrapingServiceError>(
  error: unknown,
  ErrorClass: new (...args: unknown[]) => T
): error is T {
  return error instanceof ErrorClass;
}

/**
 * Extract error message from various error types
 */
export function getErrorMessage(error: unknown): string {
  if (isScrapingServiceError(error)) {
    return error.message;
  }
  
  if (error instanceof Error) {
    return error.message;
  }
  
  if (typeof error === 'string') {
    return error;
  }
  
  return 'Unknown error occurred';
}

/**
 * HTTP status code mapping for errors
 */
export const httpStatusMap: Record<ErrorCode, number> = {
  INTERNAL_SERVER_ERROR: 500,
  NOT_FOUND: 404,
  BAD_REQUEST: 400,
  UNSUPPORTED_SITE: 400,
  CONTENT_NOT_FOUND: 404,
  BROWSER_ERROR: 500,
  TIMEOUT_ERROR: 408,
  RATE_LIMIT_EXCEEDED: 429,
}; 