<?php
/**
 * NOTICE OF LICENSE.
 *
 * Copyright 2022 Kosmonaft.dev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  @author    Alexandre DEBUSSCHÈRE <alexandre@kosmonaft.dev>
 *  @copyright 2022 - 2023 Kosmonaft.dev
 *  @license   MIT
 */

if (!defined('_PS_VERSION_')) {
    exit;
}

/**
 * Class MRMaxCache
 */
class MRMaxCache extends Cache
{

    const TABLE_NAME = 'mrmax_cache';

    /** @var bool */
    protected $is_connected;

    /**
     * @throws PrestaShopDatabaseException
     */
    public function __construct()
    {
        $this->is_connected = MRMaxTools::tableExists(self::TABLE_NAME);
        $this->_writeKeys();
    }

    /**
     * Cache a data.
     *
     * @param string $key
     * @param mixed $value
     * @param int|null $ttl The point in time after which the item MUST be considered expired.
     *   If null is passed explicitly, a default value MAY be used. If none is set,
     *   the value should be stored permanently or for as long as the implementation allows.
     * @return bool
     * @throws PrestaShopDatabaseException
     */
    protected function _set($key, $value, $ttl = 0)
    {
        if (!$this->is_connected) {
            return false;
        }

        if (!$ttl) {
            $ttl = strtotime('TOMORROW 1AM');
        } elseif ($ttl instanceof DateTimeInterface) {
            $ttl = $ttl->getTimestamp();
        } elseif (is_int($ttl)) {
            $ttl += time();
        } else {
            return false;
        }

        return (bool)Db::getInstance()->insert(
            self::TABLE_NAME,
            [
                'name' => pSQL($key),
                'value' => pSQL($value),
                'ttl' => date('Y-m-d H:i:s', $ttl)
            ],
            false,
            false,
            Db::ON_DUPLICATE_KEY
        );
    }

    /**
     * Retrieve a cached data by key.
     *
     * @param string $key
     * @return mixed
     */
    protected function _get($key)
    {
        if (!$this->is_connected) {
            return null;
        }

        $query = (new DbQuery())
            ->select('`value`')
            ->from(pSQL(self::TABLE_NAME))
            ->where('`name` = "'.pSQL($key).'"')
            ->where('`ttl` >= "'.pSQL(date('Y-m-d H:i:s')).'"');

        return Db::getInstance()->getValue($query);
    }

    /**
     * Check if a data is cached by key.
     *
     * @param string $key
     * @return bool
     */
    protected function _exists($key)
    {
        if (!$this->is_connected) {
            return false;
        }

        $query = (new DbQuery())
            ->select('`name`')
            ->from(pSQL(self::TABLE_NAME))
            ->where('`name` = "'.pSQL($key).'"')
            ->where('`ttl` >= "'.pSQL(date('Y-m-d H:i:s')).'"');

        return Db::getInstance()->getValue($query) == $key;
    }

    /**
     * @param string $key
     * @return bool
     */
    public function has($key)
    {
        return $this->_exists($key);
    }

    /**
     * Delete a data from the cache by key.
     *
     * @param string $key
     * @return bool
     */
    protected function _delete($key)
    {
        return (bool)Db::getInstance()->delete(
            self::TABLE_NAME,
            '`name` = "'.pSQL($key).'"',
            1,
            false
        );
    }

    /**
     * Write keys index.
     *
     * @return void
     * @throws PrestaShopDatabaseException
     */
    protected function _writeKeys()
    {
        $keys = array_map(
            function ($name) {
                return reset($name);
            },
            Db::getInstance()->executeS('SELECT `name` FROM `'._DB_PREFIX_.pSQL(self::TABLE_NAME).'`')
        );

        foreach ($keys as $key) {
            $this->keys[$key] = true;
        }
    }

    /**
     * Clean all cached data.
     *
     * @return bool
     */
    public function flush()
    {
        return (bool)Db::getInstance()->delete(
            self::TABLE_NAME,
            '',
            null,
            false
        );
    }
}
