import { Injectable } from "@nestjs/common";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { matchCommonError } from "@bff/core/errors/index.js";
import {
  BaseProviderError,
  SalesforceApiError,
  SalesforceSessionExpiredError,
  SalesforceQueryError,
  SalesforceTimeoutError,
  SalesforceNetworkError,
  SalesforceRateLimitError,
  SalesforceValidationError,
  SalesforceDuplicateError,
  SalesforceAccessError,
  SalesforceStorageLimitError,
} from "@bff/integrations/common/errors/index.js";
import { DomainHttpException } from "@bff/core/http/domain-http.exception.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 sfErrorCode = firstError.errorCode || "UNKNOWN_ERROR";
    const message = firstError.message || "Salesforce operation failed";

    // Session expired
    if (
      sfErrorCode === "INVALID_SESSION_ID" ||
      sfErrorCode === "SESSION_EXPIRED" ||
      sfErrorCode === "INVALID_AUTH_HEADER"
    ) {
      throw new SalesforceSessionExpiredError(message);
    }

    // Query / not-found errors
    if (
      sfErrorCode === "NOT_FOUND" ||
      sfErrorCode === "INVALID_CROSS_REFERENCE_KEY" ||
      message.toLowerCase().includes("no rows")
    ) {
      throw new SalesforceQueryError(message);
    }

    // Validation errors
    if (
      sfErrorCode === "REQUIRED_FIELD_MISSING" ||
      sfErrorCode === "INVALID_FIELD" ||
      sfErrorCode === "MALFORMED_ID" ||
      sfErrorCode === "FIELD_CUSTOM_VALIDATION_EXCEPTION" ||
      sfErrorCode === "FIELD_INTEGRITY_EXCEPTION"
    ) {
      throw new SalesforceValidationError(message);
    }

    // Duplicate detection
    if (sfErrorCode === "DUPLICATE_VALUE" || sfErrorCode === "DUPLICATE_EXTERNAL_ID") {
      throw new SalesforceDuplicateError(message);
    }

    // Insufficient access
    if (
      sfErrorCode === "INSUFFICIENT_ACCESS" ||
      sfErrorCode === "INSUFFICIENT_ACCESS_OR_READONLY" ||
      sfErrorCode === "CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY"
    ) {
      throw new SalesforceAccessError(message);
    }

    // Rate limiting
    if (sfErrorCode === "REQUEST_LIMIT_EXCEEDED" || sfErrorCode === "TOO_MANY_REQUESTS") {
      throw new SalesforceRateLimitError(message);
    }

    // Storage limit
    if (sfErrorCode === "STORAGE_LIMIT_EXCEEDED") {
      throw new SalesforceStorageLimitError(message);
    }

    // Default
    throw new SalesforceApiError(`Salesforce ${context} failed`, message);
  }

  /**
   * Handle general request errors (network, timeout, session expired, etc.)
   */
  handleRequestError(error: unknown, context: string): never {
    const message = extractErrorMessage(error);

    // Check for session expired
    if (this.isSessionExpiredError(error)) {
      throw new SalesforceSessionExpiredError(message, error);
    }

    // Check for timeout
    if (this.isTimeoutError(error)) {
      throw new SalesforceTimeoutError(message, error);
    }

    // Check for network errors
    if (this.isNetworkError(error)) {
      throw new SalesforceNetworkError(message, error);
    }

    // Check for rate limiting
    if (this.isRateLimitError(error)) {
      throw new SalesforceRateLimitError(message, error);
    }

    // Re-throw provider errors and DomainHttpException as-is
    if (error instanceof BaseProviderError) throw error;
    if (error instanceof DomainHttpException) throw error;

    // Wrap unknown errors
    throw new SalesforceApiError(`Salesforce ${context} failed`, message, error);
  }

  /**
   * 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.";
  }
}
