<?php

namespace objects;

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

class fxratesapi_conversion_rates_o extends db
{
    use db_object_t;

    public object_property $base;
    public object_property $target;
    public object_property $result;
    public object_property $endpoint;
    public object_property $created_at;


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

    /**
     * Add a new object to the database
     * @param string $base The base currency to use (e.g. "DKK")
     * @param string $target The target currency, (e.g. "EUR")
     * @param string $result
     * @param string $endpoint
     * @return void
     * @throws Exception If the object was not created successfully
     */
    public function add(string $base, string $target, string $result, string $endpoint): void
    {
        global /** @var db $db */
        $db;
        // Sanitize the input
        $base = $db->escape_string($base);
        $target = $db->escape_string($target);
        $result = $db->escape_string($result);
        $endpoint = $db->escape_string($endpoint);
        // Add the object
        $tmp_id = self::add_object([
            'base' => $base,
            'target' => $target,
            'result' => $result,
            'endpoint' => $endpoint
        ]);
        $this->id = $tmp_id;
        self::getObjectProperties();
        self::objectChanged();
    }

    public function getObjectProperties(): void
    {
        $this->base = new object_property($this->table, $this->id, 'base', 'string', false);
        $this->target = new object_property($this->table, $this->id, 'target', 'string', false);
        $this->result = new object_property($this->table, $this->id, 'result', 'string', false);
        $this->endpoint = new object_property($this->table, $this->id, 'endpoint', 'string', false);
        $this->created_at = new object_property($this->table, $this->id, 'created_at', 'datetime', false);
    }

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

    /**
     * Get the amount of requests made today
     * @return int
     */
    public function getTodayCount(): int
    {
        global /** @var db $db */
        $db;
        $sql = 'SELECT COUNT(*) FROM ' . $this->table . ' WHERE DATE(created_at) = CURDATE()';
        $result = $db->query($sql);
        return (int)$result->fetch_row()[0];
    }

    public function asArray(): array
    {
        return [
            'id' => (int)$this->id,
            'base' => (string)$this->base->value(),
            'target' => (string)$this->target->value(),
            'result' => (string)$this->result->value(),
            'endpoint' => (string)$this->endpoint->value(),
            'created_at' => (string)$this->created_at->value(),
        ];
    }

    /**
     * Is the conversion rate cached within the last 10 minutes?
     * @param string $base The base currency to check
     * @param string $target The target currency to check
     * @return bool
     * @throws Exception If the base or target currency is not valid
     */
    public function isCached(string $base, string $target): bool
    {
        global /** @var db $db */
        $db;
        if (!self::validateCurrencyFormat($base) || !self::validateCurrencyFormat($target)) {
            throw new Exception('The base or target currency is not valid.');
        }
        $sql = 'SELECT COUNT(*) FROM ' . $this->table . ' WHERE base = "' . $db->escape_string($base) . '" AND target = "' . $db->escape_string($target) . '" AND created_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE)';
        $result = $db->query($sql);
        return (bool)$result->fetch_row()[0];
    }

    /**
     * Validate the format of a currency
     * @note This simply checks if the currency is in the format of 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]{3}$/', $currency);
    }

    /**
     * Get the (latest) cached result for a conversion rate
     * @param string $base The base currency to check
     * @param string $target The target currency to check
     * @return object
     * @throws Exception If the base or target currency is not valid or the response is not cached
     */
    public function getCachedResult(string $base, string $target): object
    {
        global /** @var db $db */
        $db;
        if (!self::validateCurrencyFormat($base) || !self::validateCurrencyFormat($target)) {
            throw new Exception('The base or target currency is not valid.');
        }
        $result = self::getFieldsWhere(['base' => $base, 'target' => $target], ['id', 'result', 'endpoint', 'created_at']);
        if (!$result) {
            throw new Exception('The response is not cached.');
        }
        // Get the latest result
        $latest = array_pop($result);
        $this->id = $latest['id'];
        self::getObjectProperties();
        return $this;
    }

    /**
     * Get the (latest) cached response for a conversion rate
     * @param string $base The base currency to check
     * @param string $target The target currency to check
     * @return object
     * @throws Exception If the base or target currency is not valid or the response is not cached
     * @note This is a wrapper for getCachedResult
     */
    public function getCachedResponse(string $base, string $target): object
    {
        global /** @var db $db */
        $db;
        if (!self::validateCurrencyFormat($base) || !self::validateCurrencyFormat($target)) {
            throw new Exception('The base or target currency is not valid.');
        }
        $result = self::getFieldsWhere(['base' => $base, 'target' => $target], ['id', 'result', 'endpoint', 'created_at']);
        if (!$result) {
            throw new Exception('The response is not cached.');
        }
        // Get the latest result
        $latest = array_pop($result);
        $this->id = $latest['id'];
        self::getObjectProperties();
        return json_decode($latest['result']);
    }
}