<?php

declare(strict_types=1);

namespace LidonationDevs\ADAPAYFWC\Services;

use Exception;
use LidonationDevs\ADAPAYFWC\Enums\CurrencyEnum;
use LidonationDevs\ADAPAYFWC\Integrations\AdaUsdExchange\AdaUsdExchangeConnector;
use LidonationDevs\ADAPAYFWC\Integrations\AdaUsdExchange\Requests\CoinapiGetQuoteRequest;
use LidonationDevs\ADAPAYFWC\Integrations\AdaUsdExchange\Requests\CoinbaseGetQuoteRequest;
use LidonationDevs\ADAPAYFWC\Integrations\AdaUsdExchange\Requests\CoinmarketcapGetQuoteRequest;
use Saloon\Http\Response;


class AdaUsdQuoteService
{
    public $rate;

    public array $ratesArray = [];

    public function __construct(public CurrencyEnum|null $base = null, public CurrencyEnum|null $quote = null)
    {
        if (!is_null($base) && !is_null($quote)) {
            $this->getHttpRate($base, 'HOSKY');
        }
    }

    public function getHttpRate()
    {
        $connector = new AdaUsdExchangeConnector();

        $pool = $connector->pool([
            'coinapi' => new CoinApiGetQuoteRequest($this->base->name, $this->quote->name),
            'coinbase' => new CoinbaseGetQuoteRequest($this->base->name, $this->quote->name),
            'coinmarketcap' => new CoinmarketcapGetQuoteRequest(2010),
        ]);

        $pool->withResponseHandler(function (Response $response, string $key) {
            match ($key) {
                'coinapi' => $this->resolveCoinapi($response),
                'coinbase' => $this->resolveCoinbase($response),
                'coinmarketcap' => $this->resolveCoinmarketcap($response),
            };
        })->send()->wait();

        // if rates array is null throw error, no endpoint resolved, else calculate the average
        $avgRate = !empty($this->ratesArray) ? $this->calAvg($this->ratesArray) : throw new Exception("No exchange rate fetched, please check endpoints");
        $this->rate = $avgRate;
        
        return $this;
    }

    //calculates average rate from an array.
    protected function calAvg($rateArray): float|int
    {
        return array_sum($rateArray) / count($rateArray);
    }

    public function resolveCoinapi(Response $response)
    {
        if ($response->ok()) {
            $this->ratesArray[] = $response->json()['rate'];
            return $response->json()['rate'];
        } else {
            return null;
            // error_log("Error: coinapi endpoint did not resolve");
        }
    }

    public function resolveCoinbase(Response $response)
    {
        if ($response->ok()) {
            $this->ratesArray[] = (float) $response->json()['data']['rates'][$this->quote->name];
        } else {
            return null;
            // error_log("Error: coinbase endpoint did not resolve");
        }
    }

    public function resolveCoinmarketcap(Response $response)
    {
        $quotes = null;

        if ($response->ok()) {
            $responseData = $response->json()['data'];
            if (isset($responseData)) {
                $data = (object) ($responseData[2010] ?? null);
                if (isset($data?->quote['USD'])) {
                    $quotes = (object) $data?->quote['USD'];
                }
            }

            // if baseCurrency is USD, then return inverse price
            $inverse = !($this->base->name === 'ADA');

            $this->ratesArray[] = (float) $inverse ? 1 / $quotes->price : $quotes->price;
        } else {
            // error_log("Error: coinmarketcap endpoint did not resolve");
            return null;
        }
    }
}
