import { Injectable, HttpStatus } from "@nestjs/common";
import { ErrorCode, type ErrorCodeType } from "@customer-portal/domain/common";
import { DomainHttpException } from "@bff/core/http/domain-http.exception.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { matchCommonError } from "@bff/core/errors/index.js";

/**
 * Salesforce error response structure
 */
export interface SalesforceErrorResponse {
  errorCode?: string;
  message?: string;
  fields?: string[];
}

/**
 * Service for handling and normalizing Salesforce API errors.
 * Maps Salesforce errors to appropriate NestJS exceptions.
 *
 * Mirrors the pattern used by WhmcsErrorHandlerService for consistency.
 */
@Injectable()
export class SalesforceErrorHandlerService {
  /**
   * Handle Salesforce API error response
   */
  handleApiError(
    errorResponse: SalesforceErrorResponse | SalesforceErrorResponse[],
    context: string
  ): never {
    // Salesforce can return an array of errors
    const errors = Array.isArray(errorResponse) ? errorResponse : [errorResponse];
    const firstError = errors[0] || {};

    const errorCode = firstError.errorCode || "UNKNOWN_ERROR";
    const message = firstError.message || "Salesforce operation failed";

    const mapped = this.mapSalesforceErrorToDomain(errorCode, message, context);
    throw new DomainHttpException(mapped.code, mapped.status, message);
  }

  /**
   * Handle general request errors (network, timeout, session expired, etc.)
   */
  handleRequestError(error: unknown, context: string): never {
    // Check for session expired
    if (this.isSessionExpiredError(error)) {
      throw new DomainHttpException(
        ErrorCode.EXTERNAL_SERVICE_ERROR,
        HttpStatus.SERVICE_UNAVAILABLE,
        "Salesforce session expired"
      );
    }

    // Check for timeout
    if (this.isTimeoutError(error)) {
      throw new DomainHttpException(ErrorCode.TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
    }

    // Check for network errors
    if (this.isNetworkError(error)) {
      throw new DomainHttpException(ErrorCode.NETWORK_ERROR, HttpStatus.BAD_GATEWAY);
    }

    // Check for rate limiting
    if (this.isRateLimitError(error)) {
      throw new DomainHttpException(
        ErrorCode.RATE_LIMITED,
        HttpStatus.TOO_MANY_REQUESTS,
        "Salesforce rate limit exceeded"
      );
    }

    // Re-throw if already a DomainHttpException
    if (error instanceof DomainHttpException) {
      throw error;
    }

    // Wrap unknown errors
    throw new DomainHttpException(
      ErrorCode.EXTERNAL_SERVICE_ERROR,
      HttpStatus.BAD_GATEWAY,
      `Salesforce ${context} failed`
    );
  }

  /**
   * Map Salesforce error codes to domain error codes
   */
  private mapSalesforceErrorToDomain(
    sfErrorCode: string,
    message: string,
    context: string
  ): { code: ErrorCodeType; status: HttpStatus } {
    // Not found errors
    if (
      sfErrorCode === "NOT_FOUND" ||
      sfErrorCode === "INVALID_CROSS_REFERENCE_KEY" ||
      message.toLowerCase().includes("no rows")
    ) {
      return { code: ErrorCode.NOT_FOUND, status: HttpStatus.NOT_FOUND };
    }

    // Authentication errors
    if (
      sfErrorCode === "INVALID_SESSION_ID" ||
      sfErrorCode === "SESSION_EXPIRED" ||
      sfErrorCode === "INVALID_AUTH_HEADER"
    ) {
      return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.SERVICE_UNAVAILABLE };
    }

    // Validation errors
    if (
      sfErrorCode === "REQUIRED_FIELD_MISSING" ||
      sfErrorCode === "INVALID_FIELD" ||
      sfErrorCode === "MALFORMED_ID" ||
      sfErrorCode === "FIELD_CUSTOM_VALIDATION_EXCEPTION" ||
      sfErrorCode === "FIELD_INTEGRITY_EXCEPTION"
    ) {
      return { code: ErrorCode.VALIDATION_FAILED, status: HttpStatus.BAD_REQUEST };
    }

    // Duplicate detection
    if (sfErrorCode === "DUPLICATE_VALUE" || sfErrorCode === "DUPLICATE_EXTERNAL_ID") {
      return { code: ErrorCode.ACCOUNT_EXISTS, status: HttpStatus.CONFLICT };
    }

    // Insufficient access
    if (
      sfErrorCode === "INSUFFICIENT_ACCESS" ||
      sfErrorCode === "INSUFFICIENT_ACCESS_OR_READONLY" ||
      sfErrorCode === "CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY"
    ) {
      return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.FORBIDDEN };
    }

    // Rate limiting
    if (sfErrorCode === "REQUEST_LIMIT_EXCEEDED" || sfErrorCode === "TOO_MANY_REQUESTS") {
      return { code: ErrorCode.RATE_LIMITED, status: HttpStatus.TOO_MANY_REQUESTS };
    }

    // Storage limit
    if (sfErrorCode === "STORAGE_LIMIT_EXCEEDED") {
      return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.SERVICE_UNAVAILABLE };
    }

    // Default: external service error
    void context; // reserved for future use
    return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.BAD_GATEWAY };
  }

  /**
   * Check if error is a session expired error
   */
  isSessionExpiredError(error: unknown): boolean {
    const message = extractErrorMessage(error).toLowerCase();
    return (
      message.includes("session expired") ||
      message.includes("invalid session") ||
      message.includes("invalid_session_id")
    );
  }

  /**
   * Check if error is a timeout error
   */
  isTimeoutError(error: unknown): boolean {
    const message = extractErrorMessage(error).toLowerCase();
    return (
      message.includes("timeout") ||
      message.includes("aborted") ||
      (error instanceof Error && error.name === "AbortError")
    );
  }

  /**
   * Check if error is a network error
   */
  isNetworkError(error: unknown): boolean {
    const message = extractErrorMessage(error).toLowerCase();
    return (
      message.includes("network") ||
      message.includes("connection") ||
      message.includes("econnrefused") ||
      message.includes("enotfound") ||
      message.includes("fetch failed")
    );
  }

  /**
   * Check if error is a rate limit error
   */
  isRateLimitError(error: unknown): boolean {
    const message = extractErrorMessage(error).toLowerCase();
    return (
      message.includes("rate limit") ||
      message.includes("too many requests") ||
      message.includes("request_limit_exceeded")
    );
  }

  /**
   * Get user-friendly error message
   */
  getUserFriendlyMessage(error: unknown): string {
    const message = extractErrorMessage(error);

    // Try common error patterns first
    const commonResult = matchCommonError(message, "generic");
    if (commonResult.matched) {
      return commonResult.message;
    }

    // Salesforce-specific messages
    if (this.isSessionExpiredError(error)) {
      return "Service connection expired. Please try again.";
    }

    if (message.toLowerCase().includes("no rows")) {
      return "The requested record was not found.";
    }

    return "An unexpected error occurred. Please try again later.";
  }
}
