<?php
/**
 * PayPal Helper Class.
 *
 * @package sureforms-pro
 * @since 2.4.0
 */

namespace SRFM_Pro\Inc\Business\Payments\PayPal;

use SRFM\Inc\Payments\Payment_Helper;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * PayPal Helper.
 *
 * @since 2.4.0
 */
class Helper {
	/**
	 * Retrieve the middleware base URL for PayPal API communication.
	 *
	 * By default, returns the production middleware URL that securely proxies requests
	 * between the plugin and PayPal's API.
	 *
	 * Developers working in local or staging environments can override the SRFM_MIDDLEWARE_BASE_URL
	 * constant (for example, set it to "http://sureforms-payments-middleware.test") to point
	 * to a locally running payments middleware app (e.g., http://sureforms-payments-middleware.test/payments/paypal/).
	 *
	 * You can also modify the return value or use a filter hook to customize the URL as needed
	 * for testing, debugging, or customizing payment flows during development.
	 *
	 * @since 2.4.0
	 * @return string The middleware base URL.
	 */
	public static function middle_ware_base_url() {
		return SRFM_MIDDLEWARE_BASE_URL . 'payments/paypal/';
	}

	/**
	 * Get PayPal API base URL for the current mode.
	 *
	 * @param string|null $mode The payment mode ('test' or 'live'). If null, uses current mode.
	 * @since 2.4.0
	 * @return string The API base URL.
	 */
	public static function get_api_base_url( $mode = null ) {
		if ( null === $mode ) {
			$mode = Payment_Helper::get_payment_mode();
		}

		return 'live' === $mode
			? 'https://api.paypal.com/'
			: 'https://api.sandbox.paypal.com/';
	}

	/**
	 * Get all PayPal settings from payment settings.
	 *
	 * Retrieves the complete PayPal settings array from the nested structure:
	 * payment_settings -> paypal
	 *
	 * @since 2.4.0
	 * @return array The PayPal settings array, or default settings if not found.
	 */
	public static function get_all_paypal_settings() {
		$paypal_settings = Payment_Helper::get_gateway_settings( 'paypal' );

		// Return default settings if empty.
		return ! empty( $paypal_settings ) ? $paypal_settings : self::get_default_paypal_settings();
	}

	/**
	 * Update all PayPal settings in payment settings.
	 *
	 * Stores the complete PayPal settings array in the nested structure:
	 * payment_settings -> paypal
	 *
	 * @param array $settings The PayPal settings array to save.
	 * @since 2.4.0
	 * @return bool True on success, false on failure.
	 */
	public static function update_all_paypal_settings( $settings ) {
		if ( ! is_array( $settings ) ) {
			return false;
		}

		return Payment_Helper::update_gateway_settings( 'paypal', $settings );
	}

	/**
	 * Get default PayPal settings structure.
	 *
	 * @since 2.4.0
	 * @return array Default PayPal settings array.
	 */
	public static function get_default_paypal_settings() {
		return [
			'paypal_sandbox_connected'  => false,
			'paypal_live_connected'     => false,
			'paypal_account_id'         => '',
			'paypal_account_email'      => '',
			'paypal_live_client_id'     => '',
			'paypal_live_client_secret' => '',
			'paypal_test_client_id'     => '',
			'paypal_test_client_secret' => '',
			'webhook_test_secret'       => '',
			'webhook_test_url'          => '',
			'webhook_test_id'           => '',
			'webhook_live_secret'       => '',
			'webhook_live_url'          => '',
			'webhook_live_id'           => '',
			'account_name'              => '',
		];
	}

	/**
	 * Get the PayPal settings page URL.
	 *
	 * @since 2.4.0
	 * @return string The URL to the PayPal settings page.
	 */
	public static function get_paypal_settings_url() {
		return admin_url( 'admin.php?page=sureforms_form_settings&tab=payments-settings&subpage=payment-methods&gateway=paypal' );
	}

	/**
	 * Get the PayPal BN code.
	 *
	 * @since 2.4.0
	 * @return string The PayPal BN code.
	 */
	public static function paypal_bn_code() {
		return 'BrainstormForceInc_Cart_PPCPBran';
	}

	/**
	 * Check if PayPal is connected for sandbox/test mode.
	 *
	 * @since 2.4.0
	 * @return bool True if PayPal sandbox is connected, false otherwise.
	 */
	public static function is_paypal_sandbox_connected() {
		$settings = self::get_all_paypal_settings();
		return ! empty( $settings['paypal_sandbox_connected'] );
	}

