import { Injectable, HttpStatus } from "@nestjs/common";
import { ErrorCode, type ErrorCodeType } from "@customer-portal/domain/common";
import type { WhmcsErrorResponse } from "@customer-portal/domain/common/providers";
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";

/**
 * Service for handling and normalizing WHMCS API errors
 * Maps WHMCS errors to appropriate NestJS exceptions
 */
@Injectable()
export class WhmcsErrorHandlerService {
  /**
   * Handle WHMCS API error response
   */
  handleApiError(
    errorResponse: WhmcsErrorResponse,
    action: string,
    params: Record<string, unknown>
  ): never {
    const message = errorResponse.message;
    const errorCode = errorResponse.errorcode;

    const mapped = this.mapProviderErrorToDomain(action, message, errorCode, params);
    throw new DomainHttpException(mapped.code, mapped.status);
  }

  /**
   * Handle general request errors (network, timeout, etc.)
   * @param error - The error to handle
   * @param _context - Context string for error messages (kept for signature consistency)
   */
  handleRequestError(error: unknown, _context?: string): never {
    if (this.isTimeoutError(error)) {
      throw new DomainHttpException(ErrorCode.TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
    }

    if (this.isNetworkError(error)) {
      throw new DomainHttpException(ErrorCode.NETWORK_ERROR, HttpStatus.BAD_GATEWAY);
    }

    if (this.isRateLimitError(error)) {
      throw new DomainHttpException(
        ErrorCode.RATE_LIMITED,
        HttpStatus.TOO_MANY_REQUESTS,
        "WHMCS 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,
      _context ? `WHMCS ${_context} failed` : "WHMCS operation failed"
    );
  }

  private mapProviderErrorToDomain(
    action: string,
    message: string,
    providerErrorCode: string | undefined,
    params: Record<string, unknown>
  ): { code: ErrorCodeType; status: HttpStatus } {
    // 1) ValidateLogin: user credentials are wrong (expected)
    if (
      action === "ValidateLogin" &&
      this.isValidateLoginInvalidCredentials(message, providerErrorCode)
    ) {
      return { code: ErrorCode.INVALID_CREDENTIALS, status: HttpStatus.UNAUTHORIZED };
    }

    // 2) Not-found style outcomes (expected for some reads)
    if (this.isNotFoundError(action, message)) {
      return { code: ErrorCode.NOT_FOUND, status: HttpStatus.NOT_FOUND };
    }

    // 3) WHMCS API key auth failures: external service/config problem (not end-user auth)
    if (this.isAuthenticationError(message, providerErrorCode)) {
      return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.SERVICE_UNAVAILABLE };
    }

    // 4) Validation failures: treat as bad request
    if (this.isValidationError(message, providerErrorCode)) {
      return { code: ErrorCode.VALIDATION_FAILED, status: HttpStatus.BAD_REQUEST };
    }

    // 5) Default: external service error
    void params; // reserved for future mapping detail; keep signature stable
    return { code: ErrorCode.EXTERNAL_SERVICE_ERROR, status: HttpStatus.BAD_GATEWAY };
  }

  private isNotFoundError(action: string, message: string): boolean {
    const lowerMessage = message.toLowerCase();

    if (action === "GetClientsDetails" && lowerMessage.includes("client not found")) return true;
    if (
      (action === "GetInvoice" || action === "UpdateInvoice") &&
      lowerMessage.includes("invoice not found")
    ) {
      return true;
    }
    if (action === "GetClientsProducts" && lowerMessage.includes("no products found")) return true;

    return false;
  }

  /**
   * Check if error indicates authentication failure
   */
  private isAuthenticationError(message: string, errorCode?: string): boolean {
    const lowerMessage = message.toLowerCase();

    return (
      lowerMessage.includes("authentication") ||
      lowerMessage.includes("unauthorized") ||
      lowerMessage.includes("invalid credentials") ||
      lowerMessage.includes("access denied") ||
      errorCode === "AUTHENTICATION_FAILED"
    );
  }

  /**
   * Check if error indicates validation failure
   */
  private isValidationError(message: string, errorCode?: string): boolean {
    const lowerMessage = message.toLowerCase();

    return (
      lowerMessage.includes("required") ||
      lowerMessage.includes("invalid") ||
      lowerMessage.includes("not valid") ||
      lowerMessage.includes("missing") ||
      lowerMessage.includes("validation") ||
      errorCode === "VALIDATION_ERROR"
    );
  }

  /**
   * ValidateLogin returns user-facing "invalid credentials" strings (e.g. "Email or Password Invalid").
   * Treat these as authentication failures rather than external service errors.
   */
  private isValidateLoginInvalidCredentials(message: string, errorCode?: string): boolean {
    const lowerMessage = message.toLowerCase();

    // WHMCS commonly responds with: "Email or Password Invalid"
    if (lowerMessage.includes("email or password invalid")) return true;
    if (lowerMessage.includes("password invalid")) return true;
    if (lowerMessage.includes("invalid login")) return true;
    if (errorCode === "EMAIL_OR_PASSWORD_INVALID") return true;

    return false;
  }

  /**
   * Check if error is a timeout error
   */
  private 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
   */
  private 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")
    );
  }

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

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

    // Use shared error pattern matcher with billing category
    const commonResult = matchCommonError(message, "billing");
    if (commonResult.matched) {
      return commonResult.message;
    }

    return "Billing operation failed. Please try again or contact support.";
  }
}
