<?php

namespace ChinaPayments;

class CurrencyConverter {

	/**
	 * @var CurrencyConverter|null
	 */
	protected static $_instance = null;

	public static function instance(): CurrencyConverter {
		if ( self::$_instance === null ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Get exchange rate from source currency to target currency.
	 *
	 * @param string $from Source currency code (e.g. 'USD')
	 * @param string $to   Target currency code (e.g. 'CNY')
	 * @return float|false  Exchange rate or false on failure
	 */
	public function get_rate( string $from, string $to ) {
		$from = strtoupper( $from );
		$to   = strtoupper( $to );

		if ( $from === $to ) {
			return 1.0;
		}

		$transient_key = CHINA_PAYMENTS_ALIAS . '_fx_rate_' . $from . '_' . $to;
		$cached_rate   = get_transient( $transient_key );

		if ( $cached_rate !== false ) {
			return (float) $cached_rate;
		}

		$rate = $this->_fetch_rate( $from, $to );

		if ( $rate !== false ) {
			set_transient( $transient_key, $rate, HOUR_IN_SECONDS );
		}

		return $rate;
	}

	/**
	 * Convert an amount from one currency to another.
	 *
	 * @param float  $amount Amount to convert
	 * @param string $from   Source currency code
	 * @param string $to     Target currency code
	 * @return float|false Converted amount or false on failure
	 */
	public function convert( float $amount, string $from, string $to ) {
		$rate = $this->get_rate( $from, $to );

		if ( $rate === false ) {
			return false;
		}

		return round( $amount * $rate, 2 );
	}

	/**
	 * Format an amount as CNY.
	 *
	 * @param float $amount
	 * @return string
	 */
	public function format_cny( float $amount ): string {
		return '¥' . number_format( $amount, 2 );
	}

	/**
	 * Get the display string with CNY equivalent.
	 *
	 * @param string $price_html  Original price HTML
	 * @param float  $amount      Numeric amount
	 * @param string $currency    Store currency code
	 * @return string
	 */
	public function get_display_with_cny( string $price_html, float $amount, string $currency ): string {
		if ( strtoupper( $currency ) === 'CNY' ) {
			return $price_html;
		}

		$cny_amount = $this->convert( $amount, $currency, 'CNY' );

		if ( $cny_amount === false ) {
			return $price_html;
		}

		return $price_html . ' <span class="china-payments-cny-equivalent">(~' . $this->format_cny( $cny_amount ) . ')</span>';
	}

	/**
	 * Get the current cached rate info for admin display.
	 *
	 * @param string $from
	 * @param string $to
	 * @return array
	 */
	public function get_rate_info( string $from, string $to ): array {
		$from = strtoupper( $from );
		$to   = strtoupper( $to );

		$transient_key = CHINA_PAYMENTS_ALIAS . '_fx_rate_' . $from . '_' . $to;
		$cached_rate   = get_transient( $transient_key );

		return array(
			'from'      => $from,
			'to'        => $to,
			'rate'      => ( $cached_rate !== false ? (float) $cached_rate : null ),
			'available' => ( $cached_rate !== false ),
		);
	}

	/**
	 * Fetch rate from frankfurter.app (free, no API key).
	 *
	 * @param string $from
	 * @param string $to
	 * @return float|false
	 */
	protected function _fetch_rate( string $from, string $to ) {
		$url = 'https://api.frankfurter.app/latest?from=' . urlencode( $from ) . '&to=' . urlencode( $to );

		$response = wp_remote_get( $url, array(
			'timeout' => 10,
			'headers' => array(
				'Accept' => 'application/json',
			),
		) );

		if ( is_wp_error( $response ) ) {
			return false;
		}

		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body, true );

		if ( ! isset( $data['rates'][ $to ] ) ) {
			return false;
		}

		return (float) $data['rates'][ $to ];
	}

	/**
	 * Force refresh the cached rate.
	 *
	 * @param string $from
	 * @param string $to
	 * @return float|false
	 */
	public function refresh_rate( string $from, string $to ) {
		$from = strtoupper( $from );
		$to   = strtoupper( $to );

		$transient_key = CHINA_PAYMENTS_ALIAS . '_fx_rate_' . $from . '_' . $to;
		delete_transient( $transient_key );

		return $this->get_rate( $from, $to );
	}
}