	/**
	 * Check if PayPal is connected for live mode.
	 *
	 * @since 2.4.0
	 * @return bool True if PayPal live is connected, false otherwise.
	 */
	public static function is_paypal_live_connected() {
		$settings = self::get_all_paypal_settings();
		return ! empty( $settings['paypal_live_connected'] );
	}

	/**
	 * Check if PayPal is connected for the current payment mode.
	 *
	 * @param string|null $mode The payment mode ('test' or 'live'). If null, uses current mode.
	 * @since 2.4.0
	 * @return bool True if PayPal is connected for the specified mode, false otherwise.
	 */
	public static function is_paypal_connected( $mode = null ) {
		if ( null === $mode ) {
			$mode = Payment_Helper::get_payment_mode();
		}

		return 'live' === $mode
			? self::is_paypal_live_connected()
			: self::is_paypal_sandbox_connected();
	}

	/**
	 * Get the current PayPal payment mode.
	 *
	 * @since 2.4.0
	 * @return string The payment mode ('test' or 'live').
	 */
	public static function get_paypal_mode() {
		return Payment_Helper::get_payment_mode();
	}

	/**
	 * Get the PayPal client ID for the current payment mode.
	 *
	 * @param string|null $mode The payment mode ('test' or 'live'). If null, uses current mode.
	 * @since 2.4.0
	 * @return string The PayPal client ID.
	 */
	public static function get_paypal_client_id( $mode = null ) {
		if ( null === $mode ) {
			$mode = Payment_Helper::get_payment_mode();
		}

		$settings = self::get_all_paypal_settings();

		return 'live' === $mode
			? ( $settings['paypal_live_client_id'] ?? '' )
			: ( $settings['paypal_test_client_id'] ?? '' );
	}

	/**
	 * Get the PayPal client secret for the current payment mode.
	 *
	 * @param string|null $mode The payment mode ('test' or 'live'). If null, uses current mode.
	 * @since 2.4.0
	 * @return string The PayPal client secret.
	 */
	public static function get_paypal_client_secret( $mode = null ) {
		if ( null === $mode ) {
			$mode = Payment_Helper::get_payment_mode();
		}

		$settings = self::get_all_paypal_settings();

		return 'live' === $mode
			? ( $settings['paypal_live_client_secret'] ?? '' )
			: ( $settings['paypal_test_client_secret'] ?? '' );
	}

	/**
	 * Get the currency for PayPal payments.
	 *
	 * Uses the global payment currency from Payment_Helper.
	 *
	 * @since 2.4.0
	 * @return string The currency code (e.g., 'USD', 'EUR').
	 */
	public static function get_currency() {
		return Payment_Helper::get_currency();
	}

	/**
	 * Check if a currency is zero-decimal (no fractional units).
	 *
	 * PayPal supports three zero-decimal currencies:
	 * - HUF (Hungarian Forint)
	 * - JPY (Japanese Yen)
	 * - TWD (New Taiwan Dollar)
	 *
	 * @param string $currency Currency code (e.g., 'USD', 'JPY').
	 * @since 2.4.0
	 * @return bool True if the currency is zero-decimal, false otherwise.
	 */
	public static function is_zero_decimal_currency( $currency ) {
		$zero_decimal_currencies = [ 'HUF', 'JPY', 'TWD' ];
		return in_array( strtoupper( $currency ), $zero_decimal_currencies, true );
	}

	/**
	 * Format amount for PayPal API based on currency type.
	 *
	 * For zero-decimal currencies (HUF, JPY, TWD), returns the amount as-is (whole number).
	 * For other currencies, returns the amount formatted with 2 decimal places.
	 *
	 * @param float|int $amount   The amount to format.
	 * @param string    $currency Currency code (e.g., 'USD', 'JPY').
	 * @since 2.4.0
	 * @return string Formatted amount string for PayPal API.
	 */
	public static function format_amount_for_paypal( $amount, $currency ) {
		if ( self::is_zero_decimal_currency( $currency ) ) {
			// Zero-decimal currencies: use whole numbers only.
			return number_format( $amount, 0, '.', '' );
		}

		// Standard currencies: use 2 decimal places.
		return number_format( $amount, 2, '.', '' );
	}

