<?php
/**
 * Visitor country detection and country → currency resolution.
 *
 * Country comes from the CDN/edge that already knows it — Cloudflare's CF-IPCountry header on
 * every request — so no geo database, API call, or account access is needed. The resolved
 * currency is always validated against the currencies the merchant actually offers.
 *
 * @package BW_2Checkout_Pricing
 */

defined( 'ABSPATH' ) || exit;

/**
 * Currency => the countries that should see it. Only currencies enabled on the 2Checkout account
 * are worth listing; anything unmapped falls back to the plugin's default currency.
 *
 * @return array<string,string[]>
 */
function bw_2checkout_pricing_currency_countries() {
	return array(
		'EUR' => array( 'AD', 'AT', 'BE', 'CY', 'EE', 'FI', 'FR', 'DE', 'GR', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'MC', 'ME', 'NL', 'PT', 'SM', 'SK', 'SI', 'ES', 'VA', 'XK' ),
		'GBP' => array( 'GB', 'GG', 'IM', 'JE' ),
		'CAD' => array( 'CA' ),
		'AUD' => array( 'AU' ),
		'NZD' => array( 'NZ' ),
		'CHF' => array( 'CH', 'LI' ),
		'SEK' => array( 'SE' ),
		'NOK' => array( 'NO' ),
		'DKK' => array( 'DK' ),
		'PLN' => array( 'PL' ),
		'CZK' => array( 'CZ' ),
		'HUF' => array( 'HU' ),
		'RON' => array( 'RO' ),
		'RSD' => array( 'RS' ),
		'RUB' => array( 'RU' ),
		'UAH' => array( 'UA' ),
		'BYN' => array( 'BY' ),
		'MDL' => array( 'MD' ),
		'TRY' => array( 'TR' ),
		'ILS' => array( 'IL' ),
		'AED' => array( 'AE' ),
		'SAR' => array( 'SA' ),
		'QAR' => array( 'QA' ),
		'KWD' => array( 'KW' ),
		'OMR' => array( 'OM' ),
		'JOD' => array( 'JO' ),
		'EGP' => array( 'EG' ),
		'DZD' => array( 'DZ' ),
		'TND' => array( 'TN' ),
		'SYP' => array( 'SY' ),
		'KES' => array( 'KE' ),
		'NGN' => array( 'NG' ),
		'NAD' => array( 'NA' ),
		'ZAR' => array( 'ZA' ),
		'INR' => array( 'IN' ),
		'CNY' => array( 'CN' ),
		'HKD' => array( 'HK' ),
		'SGD' => array( 'SG' ),
		'TWD' => array( 'TW' ),
		'KRW' => array( 'KR' ),
		'JPY' => array( 'JP' ),
		'VND' => array( 'VN' ),
		'MXN' => array( 'MX' ),
		'BRL' => array( 'BR' ),
		'ARS' => array( 'AR' ),
		'BOB' => array( 'BO' ),
		'CLP' => array( 'CL' ),
		'COP' => array( 'CO' ),
		'PEN' => array( 'PE' ),
		'PYG' => array( 'PY' ),
		'UYU' => array( 'UY' ),
		'USD' => array( 'US' ),
	);
}

/**
 * Country code => currency lookup, built once per request from the map above.
 *
 * @return array<string,string>
 */
function bw_2checkout_pricing_country_currency_map() {
	static $map = null;
	if ( null !== $map ) {
		return $map;
	}
	$map = array();
	foreach ( bw_2checkout_pricing_currency_countries() as $currency => $countries ) {
		foreach ( $countries as $cc ) {
			$map[ $cc ] = $currency;
		}
	}
	return $map;
}

/**
 * The visitor's two-letter country code, or '' if the edge didn't tell us.
 *
 * `?bw2cp_country=XX` overrides it so any country can be previewed without a VPN.
 *
 * @return string
 */
function bw_2checkout_pricing_detect_country() {
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display preference.
	if ( isset( $_GET['bw2cp_country'] ) ) {
		$forced = strtoupper( sanitize_text_field( wp_unslash( $_GET['bw2cp_country'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( preg_match( '/^[A-Z]{2}$/', $forced ) ) {
			return $forced;
		}
	}

	// Cloudflare sets the first one on every proxied request; the rest cover other edges/CDNs.
	$headers = array(
		'HTTP_CF_IPCOUNTRY',
		'HTTP_CLOUDFRONT_VIEWER_COUNTRY',
		'HTTP_X_GEOIP_COUNTRY',
		'HTTP_X_COUNTRY_CODE',
		'GEOIP_COUNTRY_CODE',
	);
	foreach ( $headers as $key ) {
		if ( empty( $_SERVER[ $key ] ) ) {
			continue;
		}
		$cc = strtoupper( sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) ) );
		// Cloudflare sends XX for unknown and T1 for Tor.
		if ( preg_match( '/^[A-Z]{2}$/', $cc ) && 'XX' !== $cc && 'T1' !== $cc ) {
			return $cc;
		}
	}
	return '';
}

/**
 * Currencies the merchant offers, from the plugin settings.
 *
 * @return string[]
 */
function bw_2checkout_pricing_allowed_currencies() {
	$raw = (string) bw_2checkout_pricing_get_setting( 'currencies', 'USD' );
	return array_values( array_filter( array_map(
		static function ( $c ) {
			return strtoupper( trim( $c ) );
		},
		explode( ',', $raw )
	) ) );
}

/**
 * The currency to show this visitor.
 *
 * Order of precedence: explicit ?bw2cp_currency= override → geolocation (when enabled) → the
 * configured default. A currency the merchant doesn't offer is never returned.
 *
 * @return string
 */
function bw_2checkout_pricing_resolve_currency() {
	$default = strtoupper( (string) bw_2checkout_pricing_get_setting( 'default_currency', 'USD' ) );
	$allowed = bw_2checkout_pricing_allowed_currencies();

	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display preference.
	if ( isset( $_GET['bw2cp_currency'] ) ) {
		$forced = strtoupper( sanitize_text_field( wp_unslash( $_GET['bw2cp_currency'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( preg_match( '/^[A-Z]{3}$/', $forced ) && ( ! $allowed || in_array( $forced, $allowed, true ) ) ) {
			return $forced;
		}
	}

	if ( ! bw_2checkout_pricing_get_setting( 'geo_enabled', true ) ) {
		return $default;
	}

	$country = bw_2checkout_pricing_detect_country();
	if ( '' === $country ) {
		return $default;
	}

	$map = bw_2checkout_pricing_country_currency_map();
	if ( ! isset( $map[ $country ] ) ) {
		return $default;
	}

	$currency = $map[ $country ];
	if ( $allowed && ! in_array( $currency, $allowed, true ) ) {
		return $default;
	}

	/**
	 * Filter the currency chosen for the current visitor.
	 *
	 * @param string $currency Resolved ISO 4217 code.
	 * @param string $country  Detected two-letter country code ('' if unknown).
	 */
	$currency = (string) apply_filters( 'bw_2checkout_pricing_currency', $currency, $country );

	// Let the warmer know this currency is in real use so it stays primed.
	if ( function_exists( 'bw_2checkout_pricing_note_currency' ) ) {
		bw_2checkout_pricing_note_currency( $currency );
	}

	return $currency;
}
