/**
 * Stripe Payment Error Handling Utility
 *
 * Maps Stripe decline codes to user-friendly messages for both owners and renters.
 * Provides structured error information for frontend handling.
 */

const logger = require('./logger');

const DECLINE_MESSAGES = {
  authentication_required: {
    ownerMessage: "The renter's card requires additional authentication.",
    renterMessage:
      "Your card requires authentication to complete this payment.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: false,
    requires3DS: true,
  },
  insufficient_funds: {
    ownerMessage: "The renter's card has insufficient funds.",
    renterMessage: "Your card has insufficient funds.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: false, // renter might add funds
  },
  card_declined: {
    ownerMessage: "The renter's card was declined.",
    renterMessage:
      "Your card was declined. Please contact your bank or try a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  generic_decline: {
    ownerMessage: "The renter's card was declined.",
    renterMessage: "Your card was declined. Please try a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  expired_card: {
    ownerMessage: "The renter's card has expired.",
    renterMessage: "Your card has expired. Please add a new payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  processing_error: {
    ownerMessage:
      "A payment processing error occurred. This is usually temporary.",
    renterMessage: "A temporary error occurred processing your payment.",
    canOwnerRetry: true, // Owner can retry immediately
    requiresNewPaymentMethod: false,
  },
  lost_card: {
    ownerMessage: "The renter's card cannot be used.",
    renterMessage:
      "Your card cannot be used. Please add a different payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  stolen_card: {
    ownerMessage: "The renter's card cannot be used.",
    renterMessage:
      "Your card cannot be used. Please add a different payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  incorrect_cvc: {
    ownerMessage: "Payment verification failed.",
    renterMessage:
      "Your card couldn't be verified. Please re-enter your payment details.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  invalid_cvc: {
    ownerMessage: "Payment verification failed.",
    renterMessage:
      "Your card couldn't be verified. Please re-enter your payment details.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  fraudulent: {
    ownerMessage: "This payment was blocked for security reasons.",
    renterMessage:
      "Your payment was blocked by our security system. Please try a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  incorrect_zip: {
    ownerMessage: "Renter's billing address couldn't be verified.",
    renterMessage:
      "Your billing address couldn't be verified. Please update your payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  card_velocity_exceeded: {
    ownerMessage:
      "Too many payment attempts on this card. Please try again later.",
    renterMessage:
      "Too many attempts on your card. Please wait or try a different card.",
    canOwnerRetry: true, // After delay
    requiresNewPaymentMethod: false,
  },
  do_not_honor: {
    ownerMessage: "The renter's card was declined.",
    renterMessage:
      "Your card was declined. Please contact your bank or try a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  invalid_account: {
    ownerMessage: "The renter's card account is invalid.",
    renterMessage: "Your card account is invalid. Please use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  new_account_information_available: {
    ownerMessage: "The renter's card information needs to be updated.",
    renterMessage:
      "Your card information needs to be updated. Please re-enter your payment details.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  card_not_supported: {
    ownerMessage: "The renter's card type is not supported.",
    renterMessage:
      "This card type is not supported. Please use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  currency_not_supported: {
    ownerMessage: "The renter's card doesn't support this currency.",
    renterMessage:
      "Your card doesn't support USD payments. Please use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  try_again_later: {
    ownerMessage:
      "The payment processor is temporarily unavailable. Please try again.",
    renterMessage:
      "A temporary error occurred. The owner can try again shortly.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },

  // Additional Card Declines
  call_issuer: {
    ownerMessage: "The renter needs to contact their bank.",
    renterMessage:
      "Please call your bank to authorize this transaction, then try again.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },
  do_not_try_again: {
    ownerMessage: "The renter's card has been permanently declined.",
    renterMessage:
      "This card cannot be used. Please add a different payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
    isPermanent: true,
  },
  duplicate_transaction: {
    ownerMessage: "This appears to be a duplicate charge attempt.",
    renterMessage:
      "This looks like a duplicate charge. Please wait a moment before trying again.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },
  issuer_not_available: {
    ownerMessage: "The renter's bank is temporarily unavailable.",
    renterMessage:
      "Your bank is temporarily unavailable. Please try again in a few minutes.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },
  restricted_card: {
    ownerMessage: "The renter's card has restrictions.",
    renterMessage:
      "This card has restrictions. Please contact your bank or use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  withdrawal_count_limit_exceeded: {
    ownerMessage: "The renter has exceeded their card's daily limit.",
    renterMessage:
      "You've reached your card's daily limit. Please try tomorrow or use a different card.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },
  not_permitted: {
    ownerMessage: "This transaction is not permitted on the renter's card.",
    renterMessage:
      "This transaction is not permitted on your card. Please use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  invalid_amount: {
    ownerMessage: "The payment amount is invalid.",
    renterMessage:
      "There was an issue with the payment amount. Please contact support.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: false,
  },
  security_violation: {
    ownerMessage: "The payment was blocked for security reasons.",
    renterMessage:
      "Your payment was blocked for security reasons. Please contact your bank.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  stop_payment_order: {
    ownerMessage: "A stop payment order exists on this card.",
    renterMessage:
      "A stop payment has been placed. Please contact your bank or use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  transaction_not_allowed: {
    ownerMessage: "Renter's card does not allow this transaction type.",
    renterMessage:
      "This transaction is not allowed on your card. Please use a different card.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
};

// Default error for unknown decline codes
const DEFAULT_ERROR = {
  ownerMessage: "The payment could not be processed.",
  renterMessage:
    "Your payment could not be processed. Please try a different payment method.",
  canOwnerRetry: false,
  requiresNewPaymentMethod: true,
};

// Mapping for StripeInvalidRequestError codes
const INVALID_REQUEST_MESSAGES = {
  resource_missing: {
    ownerMessage: "The renter's payment method is no longer valid.",
    renterMessage:
      "Your payment method is no longer valid. Please add a new payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  payment_method_invalid: {
    ownerMessage: "The renter's payment method is invalid.",
    renterMessage:
      "Your payment method is invalid. Please add a new payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
  payment_intent_unexpected_state: {
    ownerMessage: "This payment is in an unexpected state.",
    renterMessage: "There was an issue with your payment. Please try again.",
    canOwnerRetry: true,
    requiresNewPaymentMethod: false,
  },
  customer_deleted: {
    ownerMessage: "The renter's payment profile has been deleted.",
    renterMessage:
      "Your payment profile needs to be set up again. Please add a new payment method.",
    canOwnerRetry: false,
    requiresNewPaymentMethod: true,
  },
};

/**
 * Parse a Stripe error and return structured error information
 * @param {Error} error - The error object from Stripe
 * @returns {Object} Structured error with code, messages, and retry info
 */
function parseStripeError(error) {
  // Check if this is a Stripe error
  if (error.type === "StripeCardError" || error.code === "card_declined") {
    const declineCode = error.decline_code || error.code || "card_declined";
    const errorInfo = DECLINE_MESSAGES[declineCode] || DEFAULT_ERROR;

    // Log if we're falling back to default for an unknown decline code
    if (!DECLINE_MESSAGES[declineCode]) {
      logger.warn("[StripeErrors] Unknown decline code - please add mapping", {
        declineCode,
        errorCode: error.code,
        errorType: error.type,
        errorMessage: error.message,
      });
    }

    return {
      code: declineCode,
      ownerMessage: errorInfo.ownerMessage,
      renterMessage: errorInfo.renterMessage,
      canOwnerRetry: errorInfo.canOwnerRetry,
      requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
      // Include original error info for logging (not for users)
      _originalMessage: error.message,
      _stripeCode: error.code,
    };
  }

  // Handle other Stripe error types
  if (error.type === "StripeInvalidRequestError") {
    // First, check if there's a decline_code in the error (some bank errors include this)
    if (error.decline_code && DECLINE_MESSAGES[error.decline_code]) {
      const errorInfo = DECLINE_MESSAGES[error.decline_code];
      return {
        code: error.decline_code,
        ownerMessage: errorInfo.ownerMessage,
        renterMessage: errorInfo.renterMessage,
        canOwnerRetry: errorInfo.canOwnerRetry,
        requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
        _originalMessage: error.message,
        _stripeCode: error.code,
      };
    }

    // Check if error.code has a specific mapping
    const errorCode = error.code;
    if (errorCode && INVALID_REQUEST_MESSAGES[errorCode]) {
      const errorInfo = INVALID_REQUEST_MESSAGES[errorCode];
      return {
        code: errorCode,
        ownerMessage: errorInfo.ownerMessage,
        renterMessage: errorInfo.renterMessage,
        canOwnerRetry: errorInfo.canOwnerRetry,
        requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
        _originalMessage: error.message,
        _stripeCode: error.code,
      };
    }

    // Also check DECLINE_MESSAGES for the error code (some codes appear in both contexts)
    if (errorCode && DECLINE_MESSAGES[errorCode]) {
      const errorInfo = DECLINE_MESSAGES[errorCode];
      return {
        code: errorCode,
        ownerMessage: errorInfo.ownerMessage,
        renterMessage: errorInfo.renterMessage,
        canOwnerRetry: errorInfo.canOwnerRetry,
        requiresNewPaymentMethod: errorInfo.requiresNewPaymentMethod,
        _originalMessage: error.message,
        _stripeCode: error.code,
      };
    }

    // Log unhandled StripeInvalidRequestError codes for future mapping
    logger.warn(
      "[StripeErrors] Unhandled StripeInvalidRequestError - please add mapping",
      {
        errorCode: error.code,
        declineCode: error.decline_code,
        errorMessage: error.message,
        param: error.param,
      }
    );

    return {
      code: errorCode || "invalid_request",
      ownerMessage: "There was a problem processing this payment.",
      renterMessage: "There was a problem with your payment method.",
      canOwnerRetry: false,
      requiresNewPaymentMethod: true,
      _originalMessage: error.message,
      _stripeCode: error.code,
    };
  }

  if (
    error.type === "StripeAPIError" ||
    error.type === "StripeConnectionError"
  ) {
    return {
      code: "api_error",
      ownerMessage: "A temporary error occurred. Please try again.",
      renterMessage: "A temporary error occurred processing your payment.",
      canOwnerRetry: true,
      requiresNewPaymentMethod: false,
      _originalMessage: error.message,
      _stripeCode: error.code,
    };
  }

  if (error.type === "StripeRateLimitError") {
    return {
      code: "rate_limit",
      ownerMessage: "Too many requests. Please wait a moment and try again.",
      renterMessage: "Please wait a moment and try again.",
      canOwnerRetry: true,
      requiresNewPaymentMethod: false,
      _originalMessage: error.message,
      _stripeCode: error.code,
    };
  }

  // Default fallback for unknown errors
  logger.warn("[StripeErrors] Unknown error type - please investigate", {
    errorType: error.type,
    errorCode: error.code,
    declineCode: error.decline_code,
    errorMessage: error.message,
  });

  return {
    code: "unknown_error",
    ...DEFAULT_ERROR,
    _originalMessage: error.message,
    _stripeCode: error.code,
  };
}

/**
 * Custom PaymentError class for structured payment failures
 */
class PaymentError extends Error {
  constructor(parsedError) {
    super(parsedError.ownerMessage);
    this.name = "PaymentError";
    this.code = parsedError.code;
    this.ownerMessage = parsedError.ownerMessage;
    this.renterMessage = parsedError.renterMessage;
    this.canOwnerRetry = parsedError.canOwnerRetry;
    this.requiresNewPaymentMethod = parsedError.requiresNewPaymentMethod;
    this._originalMessage = parsedError._originalMessage;
    this._stripeCode = parsedError._stripeCode;
  }

  toJSON() {
    return {
      code: this.code,
      ownerMessage: this.ownerMessage,
      renterMessage: this.renterMessage,
      canOwnerRetry: this.canOwnerRetry,
      requiresNewPaymentMethod: this.requiresNewPaymentMethod,
    };
  }
}

module.exports = {
  parseStripeError,
  PaymentError,
  DECLINE_MESSAGES,
};