	/**
	 * Convert amount from cents to PayPal format.
	 *
	 * For zero-decimal currencies (HUF, JPY, TWD), the amount in cents is already the final value.
	 * For other currencies, divide by 100 to convert cents to dollars.
	 *
	 * @param int    $amount_in_cents The amount in smallest currency unit (cents).
	 * @param string $currency        Currency code (e.g., 'USD', 'JPY').
	 * @since 2.4.0
	 * @return string Formatted amount for PayPal API.
	 */
	public static function convert_cents_to_paypal_format( $amount_in_cents, $currency ) {
		if ( self::is_zero_decimal_currency( $currency ) ) {
			// Zero-decimal currencies: cents IS the whole amount.
			return self::format_amount_for_paypal( $amount_in_cents, $currency );
		}

		// Standard currencies: convert cents to dollars.
		$amount_in_dollars = $amount_in_cents / 100;
		return self::format_amount_for_paypal( $amount_in_dollars, $currency );
	}

	/**
	 * Convert amount from PayPal format to cents.
	 *
	 * For zero-decimal currencies (HUF, JPY, TWD), the amount is already in the smallest unit.
	 * For other currencies, multiply by 100 to convert dollars to cents.
	 *
	 * @param float|string $amount   The amount from PayPal.
	 * @param string       $currency Currency code (e.g., 'USD', 'JPY').
	 * @since 2.4.0
	 * @return int Amount in smallest currency unit (cents).
	 */
	public static function convert_paypal_amount_to_cents( $amount, $currency ) {
		$amount = floatval( $amount );

		if ( self::is_zero_decimal_currency( $currency ) ) {
			// Zero-decimal currencies: amount is already in smallest unit.
			return intval( $amount );
		}

		// Standard currencies: convert dollars to cents.
		return intval( $amount * 100 );
	}

	/**
	 * Generate unique payment ID.
	 *
	 * Creates a unique transaction ID in the format: SRFM-{timestamp}-{payment_id}
	 * This matches the pattern used by Stripe integration.
	 *
	 * @param int $payment_id Payment database ID.
	 * @since 2.4.0
	 * @return string Unique payment ID.
	 */
	public static function generate_unique_payment_id( $payment_id ) {
		return 'SRFM-' . time() . '-' . $payment_id;
	}

	/**
	 * Summary of get_error_messages
	 *
	 * @param mixed $error_code Error code received from PayPal API.
	 * @param mixed $default Default error message.
	 *
	 * @since 2.4.0
	 * @return string
	 */
	public static function get_error_messages( $error_code, $default ) {
		$error_messages = [
			'CURRENCY_NOT_SUPPORTED'              => __( 'This currency code is not supported. Please contact the site administrator for assistance.', 'sureforms-pro' ),
			'CURRENCY_NOT_SUPPORTED_FOR_RECEIVER' => __( 'This currency code is not supported for the receiver. Please contact the site administrator for assistance.', 'sureforms-pro' ),
		];

		$get_message = isset( $error_messages[ $error_code ] ) && is_string( $error_messages[ $error_code ] ) ? $error_messages[ $error_code ] : $default;

		return ! empty( $get_message ) && is_string( $get_message ) ? $get_message : __( 'An unexpected error occurred. Please try again.', 'sureforms-pro' );
	}

	/**
	 * Normalize PayPal subscription status to standardized values.
	 *
	 * PayPal returns statuses like ACTIVE, CANCELLED, SUSPENDED, EXPIRED.
	 * This normalizes them to: active, paused, canceled.
	 *
	 * @param string $paypal_status Status from PayPal API.
	 * @since 2.4.0
	 * @return string Normalized status (active, paused, or canceled).
	 */
	public static function normalize_subscription_status( $paypal_status ) {
		// Convert to uppercase for comparison.
		$status = strtoupper( trim( $paypal_status ) );

		// Map PayPal statuses to normalized values.
		switch ( $status ) {
			case 'ACTIVE':
			case 'APPROVAL_PENDING':
			case 'APPROVED':
				return 'active';

			case 'SUSPENDED':
				return 'paused';

			case 'CANCELLED':
			case 'CANCELED':
			case 'EXPIRED':
			default:
				return 'canceled';
		}
	}

