<?php
/**
 * PostGate Product Utilities Class
 *
 * This file contains utility functions for product management, price retrieval,
 * and Stripe product/price data operations used throughout the PostGate plugin.
 *
 * @package PostGate
 * @subpackage Utilities
 * @since 1.0.0
 * @author QodeSphere
 * @license GPL-2.0+
 */

defined( 'ABSPATH' ) || exit;

/**
 * PostGate Product Utilities Class
 *
 * Provides utility functions for product operations, price retrieval for products,
 * and Stripe product data management. This class works with products (which contain
 * multiple prices) rather than individual prices.
 *
 * @since 1.0.0
 */
class PostGate_Product_Utils {

	/**
	 * Return the current Stripe Connect mode.
	 *
	 * @since 1.0.0
	 * @return 'live'|'sandbox'
	 */
	public static function current_mode(): string {
		return get_option( 'postgate_stripe_connect_mode', 'sandbox' ) === 'live' ? 'live' : 'sandbox';
	}

	/**
	 * Get all products with their prices for the current mode.
	 *
	 * @since 1.0.0
	 * @return array{products: array, default_product_id: string|null}
	 */
	public static function get_products(): array {
		$mode = self::current_mode();
		$all = get_option( 'postgate_stripe_products', [] );

		if ( ! isset( $all[ $mode ]['products'] ) || ! is_array( $all[ $mode ]['products'] ) ) {
			return [ 'products' => [], 'default_product_id' => null ];
		}

		$products = $all[ $mode ]['products'];
		$default_product_id = null;

		// Find default product
		foreach ( $products as $product ) {
			if ( ! empty( $product['is_default'] ) ) {
				$default_product_id = $product['id'];
				break;
			}
		}

		return [
			'products' => $products,
			'default_product_id' => $default_product_id,
		];
	}

	/**
	 * Get a specific product by ID.
	 *
	 * @since 1.0.0
	 * @param string $product_id Stripe Product ID
	 * @return array|null Product data with prices, or null if not found
	 */
	public static function get_product_by_id( string $product_id ): ?array {
		$data = self::get_products();

		foreach ( $data['products'] as $product ) {
			if ( $product['id'] === $product_id ) {
				return $product;
			}
		}

		return null;
	}

	/**
	 * Get all prices for a specific product.
	 *
	 * @since 1.0.0
	 * @param string $product_id Stripe Product ID
	 * @return array Array of prices for the product
	 */
	public static function get_prices_for_product( string $product_id ): array {
		$product = self::get_product_by_id( $product_id );

		if ( ! $product || ! isset( $product['prices'] ) || ! is_array( $product['prices'] ) ) {
			return [];
		}

		return array_values( $product['prices'] );
	}

	/**
	 * Get all price IDs from multiple products.
	 *
	 * Extracts all price IDs from the given product IDs, allowing the paywall
	 * to display all pricing options for each product.
	 *
	 * @since 1.0.0
	 * @param array $product_ids Array of Stripe Product IDs
	 * @return array Array of all price IDs from all products
	 */
	public static function get_all_price_ids_from_products( array $product_ids ): array {
		$product_ids = array_values( array_unique( array_map( 'strval', $product_ids ) ) );

		if ( empty( $product_ids ) ) {
			return [];
		}

		$all_price_ids = [];

		foreach ( $product_ids as $product_id ) {
			$prices = self::get_prices_for_product( $product_id );
			foreach ( $prices as $price ) {
				if ( ! empty( $price['id'] ) ) {
					$all_price_ids[] = (string) $price['id'];
				}
			}
		}

		return array_values( array_unique( $all_price_ids ) );
	}

	/**
	 * Get the default price ID for a product.
	 *
	 * Returns the price marked as default in the product, or the Stripe default_price,
	 * or the first price if no default is specified.
	 *
	 * @since 1.0.0
	 * @param string $product_id Stripe Product ID
	 * @return string|null Price ID or null if product has no prices
	 */
	public static function get_default_price_for_product( string $product_id ): ?string {
		$product = self::get_product_by_id( $product_id );

		if ( ! $product || empty( $product['prices'] ) ) {
			return null;
		}

		// If product has explicit default_price set
		if ( ! empty( $product['default_price'] ) ) {
			return $product['default_price'];
		}

		// Otherwise return first price
		$prices = array_values( $product['prices'] );
		return $prices[0]['id'] ?? null;
	}

