<?php

use Mtc\Core\Currency;

/**
 * Mtc Currency Class
 *
 * Wrapper class for ecom currency logic.
 *
 * @author Lukas Giegerich | mtc.
 * @version 2014-04-01
 *
 * @todo Move currency functionality from basket and item classes as well as function file to this class
 */
class Currencies {

    /**
     * All currency rates will be stored relative to this one. Table will auto
     * update if changed during active session.
     * @var string
     */
    const SITE_CURRENCY = 'GBP';

    /**
     * URL for currency feed (serves rates in euro)
     * @var string
     */
    private static $CURRENCY_FEED_ADDRESS = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';

    /**
     * Minimum number of currencies expected from feed. There were 33 at the
     * time of writing (1/4/14) but I thought a lower limit would be better in
     * case the number ever drops.
     *
     * The limit of 10 should avoid processing null results but not require a
     * global update if the ECB decides to change the feed.
     *
     * @var int
     */
    private static $MIN_NUM_CURRENCIES = 10;

    /**
     * Determines if DEV_EMAIL should recieve currency error reports
     * @var boolean
     */
    private static $NOTIFY_DEV = true;

    /**
     * Currency rates relative to the Euro
     * @var array
     */
    private $euro_rates = [];

    /**
     * Currency rates relative to the British Pound
     * @var array
     */
    private $site_rates = [];

    function __construct() {
        /*
         * only calls this block if there is an active session
         */
        if (session_status() == PHP_SESSION_ACTIVE) {
            $this->populateCurrencySessions();

            if (isset($_SESSION['SITE_CURRENCY']) && $_SESSION['SITE_CURRENCY'] != self::SITE_CURRENCY) {
                $this->updateCurrencyTable();
            }
            $_SESSION['SITE_CURRENCY'] = self::SITE_CURRENCY;
        }
    }

    /**
     * Returns a value for the active currency based on the site's set currency
     * @param float $price in self::SITE_CURRENCY
     * @return float
     */
    public function getCurrency($price) {
        return $price * $_SESSION['c_rates'][$_SESSION['currency']];
    }

    /**
     * wrapper method calling methods needed to perform cron update
     */
    public function updateCurrencyTable() {
        $this->getEURRatesFromRemoteFeed();
        $this->calculateSiteRates();
        $this->saveSiteRates();
    }

    /**
     * Instantiates currency and c_rates sessions. If the table with the rates
     * is empty or outdated the method attempts to populate it.
     */
    public function populateCurrencySessions() {

        if (!isset($_SESSION['currency']) || $_SESSION['currency'] == '') {
            $_SESSION['currency'] = self::SITE_CURRENCY;
        }

        if (!isset($_SESSION['c_rates'])) {

            $currencies = Currency::all();

            /*
             * populate the c_rates session
             */
            foreach ($currencies as $currency) {
                $_SESSION['c_rates'][$currency->currency] = $currency->ratio;
            }
        }
    }

    /**
     * Connects to ECB feed to fetch euro rates. Based on code provided here:
     * http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html
     *
     * @return boolean
     */
    private function getEURRatesFromRemoteFeed() {
        $XML_content = file(self::$CURRENCY_FEED_ADDRESS);
        if (is_array($XML_content) && count($XML_content) > 0) {
            $this->euro_rates['EUR'] = 1;
            foreach ($XML_content as $line) {
                if (preg_match("/currency='([[:alpha:]]+)'/", $line, $currency_code)) {
                    if (preg_match("/rate='([[:graph:]]+)'/", $line, $rate)) {
                        $this->euro_rates[$currency_code[1]] = $rate[1];
                    }
                }
            }
            return true;
        }
        elseif (self::$NOTIFY_DEV) {
            mail(DEV_EMAIL, 'ERROR: ' . config('app.name') . ' currency issue', config('app.name') . ' currency cron job failed to connect to ECB at ' . date('Y-m-d H:i:s'));
            return false;
        }
    }

    /**
     * Takes euro rates and loops them against self::SITE_CURRENCY to eur rate
     * to determine self::SITE_CURRENCY
     * rates.
     *
     * @return boolean
     */
    private function calculateSiteRates() {
        if (isset($this->euro_rates[self::SITE_CURRENCY])) {
            $site_rate = $this->euro_rates[self::SITE_CURRENCY];
            foreach ($this->euro_rates as $currency => $rate) {
                $this->site_rates[$currency] = round($rate / $site_rate, 4);
            }
            return true;
        }
        elseif (self::$NOTIFY_DEV) {
            mail(DEV_EMAIL, 'ERROR: ' . config('app.name') . ' currency issue', config('app.name') . ' site currency not included in xml feed currencies at ' . date('Y-m-d H:i:s'));
            return false;
        }
    }

    /**
     * Updates currencies table from self::SITE_CURRENCY rates
     *
     * @return void
     */
    private function saveSiteRates(): void
    {
        if (count($this->site_rates) >= self::$MIN_NUM_CURRENCIES && isset($this->site_rates[self::SITE_CURRENCY])) {
            foreach ($this->site_rates as $currency => $rate) {
                Currency::query()
                    ->updateOrCreate([
                        'currency' => $currency,
                    ],
                    [
                        'ratio' => $rate,
                    ]);
            }
        }
    }

}