	/**
	 * Get all PayPal supported currencies with their details.
	 *
	 * Returns an array of all currencies supported by PayPal with their
	 * display names, symbols, and decimal place requirements.
	 *
	 * @since 2.4.0
	 * @return array Array of currency data with currency codes as keys.
	 */
	public static function get_all_paypal_currencies_data() {
		return [
			'AUD' => [
				'name'           => __( 'Australian Dollar', 'sureforms-pro' ),
				'symbol'         => 'A$',
				'decimal_places' => 2,
			],
			'BRL' => [
				'name'           => __( 'Brazilian Real', 'sureforms-pro' ),
				'symbol'         => 'R$',
				'decimal_places' => 2,
			],
			'CAD' => [
				'name'           => __( 'Canadian Dollar', 'sureforms-pro' ),
				'symbol'         => 'C$',
				'decimal_places' => 2,
			],
			'CNY' => [
				'name'           => __( 'Chinese Renminbi', 'sureforms-pro' ),
				'symbol'         => '¥',
				'decimal_places' => 2,
			],
			'CZK' => [
				'name'           => __( 'Czech Koruna', 'sureforms-pro' ),
				'symbol'         => 'Kč',
				'decimal_places' => 2,
			],
			'DKK' => [
				'name'           => __( 'Danish Krone', 'sureforms-pro' ),
				'symbol'         => 'kr',
				'decimal_places' => 2,
			],
			'EUR' => [
				'name'           => __( 'Euro', 'sureforms-pro' ),
				'symbol'         => '€',
				'decimal_places' => 2,
			],
			'HKD' => [
				'name'           => __( 'Hong Kong Dollar', 'sureforms-pro' ),
				'symbol'         => 'HK$',
				'decimal_places' => 2,
			],
			'HUF' => [
				'name'           => __( 'Hungarian Forint', 'sureforms-pro' ),
				'symbol'         => 'Ft',
				'decimal_places' => 0,
			],
			'ILS' => [
				'name'           => __( 'Israeli New Shekel', 'sureforms-pro' ),
				'symbol'         => '₪',
				'decimal_places' => 2,
			],
			'JPY' => [
				'name'           => __( 'Japanese Yen', 'sureforms-pro' ),
				'symbol'         => '¥',
				'decimal_places' => 0,
			],
			'MYR' => [
				'name'           => __( 'Malaysian Ringgit', 'sureforms-pro' ),
				'symbol'         => 'RM',
				'decimal_places' => 2,
			],
			'MXN' => [
				'name'           => __( 'Mexican Peso', 'sureforms-pro' ),
				'symbol'         => '$',
				'decimal_places' => 2,
			],
			'TWD' => [
				'name'           => __( 'New Taiwan Dollar', 'sureforms-pro' ),
				'symbol'         => 'NT$',
				'decimal_places' => 0,
			],
			'NZD' => [
				'name'           => __( 'New Zealand Dollar', 'sureforms-pro' ),
				'symbol'         => 'NZ$',
				'decimal_places' => 2,
			],
			'NOK' => [
				'name'           => __( 'Norwegian Krone', 'sureforms-pro' ),
				'symbol'         => 'kr',
				'decimal_places' => 2,
			],
			'PHP' => [
				'name'           => __( 'Philippine Peso', 'sureforms-pro' ),
				'symbol'         => '₱',
				'decimal_places' => 2,
			],
			'PLN' => [
				'name'           => __( 'Polish Złoty', 'sureforms-pro' ),
				'symbol'         => 'zł',
				'decimal_places' => 2,
			],
			'GBP' => [
				'name'           => __( 'Pound Sterling', 'sureforms-pro' ),
				'symbol'         => '£',
				'decimal_places' => 2,
			],
			'SGD' => [
				'name'           => __( 'Singapore Dollar', 'sureforms-pro' ),
				'symbol'         => 'S$',
				'decimal_places' => 2,
			],
			'SEK' => [
				'name'           => __( 'Swedish Krona', 'sureforms-pro' ),
				'symbol'         => 'kr',
				'decimal_places' => 2,
			],
			'CHF' => [
				'name'           => __( 'Swiss Franc', 'sureforms-pro' ),
				'symbol'         => 'CHF',
				'decimal_places' => 2,
			],
			'THB' => [
				'name'           => __( 'Thai Baht', 'sureforms-pro' ),
				'symbol'         => '฿',
				'decimal_places' => 2,
			],
			'USD' => [
				'name'           => __( 'United States Dollar', 'sureforms-pro' ),
				'symbol'         => '$',
				'decimal_places' => 2,
			],
		];
	}
}