	/**
	 * Format billing interval label for display.
	 *
	 * Converts Stripe interval and interval_count into a human-readable display label.
	 * Handles pluralization and uses WordPress i18n for translations.
	 *
	 * Examples:
	 * - interval: 'month', interval_count: 1 → 'every month'
	 * - interval: 'month', interval_count: 3 → 'every 3 months'
	 * - interval: 'year', interval_count: 1 → 'every year'
	 * - interval: 'week', interval_count: 2 → 'every 2 weeks'
	 *
	 * @since 1.0.0
	 * @param string $interval      Stripe interval: 'day', 'week', 'month', or 'year'
	 * @param int    $interval_count Number of intervals (default: 1)
	 * @return string Formatted interval label for display
	 */
	public static function format_interval_label( string $interval, int $interval_count = 1 ): string {
		$interval = strtolower( trim( $interval ) );
		$interval_count = max( 1, intval( $interval_count ) );

		// Map Stripe intervals to translatable strings
		$interval_labels = array(
			'day' => array(
				'singular' => _x( 'day', 'billing interval', 'postgate' ),
				'plural' => _x( 'days', 'billing interval', 'postgate' ),
			),
			'week' => array(
				'singular' => _x( 'week', 'billing interval', 'postgate' ),
				'plural' => _x( 'weeks', 'billing interval', 'postgate' ),
			),
			'month' => array(
				'singular' => _x( 'month', 'billing interval', 'postgate' ),
				'plural' => _x( 'months', 'billing interval', 'postgate' ),
			),
			'year' => array(
				'singular' => _x( 'year', 'billing interval', 'postgate' ),
				'plural' => _x( 'years', 'billing interval', 'postgate' ),
			),
		);

		// Fallback for unknown intervals
		if ( ! isset( $interval_labels[ $interval ] ) ) {
			return $interval_count > 1
				/* translators: 1: interval count, 2: interval name */
				? sprintf( __( 'every %1$d %2$ss', 'postgate' ), $interval_count, $interval )
				/* translators: %s: interval name */
				: sprintf( __( '/ %s', 'postgate' ), $interval );
		}

		$label_map = $interval_labels[ $interval ];

		if ( $interval_count === 1 ) {
			// Simple format for single interval: "/ month", "/ year", etc.
			return '/ ' . $label_map['singular'];
		}

		// translators: %1$d is the count, %2$s is the plural interval (months, years, etc.)
		return sprintf( __( 'every %1$d %2$s', 'postgate' ), $interval_count, $label_map['plural'] );
	}

	/**
	 * Get products for given product IDs.
	 *
	 * @since 1.0.0
	 * @param array $product_ids Product IDs to retrieve
	 * @return array Array of products with their prices
	 */
	public static function get_products_for_ids( array $product_ids ): array {
		$product_ids = array_values( array_unique( array_map( 'strval', $product_ids ) ) );

		if ( empty( $product_ids ) ) {
			return [];
		}

		$all_products = self::get_products();
		$products = [];

		foreach ( $all_products['products'] as $product ) {
			if ( in_array( $product['id'], $product_ids, true ) ) {
				$products[] = $product;
			}
		}

		// Maintain order of incoming IDs
		usort(
			$products,
			static function ( $a, $b ) use ( $product_ids ) {
				$ia = array_search( $a['id'], $product_ids, true );
				$ib = array_search( $b['id'], $product_ids, true );
				return ( $ia === false ? PHP_INT_MAX : $ia ) <=> ( $ib === false ? PHP_INT_MAX : $ib );
			}
		);

		return $products;
	}

