<?php
/**
 * Currency settings and price formatting.
 *
 * @package TutorHelper_Bookings
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Provides TutorHelper currency configuration and formatting helpers.
 */
final class TutorHelper_Currency {

	/**
	 * WordPress option storing the tutor's working currency.
	 *
	 * @var string
	 */
	const OPTION_CURRENCY =
		'tutorhelper_bookings_currency';

	/**
	 * Default currency for new and existing installations.
	 *
	 * @var string
	 */
	const DEFAULT_CURRENCY = 'GBP';


	/**
	 * Return the currencies available in TutorHelper.
	 *
	 * Currency amounts are stored as integer minor units. The decimals
	 * value determines how many minor-unit digits the currency uses.
	 *
	 * @return array
	 */
	public static function get_supported_currencies() {
		$currencies = array(
			'GBP' => array(
				'name'            => __(
					'Pound sterling',
					'tutorhelper-bookings'
				),
				'symbol'          => '£',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'EUR' => array(
				'name'            => __(
					'Euro',
					'tutorhelper-bookings'
				),
				'symbol'          => '€',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'USD' => array(
				'name'            => __(
					'US dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => '$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'CAD' => array(
				'name'            => __(
					'Canadian dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => 'C$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'AUD' => array(
				'name'            => __(
					'Australian dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => 'A$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'NZD' => array(
				'name'            => __(
					'New Zealand dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => 'NZ$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'CHF' => array(
				'name'            => __(
					'Swiss franc',
					'tutorhelper-bookings'
				),
				'symbol'          => 'CHF',
				'decimals'        => 2,
				'symbol_position' => 'before_space',
			),

			'JPY' => array(
				'name'            => __(
					'Japanese yen',
					'tutorhelper-bookings'
				),
				'symbol'          => '¥',
				'decimals'        => 0,
				'symbol_position' => 'before',
			),

			'INR' => array(
				'name'            => __(
					'Indian rupee',
					'tutorhelper-bookings'
				),
				'symbol'          => '₹',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'ZAR' => array(
				'name'            => __(
					'South African rand',
					'tutorhelper-bookings'
				),
				'symbol'          => 'R',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'SGD' => array(
				'name'            => __(
					'Singapore dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => 'S$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'HKD' => array(
				'name'            => __(
					'Hong Kong dollar',
					'tutorhelper-bookings'
				),
				'symbol'          => 'HK$',
				'decimals'        => 2,
				'symbol_position' => 'before',
			),

			'SEK' => array(
				'name'            => __(
					'Swedish krona',
					'tutorhelper-bookings'
				),
				'symbol'          => 'kr',
				'decimals'        => 2,
				'symbol_position' => 'after_space',
			),

			'NOK' => array(
				'name'            => __(
					'Norwegian krone',
					'tutorhelper-bookings'
				),
				'symbol'          => 'kr',
				'decimals'        => 2,
				'symbol_position' => 'after_space',
			),

			'DKK' => array(
				'name'            => __(
					'Danish krone',
					'tutorhelper-bookings'
				),
				'symbol'          => 'kr',
				'decimals'        => 2,
				'symbol_position' => 'after_space',
			),
		);

		/**
		 * Filters currencies supported by TutorHelper Bookings.
		 *
		 * Extensions may add currencies without replacing the core helper.
		 *
		 * @param array $currencies Currency definitions keyed by ISO code.
		 */
		return apply_filters(
			'tutorhelper_bookings_supported_currencies',
			$currencies
		);
	}


	/**
	 * Return the configured working currency.
	 *
	 * @return string
	 */
	public static function get_currency() {
		$currency = strtoupper(
			sanitize_text_field(
				get_option(
					self::OPTION_CURRENCY,
					self::DEFAULT_CURRENCY
				)
			)
		);

		$currencies =
			self::get_supported_currencies();

		if ( ! isset( $currencies[ $currency ] ) ) {
			return self::DEFAULT_CURRENCY;
		}

		return $currency;
	}


	/**
	 * Validate a submitted currency code.
	 *
	 * Invalid values fall back to the existing valid currency rather
	 * than allowing an unsupported code to be saved.
	 *
	 * @param mixed $currency Submitted option value.
	 *
	 * @return string
	 */
	public static function sanitize_currency(
		$currency
	) {
		$currency = strtoupper(
			sanitize_text_field(
				(string) $currency
			)
		);

		$currencies = self::get_supported_currencies();

		if ( isset( $currencies[ $currency ] ) ) {
			return $currency;
		}

		$current_currency = self::get_currency();

        TutorHelper_Logger::error(
            'TutorHelper currency setting update rejected.',
            array(
                'submitted_currency' => $currency,
                'current_currency'   => $current_currency,
                'reason'             =>
                    'unsupported_currency',
            )
        );

        add_settings_error(
            self::OPTION_CURRENCY,
            'tutorhelper_invalid_currency',
            __(
                'The selected currency is not supported.',
                'tutorhelper-bookings'
            ),
            'error'
        );

        return $current_currency;
	}


	/**
	 * Return one currency definition.
	 *
	 * @param string $currency ISO currency code.
	 *
	 * @return array
	 */
	public static function get_currency_details(
		$currency = ''
	) {
		$currency = $currency
			? strtoupper(
				sanitize_text_field( $currency )
			)
			: self::get_currency();

		$currencies =
			self::get_supported_currencies();

		if ( isset( $currencies[ $currency ] ) ) {
			return $currencies[ $currency ];
		}

		return $currencies[
			self::DEFAULT_CURRENCY
		];
	}


	/**
	 * Return the number of decimal places used by a currency.
	 *
	 * @param string $currency ISO currency code.
	 *
	 * @return int
	 */
	public static function get_decimal_places(
		$currency = ''
	) {
		$details = self::get_currency_details(
			$currency
		);

		return isset( $details['decimals'] )
			? absint( $details['decimals'] )
			: 2;
	}


	/**
	 * Convert a major-unit amount into integer minor units.
	 *
	 * Examples:
	 * - GBP 50.00 becomes 5000.
	 * - JPY 500 becomes 500.
	 *
	 * @param string|float|int $amount   Major-unit amount.
	 * @param string           $currency ISO currency code.
	 *
	 * @return int
	 */
	public static function major_to_minor_units(
		$amount,
		$currency = ''
	) {
		$currency = $currency
			? strtoupper(
				sanitize_text_field( $currency )
			)
			: self::get_currency();

		$decimals = self::get_decimal_places(
			$currency
		);

		$normalised_amount = str_replace(
			array(
				',',
				' ',
			),
			'',
			(string) $amount
		);

		if ( ! is_numeric( $normalised_amount ) ) {
			return 0;
		}

		$multiplier = pow(
			10,
			$decimals
		);

		return (int) round(
			(float) $normalised_amount
			* $multiplier
		);
	}


	/**
	 * Convert integer minor units to a major-unit decimal string.
	 *
	 * @param int    $minor_units Amount in minor units.
	 * @param string $currency    ISO currency code.
	 *
	 * @return string
	 */
	public static function minor_to_major_units(
		$minor_units,
		$currency = ''
	) {
		$currency = $currency
			? strtoupper(
				sanitize_text_field( $currency )
			)
			: self::get_currency();

		$decimals = self::get_decimal_places(
			$currency
		);

		$divisor = pow(
			10,
			$decimals
		);

		$amount = (int) $minor_units
			/ $divisor;

		return number_format(
			$amount,
			$decimals,
			'.',
			''
		);
	}


	/**
	 * Format integer minor units for display.
	 *
	 * @param int    $minor_units Amount in minor units.
	 * @param string $currency    ISO currency code.
	 * @param bool   $show_code   Whether to append the ISO code.
	 *
	 * @return string
	 */
	public static function format_minor_units(
		$minor_units,
		$currency = '',
		$show_code = false
	) {
		$currency = $currency
			? strtoupper(
				sanitize_text_field( $currency )
			)
			: self::get_currency();

		$details = self::get_currency_details(
			$currency
		);

		$decimals = self::get_decimal_places(
			$currency
		);

		$divisor = pow(
			10,
			$decimals
		);

		$amount = (int) $minor_units
			/ $divisor;

		$formatted_number = number_format_i18n(
			$amount,
			$decimals
		);

		$symbol = isset( $details['symbol'] )
			? $details['symbol']
			: $currency;

		$position = isset(
			$details['symbol_position']
		)
			? $details['symbol_position']
			: 'before';

		switch ( $position ) {
			case 'before_space':
				$formatted =
					$symbol
					. ' '
					. $formatted_number;
				break;

			case 'after':
				$formatted =
					$formatted_number
					. $symbol;
				break;

			case 'after_space':
				$formatted =
					$formatted_number
					. ' '
					. $symbol;
				break;

			case 'before':
			default:
				$formatted =
					$symbol
					. $formatted_number;
				break;
		}

		if ( $show_code ) {
			$formatted .= ' ' . $currency;
		}

		return $formatted;
	}
}
