import { Injectable, HttpStatus, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ErrorCode } 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";
import {
  BaseProviderError,
  WhmcsApiError,
  WhmcsNotFoundError,
  WhmcsAuthError,
  WhmcsInvalidCredentialsError,
  WhmcsValidationError,
  WhmcsTimeoutError,
  WhmcsNetworkError,
  WhmcsRateLimitError,
  WhmcsHttpError,
} from "@bff/integrations/common/errors/index.js";

/**
 * Service for handling and normalizing WHMCS API errors
 * Maps WHMCS errors to typed provider error classes
 */
@Injectable()
export class WhmcsErrorHandlerService {
  constructor(@Inject(Logger) private readonly logger: Logger) {}
  /**
   * Handle WHMCS API error response
   */
  handleApiError(errorResponse: WhmcsErrorResponse, action: string): never {
    const message = errorResponse.message;
    const errorCode = errorResponse.errorcode;

    // 1) ValidateLogin: user credentials are wrong (expected)
    if (action === "ValidateLogin" && this.isValidateLoginInvalidCredentials(message, errorCode)) {
      throw new WhmcsInvalidCredentialsError(message);
    }

    // 2) Not-found style outcomes (expected for some reads)
    if (this.isNotFoundError(action, message)) {
      const resource = this.getNotFoundResource(action);
      throw new WhmcsNotFoundError(resource, message);
    }

    // 3) WHMCS API key auth failures: external service/config problem (not end-user auth)
    if (this.isAuthenticationError(message, errorCode)) {
      throw new WhmcsAuthError(message);
    }

    // 4) Validation failures: treat as bad request
    if (this.isValidationError(message, errorCode)) {
      throw new WhmcsValidationError(message);
    }

    // 5) Default: external service error
    throw new WhmcsApiError("WHMCS API error", message);
  }

  /**
   * Handle general request errors (network, timeout, HTTP status errors, etc.)
   * @param error - The error to handle
   * @param _context - Context string for error messages (kept for signature consistency)
   */
  handleRequestError(error: unknown, _context?: string): never {
    const message = extractErrorMessage(error);

    if (this.isTimeoutError(error)) {
      this.logger.warn({ error: message }, "WHMCS request timeout");
      throw new WhmcsTimeoutError(message, error);
    }

    if (this.isNetworkError(error)) {
      this.logger.warn({ error: message }, "WHMCS network error");
      throw new WhmcsNetworkError(message, error);
    }

    if (this.isRateLimitError(error)) {
      throw new WhmcsRateLimitError(message, error);
    }

    // Check for HTTP status errors (e.g., "HTTP 500: Internal Server Error")
    const httpStatusError = this.parseHttpStatusError(message);
    if (httpStatusError) {
      // WARN not ERROR — these are external system issues (config, permissions, availability),
      // not bugs in our code. 401/403 during discovery is expected for some WHMCS setups.
      this.logger.warn(
        {
          upstreamStatus: httpStatusError.status,
          upstreamStatusText: httpStatusError.statusText,
          originalError: message,
        },
        "WHMCS HTTP status error"
      );

      // Map upstream HTTP status to appropriate domain error
      const mapped = this.mapHttpStatusToDomainError(httpStatusError.status);
      throw new WhmcsHttpError(
        httpStatusError.status,
        mapped.message,
        mapped.code,
        mapped.domainStatus,
        error
      );
    }

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

    // Log unhandled errors for debugging
    this.logger.error(
      {
        error: message,
        errorType: error instanceof Error ? error.constructor.name : typeof error,
        stack: error instanceof Error ? error.stack : undefined,
      },
      "WHMCS unhandled request error"
    );

    // Wrap unknown errors with context
    throw new WhmcsApiError(
      _context ? `WHMCS ${_context} failed` : "WHMCS operation failed",
      message,
      error
    );
  }

  /**
   * Parse HTTP status error from error message (e.g., "HTTP 500: Internal Server Error")
   */
  private parseHttpStatusError(message: string): { status: number; statusText: string } | null {
    const match = message.match(/^HTTP (\d{3}):\s*(.*)$/i);
    if (match?.[1]) {
      return {
        status: Number.parseInt(match[1], 10),
        statusText: match[2] || "Unknown",
      };
    }
    return null;
  }

  /**
   * Map upstream HTTP status codes to domain errors
   */
  private mapHttpStatusToDomainError(upstreamStatus: number): {
    code: typeof ErrorCode.EXTERNAL_SERVICE_ERROR | typeof ErrorCode.SERVICE_UNAVAILABLE;
    domainStatus: HttpStatus;
    message: string;
  } {
    // 4xx errors from WHMCS typically indicate config/permission issues
    if (upstreamStatus === 401 || upstreamStatus === 403) {
      return {
        code: ErrorCode.EXTERNAL_SERVICE_ERROR,
        domainStatus: HttpStatus.SERVICE_UNAVAILABLE,
        message: `WHMCS authentication/authorization failed (HTTP ${upstreamStatus}). Check API credentials and permissions.`,
      };
    }

    if (upstreamStatus === 404) {
      return {
        code: ErrorCode.EXTERNAL_SERVICE_ERROR,
        domainStatus: HttpStatus.BAD_GATEWAY,
        message: `WHMCS endpoint not found (HTTP 404). Check WHMCS configuration.`,
      };
    }

    if (upstreamStatus >= 400 && upstreamStatus < 500) {
      return {
        code: ErrorCode.EXTERNAL_SERVICE_ERROR,
        domainStatus: HttpStatus.BAD_GATEWAY,
        message: `WHMCS client error (HTTP ${upstreamStatus}). Request may be malformed.`,
      };
    }

    // 5xx errors indicate WHMCS server issues
    if (upstreamStatus === 500) {
      return {
        code: ErrorCode.EXTERNAL_SERVICE_ERROR,
        domainStatus: HttpStatus.BAD_GATEWAY,
        message: `WHMCS internal server error (HTTP 500). The WHMCS server encountered an error.`,
      };
    }

    if (upstreamStatus === 502 || upstreamStatus === 503 || upstreamStatus === 504) {
      return {
        code: ErrorCode.SERVICE_UNAVAILABLE,
        domainStatus: HttpStatus.SERVICE_UNAVAILABLE,
        message: `WHMCS service unavailable (HTTP ${upstreamStatus}). The server may be down or overloaded.`,
      };
    }

    // Default for other 5xx errors
    if (upstreamStatus >= 500) {
      return {
        code: ErrorCode.EXTERNAL_SERVICE_ERROR,
        domainStatus: HttpStatus.BAD_GATEWAY,
        message: `WHMCS server error (HTTP ${upstreamStatus}).`,
      };
    }

    // Fallback for unexpected status codes
    return {
      code: ErrorCode.EXTERNAL_SERVICE_ERROR,
      domainStatus: HttpStatus.BAD_GATEWAY,
      message: `Unexpected WHMCS response (HTTP ${upstreamStatus}).`,
    };
  }

  /**
   * Map action name to resource type for not-found errors
   */
  private getNotFoundResource(action: string): "client" | "invoice" | "product" {
    if (action === "GetClientsDetails") return "client";
    if (action === "GetInvoice" || action === "UpdateInvoice") return "invoice";
    if (action === "GetClientsProducts") return "product";
    return "client"; // default fallback
  }

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