	/**
	 * Format products into template-ready options with formatted prices.
	 *
	 * @since 1.0.0
	 * @param array $products Array of products
	 * @return array Formatted product options ready for template rendering
	 */
	public static function format_product_options( array $products ): array {
		$product_options = [];

		foreach ( $products as $product ) {
			$formatted_prices = [];

			if ( ! empty( $product['prices'] ) ) {
				foreach ( $product['prices'] as $price ) {
					$amount_minor = (int) ( $price['amount'] ?? 0 );
					$currency = strtoupper( (string) ( $price['currency'] ?? 'USD' ) );
					$amount_locale = self::format_price( $amount_minor, $currency );

					$is_recurring = ( $price['type'] === 'recurring' );
					$interval = (string) ( $price['interval'] ?? '' );
					$interval_count = (int) ( $price['interval_count'] ?? 1 );
					$interval_label = ( $is_recurring && $interval ) ? self::format_interval_label( $interval, $interval_count ) : '';

					$formatted_prices[] = [
						'id' => $price['id'],
						'amount_minor' => $amount_minor,
						'amount_locale' => $amount_locale,
						'currency' => $currency,
						'type' => $price['type'],
						'interval' => $interval,
						'interval_count' => $interval_count,
						'interval_label' => $interval_label,
						'type_label' => $is_recurring ? __( 'Subscription', 'postgate' ) : __( 'Single purchase', 'postgate' ),
						'nickname' => (string) ( $price['nickname'] ?? '' ),
						'metadata' => $price['metadata'] ?? [],
						'aria_label' => trim( ( $price['metadata']['label'] ?? $product['name'] ) . ' – ' . $amount_locale . ( $interval_label ? ' ' . $interval_label : '' ) ),
					];
				}
			}

			$product_options[] = [
				'id' => $product['id'],
				'name' => $product['name'] ?? '',
				'description' => $product['description'] ?? '',
				'default_price_id' => self::get_default_price_for_product( $product['id'] ),
				'prices' => $formatted_prices,
				'is_default' => (bool) ( $product['is_default'] ?? false ),
			];
		}

		return $product_options;
	}

	/**
	 * Check if a product exists in the current mode.
	 *
	 * @since 1.0.0
	 * @param string $product_id Stripe Product ID
	 * @return bool True if product exists, false otherwise
	 */
	public static function product_exists( string $product_id ): bool {
		return self::get_product_by_id( $product_id ) !== null;
	}

	/**
	 * Get price details for specific price IDs from product structure.
	 *
	 * Searches for prices within the product structure and returns formatted data.
	 * This allows fetching individual prices from the nested product structure.
	 *
	 * @since 1.0.0
	 * @param array $price_ids Array of Stripe Price IDs to retrieve
	 * @return array{items: array, default_id: string|null} Price items and default ID
	 */
	public static function get_prices_for_ids( array $price_ids ): array {
		$price_ids = array_values( array_unique( array_map( 'strval', $price_ids ) ) );
		if ( empty( $price_ids ) ) {
			return [ 'items' => [], 'default_id' => null ];
		}

		$mode = self::current_mode();
		$all = get_option( 'postgate_stripe_products' );
		$out = [];
		$def = null;

		if ( is_array( $all ) && ! empty( $all[ $mode ]['products'] ) && is_array( $all[ $mode ]['products'] ) ) {
			foreach ( $all[ $mode ]['products'] as $product ) {
				if ( empty( $product['prices'] ) || ! is_array( $product['prices'] ) ) {
					continue;
				}

				foreach ( $product['prices'] as $price ) {
					if ( empty( $price['id'] ) || ! in_array( (string) $price['id'], $price_ids, true ) ) {
						continue;
					}

					// Use product name as title for the price
					$price_title = ! empty( $product['name'] ) ? $product['name'] : (string) $price['id'];

					$p = [
						'id' => (string) ( $price['id'] ?? '' ),
						'product_id' => (string) ( $product['id'] ?? '' ),
						'title' => $price_title,
						'description' => (string) ( $product['description'] ?? '' ),
						'amount' => isset( $price['amount'] ) ? (int) $price['amount'] : 0,
						'currency' => strtoupper( (string) ( $price['currency'] ?? 'USD' ) ),
						'interval' => (string) ( $price['interval'] ?? '' ),
						'interval_count' => isset( $price['interval_count'] ) ? (int) $price['interval_count'] : 1,
						'type' => (string) ( $price['type'] ?? 'one_time' ),
						'nickname' => (string) ( $price['nickname'] ?? '' ),
						'is_default' => (string) $price['id'] === (string) ( $product['default_price'] ?? '' ),
					];
					$out[] = $p;

					if ( $p['is_default'] && ! $def ) {
						$def = $p['id'];
					}
				}
			}
		}

		// Keep the incoming IDs order
		usort(
			$out,
			static function ( $a, $b ) use ( $price_ids ) {
				$ia = array_search( $a['id'], $price_ids, true );
				$ib = array_search( $b['id'], $price_ids, true );
				return ( $ia === false ? PHP_INT_MAX : $ia ) <=> ( $ib === false ? PHP_INT_MAX : $ib );
			}
		);

		return [ 'items' => $out, 'default_id' => $def ];
	}

