<?php

namespace objects;

use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;

class currency_conversion_rates_o extends db
{
    use db_object_t;

    public object_property $currency;
    public object_property $rate;
    public object_property $updated_at;
    public object_property $created_at;


    public function structure(): void
    {
        $this->setTable('currency_conversion_rates');
    }

    public function asArray(): array
    {
        return [
            'id' => (int)$this->id,
            'currency' => (string)$this->currency->value(),
            'rate' => (float)$this->rate->value(),
            'updated_at' => (string)$this->updated_at->value(),
            'created_at' => (string)$this->created_at->value(),
        ];
    }

    /**
     * Update (or create) the conversion rates
     * @param object $currencies The currencies to update (e.g. { "DKK": 1.0, "EUR": 0.5 })
     * @return void
     * @throws Exception If the currency is not valid
     * @throws Exception If the rate is not valid
     */
    public function setMany(object $currencies): void
    {
        // Check if the currencies are valid
        if (!is_object($currencies)) {
            throw new Exception('The currencies are not valid.');
        }
        // Check if the currencies are empty
        if (empty($currencies)) {
            throw new Exception('The currencies are empty.');
        }
        // Transform the object to an array
        $currencies = (array)$currencies;
        foreach ( $currencies as $currency => $relative_rate ) {
            // Check if the currency is valid
            if (!self::validateCurrencyFormat($currency)) {
                throw new Exception('The currency is not valid. (' . $currency . ')');
            }
            // Check if the rate is valid
            if ($relative_rate <= 0) {
                throw new Exception('The rate is not valid.');
            }
            // Update the conversion rate
            self::set($currency, $relative_rate);
        }
    }

    /**
     * Validate the format of a currency
     * @note This simply checks if the currency is in the format of up to three uppercase letters.
     * @note This does not check if the currency is actually valid.
     * @param string $currency
     * @return bool
     */
    public static function validateCurrencyFormat(string $currency): bool
    {
        return (bool)preg_match('/^[A-Z]{1,3}$/', $currency);
    }

    /**
     * Update (or create) the conversion rate for a currency
     * @param string $currency The currency to update (e.g. "DKK")
     * @param float $relative_rate The conversion rate to update (e.g. 1.0)
     * @return void
     * @throws Exception
     */
    public function set(string $currency, float $relative_rate): void
    {
        global /** @var db $db */
        $db;
        // Check if the object exists
        if (!self::countRowsWhere(['currency' => $currency])) {
            // If not, add it
            self::add($currency, $relative_rate);
            return;
        }
        // If it does, update it
        // Get the object
        $result = self::getFieldsWhere(['currency' => $currency], ['id']);
        if (empty($result)) {
            throw new Exception('The currency conversion rate does exist, but the id was not found.');
        }
        self::select((int)$result[0]['id']);
        // Update the object
        $this->rate->set((float)$relative_rate);
        self::objectChanged();
    }

    /**
     * Add a new object to the database
     * @param string $currency The currency to update (e.g. "DKK")
     * @param float $relative_rate The conversion rate to update (e.g. 1.0)
     * @return currency_conversion_rates_o
     * @throws Exception If the object was not created successfully
     */
    public function add(string $currency, float $relative_rate): self
    {
        global /** @var db $db */
        $db;
        // Sanitize the input
        $currency = $db->escape_string($currency);
        $relative_rate = (float)$relative_rate;
        // Check if the currency is valid
        if (!self::validateCurrencyFormat($currency)) {
            throw new Exception('The currency is not valid.');
        }
        // Check if the rate is valid
        if ($relative_rate <= 0) {
            throw new Exception('The rate is not valid.');
        }
        // Add the object
        $tmp_id = self::add_object([
            'currency' => $currency,
            'rate' => $relative_rate,
        ]);
        $this->id = $tmp_id;
        self::getObjectProperties();
        self::objectChanged();
        return $this;
    }

    public function getObjectProperties(): void
    {
        $this->currency = new object_property($this->table, $this->id, 'currency', 'string', false);
        $this->rate = new object_property($this->table, $this->id, 'rate', 'float', false);
        $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'datetime', false);
        $this->created_at = new object_property($this->table, $this->id, 'created_at', 'datetime', false);
    }

    public function objectChanged(): void
    {
        //TODO: Add cache invalidation
    }

    /**
     * Convert an amount from the base currency to the target currency
     * @param string $currency The target currency (e.g. "EUR")
     * @param float $amount The amount to convert in the base currency (e.g. 100) which is 100 DKK
     * @throws Exception
     */
    public function convertTo(string $currency, float $amount): float
    {
        // Check if the currency is valid
        if (!self::validateCurrencyFormat($currency)) {
            throw new Exception('The currency is not valid.');
        }
        // Check if the amount is valid
        if ($amount <= 0) {
            throw new Exception('The amount is not valid.');
        }
        // Convert the amount
        return self::getRate($currency) * $amount;
    }

    /**
     * Get the conversion rate for a currency
     * @param string $currency The currency to get (e.g. "DKK")
     * @return float The conversion rate (e.g. 1.0)
     * @throws Exception If the currency is not valid
     */
    public function getRate(string $currency): float
    {
        // Check if the currency is valid
        if (!self::validateCurrencyFormat($currency)) {
            throw new Exception('The currency is not valid.');
        }
        // Check if the currency is 'DKK', then return 1.0 (base currency)
        if ($currency === 'DKK') {
            return 1.0;
        }
        // Get the conversion rate
        $result = self::getFieldsWhere(['currency' => $currency], ['rate']);
        if (empty($result)) {
            throw new Exception('The currency conversion rate does not exist.');
        }
        return (float)$result[0]['rate'];
    }

}