<?php 
/**
 * Luminova Framework redis extension class.
 *
 * @package Luminova
 * @author Ujah Chigozie Peter
 * @copyright (c) Nanoblock Technology Ltd
 * @license See LICENSE file
 * @link https://luminova.ng
 */
namespace Luminova\Cache;

use \Luminova\Base\BaseCache;
use \Luminova\Exceptions\CacheException;
use \Redis;
use \DateTimeInterface;
use \DateInterval;
use \Exception;


final class RedisCache extends BaseCache
{
    /**
     * Hold the cache instance Singleton.
     * 
     * @var ?self $instance
     */
    private static ?self $instance = null;

    /**
     * Memcached connection instances.
     * 
     * @var array<string,Redis> $instances
     */
    private static array $instances = [];

    /**
     * Initializes memcache extension class instance with an optional storage name and an optional persistent ID.
     * 
     * @param string|null $storage The cache storage name (default: null). If null, you must call `create` method later.
     * @param string|null $persistentId Optional unique ID for persistent connections (default: null).
     *                      - If null is specified, will used the default persistent id from environment variables.
     *                      - If not set in environment variables `default` will be used instead,
     * 
     * @throws CacheException if there is an issue loading the cache.
     */
    public function __construct(
        ?string $storage = null, 
        private ?string $persistentId = null
    )
    {}

    /**
     * Retrieves or creates a singleton instance of the memcache extension class.
     * 
     * @param string|null $storage The cache storage name (default: null). If null, you must call `create` method later.
     * @param string|null $persistentId Optional unique ID for persistent connections (default: null).
     *                      - If null is specified, will used the default persistent id from environment variables.
     *                      - If not set in environment variables `default` will be used instead,
     * 
     * @return static The singleton instance of the cache.
     * @throws CacheException if there is an issue loading the cache.
     */
    public static function getInstance(
        ?string $storage = null, 
        ?string $persistentId = null
    ): static 
    {
        if (self::$instance === null) {
            self::$instance = new static($storage, $persistentId);
        }

        return self::$instance;
    }

    /**
     * Retrieves the current connection instance of the cache Redis being used. 
     * 
     * @return Redis|null Return the instance of Redis, otherwise null.
     */
    public function getConn(): ?Redis
    {
        return self::$instances[$this->persistentId] ?? null;
    }

    /**
     * Pings the Redis server to check its availability.
     *
     * This method attempts to set a key-value pair with a short expiration time and then retrieves it.
     * If the server responds within a reasonable time, it is considered available and the method returns 'PONG'.
     * If the server does not respond or an exception is thrown, the method returns null.
     *
     * @return string|null Returns 'PONG' if the server is available, otherwise null.
     */
    public function ping(): ?string
    {
        if(!$this->getConn()){
            return null;
        }

        try {
            return ($this->getConn()->ping() === '+PONG') ? 'PONG' : null;
        } catch (Exception) {
            return null;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setStorage(string $storage): self 
    {
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setItem(
        string $key, 
        mixed $content, 
        DateTimeInterface|int|null $expiration = 0, 
        DateInterval|int|null $expireAfter = null, 
        bool $lock = false
    ): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getItem(string $key, bool $onlyContent = true): mixed
    {
        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasItem(string $key): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isLocked(string $key): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function hasExpired(string $key): bool
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItem(string $key, bool $includeLocked = false): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItems(iterable $keys, bool $includeLocked = false): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function flush(): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function clear(): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function delete(string $storage, array $keys): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    protected function deleteIfExpired(): void {}

    /**
     * {@inheritdoc}
     */
    protected function read(?string $key = null): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    protected function commit(): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function execute(
        array $keys, 
        bool $withCas = false, 
        ?callable $callback = null
    ): bool
    {
        return false;
    }
}