	/**
	 * Format price items into template-ready options.
	 *
	 * Takes raw price data and formats it with localized amounts,
	 * interval labels, type labels, and aria labels for accessibility.
	 *
	 * @since 1.0.0
	 * @param array $items Raw price items from get_prices_for_ids()
	 * @return array Formatted price options ready for template rendering
	 */
	public static function format_price_options( array $items ): array {
		$price_options = [];

		foreach ( $items as $p ) {
			$amount_minor = (int) $p['amount'];
			$currency = (string) $p['currency'];
			$amount_locale = self::format_price( $amount_minor, $currency );

			$is_recurring = ( $p['type'] === 'recurring' );
			$interval = (string) ( $p['interval'] ?? '' );
			$interval_count = (int) ( $p['interval_count'] ?? 1 );
			$interval_label = ( $is_recurring && $interval ) ? self::format_interval_label( $interval, $interval_count ) : '';

			$price_options[] = [
				'id' => $p['id'],
				'product_id' => $p['product_id'] ?? '',
				'title' => $p['title'],
				'description' => $p['description'],
				'amount_minor' => $amount_minor,
				'currency' => $currency,
				'interval' => $interval,
				'interval_count' => $interval_count,
				'type' => $p['type'],
				'amount_locale' => $amount_locale,
				'interval_label' => $interval_label,
				'type_label' => $is_recurring ? __( 'Subscription', 'postgate' ) : __( 'Single purchase', 'postgate' ),
				'nickname' => (string) ( $p['nickname'] ?? '' ),
				'is_default' => (bool) $p['is_default'],
				'aria_label' => trim( $p['title'] . ' – ' . $amount_locale . ( $interval_label ? ' ' . $interval_label : '' ) ),
			];
		}

		return $price_options;
	}

	/**
	 * Format price items grouped by product for template rendering.
	 *
	 * Takes raw price data and groups them by product, returning a structure
	 * optimized for displaying products with their associated prices.
	 * If prices don't have product_id, returns flat structure for backward compatibility.
	 *
	 * @since 1.0.0
	 * @param array $items Raw price items from get_prices_for_ids()
	 * @return array Grouped product options ready for template rendering
	 */
	public static function format_price_options_grouped( array $items ): array {
		if ( empty( $items ) ) {
			return [];
		}

		// Check if we have product-based prices
		$has_products = false;
		foreach ( $items as $p ) {
			if ( ! empty( $p['product_id'] ) ) {
				$has_products = true;
				break;
			}
		}

		// If no products, return flat format
		if ( ! $has_products ) {
			return self::format_price_options( $items );
		}

		// Group prices by product
		$products_map = [];
		foreach ( $items as $p ) {
			$product_id = $p['product_id'] ?? '';
			if ( empty( $product_id ) ) {
				continue; // Skip prices without product
			}

			if ( ! isset( $products_map[ $product_id ] ) ) {
				// Fetch product details
				$product = self::get_product_by_id( $product_id );
				$products_map[ $product_id ] = [
					'type' => 'product',
					'product_id' => $product_id,
					'product_name' => $product['name'] ?? '',
					'product_description' => $product['description'] ?? '',
					'default_price_id' => $product['default_price'] ?? '',
					'prices' => [],
				];
			}

			// Format and add price to product
			$amount_minor = (int) $p['amount'];
			$currency = (string) $p['currency'];
			$amount_locale = self::format_price( $amount_minor, $currency );
			$is_recurring = ( $p['type'] === 'recurring' );
			$interval = (string) ( $p['interval'] ?? '' );
			$interval_count = (int) ( $p['interval_count'] ?? 1 );
			$interval_label = ( $is_recurring && $interval ) ? self::format_interval_label( $interval, $interval_count ) : '';

			$products_map[ $product_id ]['prices'][] = [
				'id' => $p['id'],
				'product_id' => $product_id, // Include product_id for each price
				'title' => $p['title'],
				'description' => $p['description'] ?? '',
				'amount_minor' => $amount_minor,
				'currency' => $currency,
				'interval' => $interval,
				'interval_count' => $interval_count,
				'type' => $p['type'],
				'amount_locale' => $amount_locale,
				'interval_label' => $interval_label,
				'type_label' => $is_recurring ? __( 'Subscription', 'postgate' ) : __( 'Single purchase', 'postgate' ),
				'nickname' => (string) ( $p['nickname'] ?? '' ),
				'is_default' => (bool) $p['is_default'],
				'aria_label' => trim( $p['title'] . ' – ' . $amount_locale . ( $interval_label ? ' ' . $interval_label : '' ) ),
			];
		}

		return array_values( $products_map );
	}

