import {
  Injectable,
  NotFoundException,
  BadRequestException,
  UnauthorizedException,
} from "@nestjs/common";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { WhmcsErrorResponse } from "@customer-portal/domain/common";

/**
 * 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;

    // Normalize common, expected error responses to domain exceptions
    if (this.isNotFoundError(action, message)) {
      throw this.createNotFoundException(action, message, params);
    }

    if (this.isAuthenticationError(message, errorCode)) {
      throw new UnauthorizedException(`WHMCS Authentication Error: ${message}`);
    }

    if (this.isValidationError(message, errorCode)) {
      throw new BadRequestException(`WHMCS Validation Error: ${message}`);
    }

    // Generic WHMCS API error
    throw new Error(`WHMCS API Error [${action}]: ${message} (${errorCode || "unknown"})`);
  }

  /**
   * Handle general request errors (network, timeout, etc.)
   */
  handleRequestError(error: unknown, action: string, _params: Record<string, unknown>): never {
    const message = getErrorMessage(error);

    if (this.isTimeoutError(error)) {
      throw new Error(`WHMCS API timeout [${action}]: Request timed out`);
    }

    if (this.isNetworkError(error)) {
      throw new Error(`WHMCS API network error [${action}]: ${message}`);
    }

    // Re-throw the original error if it's already a known exception type
    if (this.isKnownException(error)) {
      throw error;
    }

    // Generic request error
    throw new Error(`WHMCS API request failed [${action}]: ${message}`);
  }

  /**
   * Check if error indicates a not found condition
   */
  private isNotFoundError(action: string, message: string): boolean {
    const lowerMessage = message.toLowerCase();

    // Client not found errors
    if (action === "GetClientsDetails" && lowerMessage.includes("client not found")) {
      return true;
    }

    // Invoice not found errors
    if (
      (action === "GetInvoice" || action === "UpdateInvoice") &&
      lowerMessage.includes("invoice not found")
    ) {
      return true;
    }

    // Product not found errors
    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("missing") ||
      lowerMessage.includes("validation") ||
      errorCode === "VALIDATION_ERROR"
    );
  }

  /**
   * Create appropriate NotFoundException based on action and params
   */
  private createNotFoundException(
    action: string,
    message: string,
    params: Record<string, unknown>
  ): NotFoundException {
    if (action === "GetClientsDetails") {
      const emailParam = params["email"];
      if (typeof emailParam === "string") {
        return new NotFoundException(`Client with email ${emailParam} not found`);
      }

      const clientIdParam = params["clientid"];
      const identifier =
        typeof clientIdParam === "string" || typeof clientIdParam === "number"
          ? clientIdParam
          : "unknown";

      return new NotFoundException(`Client with ID ${identifier} not found`);
    }

    if (action === "GetInvoice" || action === "UpdateInvoice") {
      const invoiceIdParam = params["invoiceid"];
      const identifier =
        typeof invoiceIdParam === "string" || typeof invoiceIdParam === "number"
          ? invoiceIdParam
          : "unknown";

      return new NotFoundException(`Invoice with ID ${identifier} not found`);
    }

    // Generic not found error
    return new NotFoundException(message);
  }

  /**
   * Check if error is a timeout error
   */
  private isTimeoutError(error: unknown): boolean {
    const message = getErrorMessage(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 = getErrorMessage(error).toLowerCase();
    return (
      message.includes("network") ||
      message.includes("connection") ||
      message.includes("econnrefused") ||
      message.includes("enotfound") ||
      message.includes("fetch")
    );
  }

  /**
   * Check if error is already a known NestJS exception
   */
  private isKnownException(error: unknown): boolean {
    return (
      error instanceof NotFoundException ||
      error instanceof BadRequestException ||
      error instanceof UnauthorizedException
    );
  }

  /**
   * Get user-friendly error message for client consumption
   */
  getUserFriendlyMessage(error: unknown): string {
    if (error instanceof NotFoundException) {
      return "The requested resource was not found.";
    }

    if (error instanceof BadRequestException) {
      return "The request contains invalid data.";
    }

    if (error instanceof UnauthorizedException) {
      return "Authentication failed. Please check your credentials.";
    }

    const message = getErrorMessage(error).toLowerCase();

    if (message.includes("timeout")) {
      return "The request timed out. Please try again.";
    }

    if (message.includes("network") || message.includes("connection")) {
      return "Network error. Please check your connection and try again.";
    }

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