<?php

namespace App\Services;

use App\Facades\Settings;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Currency;

class CurrencyService
{
    /**
     * Map ECB attributes to database columns
     *
     * @var string[]
     */
    private static $attribute_map = [
        'currency' => 'code',
        'rate' => 'ratio',
    ];

    /**
     * Currencies pegged to USD and not published by the ECB feed.
     * Values are the local-currency-per-USD peg used to derive the EUR ratio.
     */
    private const USD_PEGGED_RATES = [
        'AED' => 3.6725,
        'SAR' => 3.75,
    ];

    /**
     * Update currencies in system
     *
     * @return bool
     */
    public function update(): bool
    {
        try {
            $this->save($this->getRates());
            return true;
        } catch (Exception $exception) {
            Log::error('Failed to sync currencies', [
                $exception
            ]);
            return false;
        }
    }

    public function convertToCurrency(float $price, string $priceCurrency, ?string $base_currency = null)
    {
        if (empty($base_currency)) {
            $base_currency = Settings::get('app-details-currency');
        }

        if ($priceCurrency === $base_currency) {
            return $price;
        }

        // Currency table stores ratios against EUR,
        // so we need to convert price to EUR and then convert it to system price
        $priceCurrencyRate = Currency::query()
            ->where('code', $priceCurrency)
            ->first();

        $priceInEuro = $priceCurrencyRate->ratio * $price;

        $appCurrency = Currency::query()
            ->where('code', $base_currency)
            ->first();

        return $appCurrency->ratio * $priceInEuro;
    }

    /**
     * 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
     * Rates are defined against EUR.
     *
     * @return array
     */
    private function getRates(): array
    {
        $currencies = [
            ['code' => 'EUR', 'ratio' => 1.0],
        ];
        $response = Http::get(config('services.currency.endpoint'));

        $document = simplexml_load_string($response->body());
        $usdRate = null;
        foreach ($document->Cube->Cube->Cube as $currency) {
            $entry = [];
            foreach ($currency->attributes() as $attribute => $value) {
                $entry[self::$attribute_map[$attribute]] = (string)$value;
            }
            if (($entry['code'] ?? null) === 'USD') {
                $usdRate = (float)$entry['ratio'];
            }
            $currencies[] = $entry;
        }

        if ($usdRate !== null) {
            foreach (self::USD_PEGGED_RATES as $code => $perUsd) {
                $currencies[] = ['code' => $code, 'ratio' => $usdRate * $perUsd];
            }
        }

        return $currencies;
    }


    /**
     * Save Currencies
     *
     * @param array $rates
     * @return void
     */
    private function save(array $rates): void
    {
        Currency::query()->upsert($rates, 'code');
    }
}