	/**
	 * Check if a price belongs to a product.
	 *
	 * @since 1.0.0
	 * @param string $price_id Stripe Price ID
	 * @param string $product_id Stripe Product ID
	 * @return bool True if price belongs to product, false otherwise
	 */
	public static function price_belongs_to_product( string $price_id, string $product_id ): bool {
		$product = self::get_product_by_id( $product_id );

		if ( ! $product || empty( $product['prices'] ) ) {
			return false;
		}

		foreach ( $product['prices'] as $price ) {
			if ( $price['id'] === $price_id ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Get the default product ID for the current mode.
	 *
	 * @since 1.0.0
	 * @return string Empty string if no default is set
	 */
	public static function get_default_product_id(): string {
		$data = self::get_products();
		return $data['default_product_id'] ?? '';
	}

	/**
	 * Check if a product is marked as the global default.
	 *
	 * @since 1.0.0
	 * @param string $product_id Stripe Product ID
	 * @return bool True if product is the global default, false otherwise
	 */
	public static function is_global_default_product( string $product_id ): bool {
		return self::get_default_product_id() === $product_id;
	}

	/**
	 * Fraction digits per currency.
	 * 0- and 3-decimal currencies handled explicitly; otherwise 2.
	 *
	 * @since 1.0.0
	 * @param string $currency Currency code (e.g., 'USD', 'JPY', 'BHD')
	 * @return int Number of decimal places for the currency
	 */
	public static function currency_fraction_digits( string $currency ): int {
		$currency = strtoupper( $currency );
		$zero_curs = [ 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF' ];
		$three_curs = [ 'BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND' ];
		if ( in_array( $currency, $zero_curs, true ) ) {
			return 0;
		}
		if ( in_array( $currency, $three_curs, true ) ) {
			return 3;
		}
		return 2;
	}

	/**
	 * Resolve the locale used for formatting.
	 *
	 * @since 1.0.0
	 * @return string Current WordPress locale or 'en_US' as fallback
	 */
	public static function current_locale(): string {
		if ( function_exists( 'get_locale' ) ) {
			$loc = get_locale();
			if ( ! empty( $loc ) ) {
				return $loc;
			}
		}
		return 'en_US';
	}

	/**
	 * Format an amount (minor units) to a locale-aware price string.
	 * Uses Intl NumberFormatter when available; otherwise falls back to
	 * "value CURRENCY" (ISO code).
	 *
	 * Examples (Intl):
	 *  - pt_BR + BRL → "R$ 25,00"
	 *  - fr_FR + EUR → "50,00 €"
	 * Fallback (no Intl): "25,00 BRL"
	 *
	 * @since 1.0.0
	 * @param int    $amount_minor Amount in minor units (e.g., cents)
	 * @param string $currency     Currency code (e.g., 'USD', 'EUR')
	 * @param string|null $locale  Optional locale override
	 * @return string Formatted price string
	 */
	public static function format_price( int $amount_minor, string $currency, ?string $locale = null ): string {
		$currency = strtoupper( trim( $currency ) );
		$locale = $locale ?: self::current_locale();

		$digits = self::currency_fraction_digits( $currency );
		$value = $amount_minor / (float) pow( 10, $digits );

		// Prefer Intl if available.
		if ( class_exists( '\NumberFormatter' ) ) {
			$fmt = new \NumberFormatter( $locale, \NumberFormatter::CURRENCY );
			$str = $fmt->formatCurrency( $value, $currency );
			if ( false !== $str ) {
				return $str;
			}
		}

		// Fallback: localized number + ISO code (no symbol).
		$val = number_format_i18n( $value, $digits );
		return $val . "\xC2\xA0" . $currency; // NBSP
	}

	/**
	 * Get list of Stripe-supported currencies.
	 *
	 * Returns an array of ISO 4217 currency codes supported by Stripe for card payments.
	 *
	 * @since 1.0.0
	 * @return array Array of uppercase currency codes.
	 */
	public static function get_supported_currencies(): array {
		return [
			'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN',
			'BAM', 'BBD', 'BDT', 'BGN', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD',
			'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', 'COP', 'CRC',
			'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ETB', 'EUR', 'FJD',
			'FKP', 'GBP', 'GEL', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL',
			'HTG', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'JMD', 'JPY', 'KES', 'KGS',
			'KHR', 'KMF', 'KRW', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL',
			'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MUR', 'MVR', 'MWK',
			'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'PAB',
			'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB',
			'RWF', 'SAR', 'SBD', 'SCR', 'SEK', 'SGD', 'SHP', 'SLE', 'SOS', 'SRD',
			'STD', 'SZL', 'THB', 'TJS', 'TOP', 'TRY', 'TTD', 'TWD', 'TZS', 'UAH',
			'UGX', 'USD', 'UYU', 'UZS', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XOF',
			'XPF', 'YER', 'ZAR', 'ZMW',
		];
	}

	/**
	 * Get currency names mapping.
	 *
	 * Returns a mapping of ISO currency codes to their full names.
	 *
	 * @since 1.0.0
	 * @return array Associative array of currency code => currency name.
	 */
	public static function get_currency_names(): array {
		return [
			'AED' => __( 'United Arab Emirates Dirham', 'postgate' ),
			'AFN' => __( 'Afghan Afghani', 'postgate' ),
			'ALL' => __( 'Albanian Lek', 'postgate' ),
			'AMD' => __( 'Armenian Dram', 'postgate' ),
			'ANG' => __( 'Netherlands Antillean Guilder', 'postgate' ),
			'AOA' => __( 'Angolan Kwanza', 'postgate' ),
			'ARS' => __( 'Argentine Peso', 'postgate' ),
			'AUD' => __( 'Australian Dollar', 'postgate' ),
			'AWG' => __( 'Aruban Florin', 'postgate' ),
			'AZN' => __( 'Azerbaijani Manat', 'postgate' ),
			'BAM' => __( 'Bosnia-Herzegovina Convertible Mark', 'postgate' ),
			'BBD' => __( 'Barbadian Dollar', 'postgate' ),
			'BDT' => __( 'Bangladeshi Taka', 'postgate' ),
			'BGN' => __( 'Bulgarian Lev', 'postgate' ),
			'BIF' => __( 'Burundian Franc', 'postgate' ),
			'BMD' => __( 'Bermudan Dollar', 'postgate' ),
			'BND' => __( 'Brunei Dollar', 'postgate' ),
			'BOB' => __( 'Bolivian Boliviano', 'postgate' ),
			'BRL' => __( 'Brazilian Real', 'postgate' ),
			'BSD' => __( 'Bahamian Dollar', 'postgate' ),
			'BWP' => __( 'Botswanan Pula', 'postgate' ),
			'BYN' => __( 'Belarusian Ruble', 'postgate' ),
			'BZD' => __( 'Belize Dollar', 'postgate' ),
			'CAD' => __( 'Canadian Dollar', 'postgate' ),
			'CDF' => __( 'Congolese Franc', 'postgate' ),
			'CHF' => __( 'Swiss Franc', 'postgate' ),
			'CLP' => __( 'Chilean Peso', 'postgate' ),
			'CNY' => __( 'Chinese Yuan', 'postgate' ),
			'COP' => __( 'Colombian Peso', 'postgate' ),
			'CRC' => __( 'Costa Rican Colón', 'postgate' ),
			'CVE' => __( 'Cape Verdean Escudo', 'postgate' ),
			'CZK' => __( 'Czech Koruna', 'postgate' ),
			'DJF' => __( 'Djiboutian Franc', 'postgate' ),
			'DKK' => __( 'Danish Krone', 'postgate' ),
			'DOP' => __( 'Dominican Peso', 'postgate' ),
			'DZD' => __( 'Algerian Dinar', 'postgate' ),
			'EGP' => __( 'Egyptian Pound', 'postgate' ),
			'ETB' => __( 'Ethiopian Birr', 'postgate' ),
			'EUR' => __( 'Euro', 'postgate' ),
			'FJD' => __( 'Fijian Dollar', 'postgate' ),
			'FKP' => __( 'Falkland Islands Pound', 'postgate' ),
			'GBP' => __( 'British Pound', 'postgate' ),
			'GEL' => __( 'Georgian Lari', 'postgate' ),
			'GIP' => __( 'Gibraltar Pound', 'postgate' ),
			'GMD' => __( 'Gambian Dalasi', 'postgate' ),
			'GNF' => __( 'Guinean Franc', 'postgate' ),
			'GTQ' => __( 'Guatemalan Quetzal', 'postgate' ),
			'GYD' => __( 'Guyanaese Dollar', 'postgate' ),
			'HKD' => __( 'Hong Kong Dollar', 'postgate' ),
			'HNL' => __( 'Honduran Lempira', 'postgate' ),
			'HTG' => __( 'Haitian Gourde', 'postgate' ),
			'HUF' => __( 'Hungarian Forint', 'postgate' ),
			'IDR' => __( 'Indonesian Rupiah', 'postgate' ),
			'ILS' => __( 'Israeli New Shekel', 'postgate' ),
			'INR' => __( 'Indian Rupee', 'postgate' ),
			'ISK' => __( 'Icelandic Króna', 'postgate' ),
			'JMD' => __( 'Jamaican Dollar', 'postgate' ),
			'JPY' => __( 'Japanese Yen', 'postgate' ),
			'KES' => __( 'Kenyan Shilling', 'postgate' ),
			'KGS' => __( 'Kyrgystani Som', 'postgate' ),
			'KHR' => __( 'Cambodian Riel', 'postgate' ),
			'KMF' => __( 'Comorian Franc', 'postgate' ),
			'KRW' => __( 'South Korean Won', 'postgate' ),
			'KYD' => __( 'Cayman Islands Dollar', 'postgate' ),
			'KZT' => __( 'Kazakhstani Tenge', 'postgate' ),
			'LAK' => __( 'Laotian Kip', 'postgate' ),
			'LBP' => __( 'Lebanese Pound', 'postgate' ),
			'LKR' => __( 'Sri Lankan Rupee', 'postgate' ),
			'LRD' => __( 'Liberian Dollar', 'postgate' ),
			'LSL' => __( 'Lesotho Loti', 'postgate' ),
			'MAD' => __( 'Moroccan Dirham', 'postgate' ),
			'MDL' => __( 'Moldovan Leu', 'postgate' ),
			'MGA' => __( 'Malagasy Ariary', 'postgate' ),
			'MKD' => __( 'Macedonian Denar', 'postgate' ),
			'MMK' => __( 'Myanmar Kyat', 'postgate' ),
			'MNT' => __( 'Mongolian Tugrik', 'postgate' ),
			'MOP' => __( 'Macanese Pataca', 'postgate' ),
			'MUR' => __( 'Mauritian Rupee', 'postgate' ),
			'MVR' => __( 'Maldivian Rufiyaa', 'postgate' ),
			'MWK' => __( 'Malawian Kwacha', 'postgate' ),
			'MXN' => __( 'Mexican Peso', 'postgate' ),
			'MYR' => __( 'Malaysian Ringgit', 'postgate' ),
			'MZN' => __( 'Mozambican Metical', 'postgate' ),
			'NAD' => __( 'Namibian Dollar', 'postgate' ),
			'NGN' => __( 'Nigerian Naira', 'postgate' ),
			'NIO' => __( 'Nicaraguan Córdoba', 'postgate' ),
			'NOK' => __( 'Norwegian Krone', 'postgate' ),
			'NPR' => __( 'Nepalese Rupee', 'postgate' ),
			'NZD' => __( 'New Zealand Dollar', 'postgate' ),
			'PAB' => __( 'Panamanian Balboa', 'postgate' ),
			'PEN' => __( 'Peruvian Sol', 'postgate' ),
			'PGK' => __( 'Papua New Guinean Kina', 'postgate' ),
			'PHP' => __( 'Philippine Peso', 'postgate' ),
			'PKR' => __( 'Pakistani Rupee', 'postgate' ),
			'PLN' => __( 'Polish Zloty', 'postgate' ),
			'PYG' => __( 'Paraguayan Guarani', 'postgate' ),
			'QAR' => __( 'Qatari Rial', 'postgate' ),
			'RON' => __( 'Romanian Leu', 'postgate' ),
			'RSD' => __( 'Serbian Dinar', 'postgate' ),
			'RUB' => __( 'Russian Ruble', 'postgate' ),
			'RWF' => __( 'Rwandan Franc', 'postgate' ),
			'SAR' => __( 'Saudi Riyal', 'postgate' ),
			'SBD' => __( 'Solomon Islands Dollar', 'postgate' ),
			'SCR' => __( 'Seychellois Rupee', 'postgate' ),
			'SEK' => __( 'Swedish Krona', 'postgate' ),
			'SGD' => __( 'Singapore Dollar', 'postgate' ),
			'SHP' => __( 'Saint Helena Pound', 'postgate' ),
			'SLE' => __( 'Sierra Leonean Leone', 'postgate' ),
			'SOS' => __( 'Somali Shilling', 'postgate' ),
			'SRD' => __( 'Surinamese Dollar', 'postgate' ),
			'STD' => __( 'São Tomé and Príncipe Dobra', 'postgate' ),
			'SZL' => __( 'Swazi Lilangeni', 'postgate' ),
			'THB' => __( 'Thai Baht', 'postgate' ),
			'TJS' => __( 'Tajikistani Somoni', 'postgate' ),
			'TOP' => __( 'Tongan Paʻanga', 'postgate' ),
			'TRY' => __( 'Turkish Lira', 'postgate' ),
			'TTD' => __( 'Trinidad and Tobago Dollar', 'postgate' ),
			'TWD' => __( 'New Taiwan Dollar', 'postgate' ),
			'TZS' => __( 'Tanzanian Shilling', 'postgate' ),
			'UAH' => __( 'Ukrainian Hryvnia', 'postgate' ),
			'UGX' => __( 'Ugandan Shilling', 'postgate' ),
			'USD' => __( 'US Dollar', 'postgate' ),
			'UYU' => __( 'Uruguayan Peso', 'postgate' ),
			'UZS' => __( 'Uzbekistani Som', 'postgate' ),
			'VND' => __( 'Vietnamese Dong', 'postgate' ),
			'VUV' => __( 'Vanuatu Vatu', 'postgate' ),
			'WST' => __( 'Samoan Tala', 'postgate' ),
			'XAF' => __( 'Central African CFA Franc', 'postgate' ),
			'XCD' => __( 'East Caribbean Dollar', 'postgate' ),
			'XOF' => __( 'West African CFA Franc', 'postgate' ),
			'XPF' => __( 'CFP Franc', 'postgate' ),
			'YER' => __( 'Yemeni Rial', 'postgate' ),
			'ZAR' => __( 'South African Rand', 'postgate' ),
			'ZMW' => __( 'Zambian Kwacha', 'postgate' ),
		];
	}
}
