import { HttpException, HttpStatus } from '@nestjs/common';

/**
 * Base class for all integration-related errors
 */
export abstract class IntegrationError extends HttpException {
  public readonly integrationName: string;
  public readonly originalError?: Error;
  public readonly timestamp: Date;
  public readonly retryable: boolean;

  constructor(
    integrationName: string,
    message: string,
    status: HttpStatus,
    retryable: boolean = false,
    originalError?: Error,
  ) {
    super(
      {
        statusCode: status,
        error: 'Integration Error',
        message,
        integration: integrationName,
        retryable,
        timestamp: new Date().toISOString(),
      },
      status,
    );

    this.integrationName = integrationName;
    this.originalError = originalError;
    this.timestamp = new Date();
    this.retryable = retryable;
  }
}

/**
 * Thrown when a connection to an external service cannot be established
 */
export class IntegrationConnectionError extends IntegrationError {
  constructor(integrationName: string, message: string, originalError?: Error) {
    super(
      integrationName,
      `Connection failed: ${message}`,
      HttpStatus.SERVICE_UNAVAILABLE,
      true, // Connection errors are typically retryable
      originalError,
    );
  }
}

/**
 * Thrown when authentication with an external service fails
 */
export class IntegrationAuthError extends IntegrationError {
  constructor(integrationName: string, message: string, originalError?: Error) {
    super(
      integrationName,
      `Authentication failed: ${message}`,
      HttpStatus.UNAUTHORIZED,
      false, // Auth errors usually require user intervention
      originalError,
    );
  }
}

/**
 * Thrown when rate limits are exceeded
 */
export class IntegrationRateLimitError extends IntegrationError {
  public readonly retryAfter?: number;

  constructor(
    integrationName: string,
    message: string,
    retryAfter?: number,
    originalError?: Error,
  ) {
    super(
      integrationName,
      `Rate limit exceeded: ${message}${retryAfter ? `. Retry after ${retryAfter}s` : ''}`,
      HttpStatus.TOO_MANY_REQUESTS,
      true, // Rate limit errors are retryable after waiting
      originalError,
    );
    this.retryAfter = retryAfter;
  }
}

/**
 * Thrown for general API errors from external services
 */
export class IntegrationApiError extends IntegrationError {
  public readonly apiStatusCode?: number;
  public readonly apiErrorCode?: string;

  constructor(
    integrationName: string,
    message: string,
    apiStatusCode?: number,
    apiErrorCode?: string,
    originalError?: Error,
  ) {
    // Determine if the error is retryable based on status code
    const retryable = apiStatusCode ? apiStatusCode >= 500 : false;

    // Map external status codes to appropriate HTTP status
    let status: HttpStatus;
    if (apiStatusCode) {
      if (apiStatusCode === 404) {
        status = HttpStatus.NOT_FOUND;
      } else if (apiStatusCode === 400) {
        status = HttpStatus.BAD_REQUEST;
      } else if (apiStatusCode === 403) {
        status = HttpStatus.FORBIDDEN;
      } else if (apiStatusCode >= 500) {
        status = HttpStatus.BAD_GATEWAY;
      } else {
        status = HttpStatus.BAD_REQUEST;
      }
    } else {
      status = HttpStatus.BAD_GATEWAY;
    }

    super(
      integrationName,
      `API error: ${message}`,
      status,
      retryable,
      originalError,
    );
    this.apiStatusCode = apiStatusCode;
    this.apiErrorCode = apiErrorCode;
  }
}

/**
 * Thrown when required configuration is missing
 */
export class IntegrationConfigError extends IntegrationError {
  constructor(integrationName: string, missingConfig: string[]) {
    super(
      integrationName,
      `Missing configuration: ${missingConfig.join(', ')}`,
      HttpStatus.INTERNAL_SERVER_ERROR,
      false,
    );
  }
}

/**
 * Thrown when data validation fails
 */
export class IntegrationValidationError extends IntegrationError {
  public readonly validationErrors: string[];

  constructor(
    integrationName: string,
    validationErrors: string[],
  ) {
    super(
      integrationName,
      `Validation failed: ${validationErrors.join('; ')}`,
      HttpStatus.UNPROCESSABLE_ENTITY,
      false,
    );
    this.validationErrors = validationErrors;
  }
}
