<?php

/**
 *
 * This file is part of phpFastCache.
 *
 * @license MIT License (MIT)
 *
 * For full copyright and license information, please see the docs/CREDITS.txt file.
 *
 * @author Khoa Bui (khoaofgod)  <khoaofgod@gmail.com> https://www.phpfastcache.com
 * @author Georges.L (Geolim4)  <contact@geolim4.com>
 *
 */
declare (strict_types=1);
namespace WCPOS\Vendor\Phpfastcache\Drivers\Cassandra;

use Cassandra;
use Cassandra\Exception;
use Cassandra\Exception\InvalidArgumentException;
use Cassandra\Session as CassandraSession;
use DateTime;
use WCPOS\Vendor\Phpfastcache\Cluster\AggregatablePoolInterface;
use WCPOS\Vendor\Phpfastcache\Core\Pool\DriverBaseTrait;
use WCPOS\Vendor\Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
use WCPOS\Vendor\Phpfastcache\Entities\DriverStatistic;
use WCPOS\Vendor\Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
use WCPOS\Vendor\Phpfastcache\Exceptions\PhpfastcacheLogicException;
use WCPOS\Vendor\Psr\Cache\CacheItemInterface;
/**
 * Class Driver
 * @package phpFastCache\Drivers
 * @property CassandraSession $instance Instance of driver service
 * @property Config $config Config object
 * @method Config getConfig() Return the config object
 */
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
{
    protected const CASSANDRA_KEY_SPACE = 'phpfastcache';
    protected const CASSANDRA_TABLE = 'cacheItems';
    use DriverBaseTrait;
    /**
     * @return bool
     */
    public function driverCheck() : bool
    {
        return \extension_loaded('Cassandra') && \class_exists(Cassandra::class);
    }
    /**
     * @return string
     */
    public function getHelp() : string
    {
        return <<<HELP
<p>
To install the php Cassandra extension via Pecl:
<code>sudo pecl install cassandra</code>
More information on: https://github.com/datastax/php-driver
Please note that this repository only provide php stubs and C/C++ sources, it does NOT provide php client.
</p>
HELP;
    }
    /**
     * @return DriverStatistic
     * @throws Exception
     */
    public function getStats() : DriverStatistic
    {
        $result = $this->instance->execute(new Cassandra\SimpleStatement(\sprintf('SELECT SUM(cache_length) as cache_size FROM %s.%s', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE)));
        return (new DriverStatistic())->setSize($result->first()['cache_size'])->setRawData([])->setData(\implode(', ', \array_keys($this->itemInstances)))->setInfo('The cache size represents only the cache data itself without counting data structures associated to the cache entries.');
    }
    /**
     * @return bool
     * @throws PhpfastcacheLogicException
     * @throws Exception
     */
    protected function driverConnect() : bool
    {
        if ($this->instance instanceof CassandraSession) {
            throw new PhpfastcacheLogicException('Already connected to Couchbase server');
        }
        $clientConfig = $this->getConfig();
        $clusterBuilder = Cassandra::cluster()->withContactPoints($clientConfig->getHost())->withPort($clientConfig->getPort());
        if (!empty($clientConfig->isSslEnabled())) {
            if (!empty($clientConfig->isSslVerify())) {
                $sslBuilder = Cassandra::ssl()->withVerifyFlags(Cassandra::VERIFY_PEER_CERT);
            } else {
                $sslBuilder = Cassandra::ssl()->withVerifyFlags(Cassandra::VERIFY_NONE);
            }
            $clusterBuilder->withSSL($sslBuilder->build());
        }
        $clusterBuilder->withConnectTimeout($clientConfig->getTimeout());
        if ($clientConfig->getUsername()) {
            $clusterBuilder->withCredentials($clientConfig->getUsername(), $clientConfig->getPassword());
        }
        $this->instance = $clusterBuilder->build()->connect();
        /**
         * In case of emergency:
         * $this->instance->execute(
         *      new Cassandra\SimpleStatement(\sprintf("DROP KEYSPACE %s;", self::CASSANDRA_KEY_SPACE))
         * );
         */
        $this->instance->execute(new Cassandra\SimpleStatement(\sprintf("CREATE KEYSPACE IF NOT EXISTS %s WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };", self::CASSANDRA_KEY_SPACE)));
        $this->instance->execute(new Cassandra\SimpleStatement(\sprintf('USE %s;', self::CASSANDRA_KEY_SPACE)));
        $this->instance->execute(new Cassandra\SimpleStatement(\sprintf('
                CREATE TABLE IF NOT EXISTS %s (
                    cache_uuid uuid,
                    cache_id varchar,
                    cache_data text,
                    cache_creation_date timestamp,
                    cache_expiration_date timestamp,
                    cache_length int,
                    PRIMARY KEY (cache_id)
                );', self::CASSANDRA_TABLE)));
        return \true;
    }
    /**
     * @param CacheItemInterface $item
     * @return null|array
     */
    protected function driverRead(CacheItemInterface $item)
    {
        try {
            $options = $this->getCompatibleExecutionOptionsArgument(['arguments' => ['cache_id' => $item->getKey()], 'page_size' => 1]);
            $query = \sprintf('SELECT cache_data FROM %s.%s WHERE cache_id = :cache_id;', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE);
            $results = $this->instance->execute(new Cassandra\SimpleStatement($query), $options);
            if ($results instanceof Cassandra\Rows && $results->count() === 1) {
                return $this->decode($results->first()['cache_data']);
            }
            return null;
        } catch (Exception $e) {
            return null;
        }
    }
    /**
     * @param CacheItemInterface $item
     * @return bool
     * @throws PhpfastcacheInvalidArgumentException
     */
    protected function driverWrite(CacheItemInterface $item) : bool
    {
        /**
         * Check for Cross-Driver type confusion
         */
        if ($item instanceof Item) {
            try {
                $cacheData = $this->encode($this->driverPreWrap($item));
                $options = $this->getCompatibleExecutionOptionsArgument(['arguments' => ['cache_uuid' => new Cassandra\Uuid(), 'cache_id' => $item->getKey(), 'cache_data' => $cacheData, 'cache_creation_date' => new Cassandra\Timestamp((new DateTime())->getTimestamp()), 'cache_expiration_date' => new Cassandra\Timestamp($item->getExpirationDate()->getTimestamp()), 'cache_length' => \strlen($cacheData)], 'consistency' => Cassandra::CONSISTENCY_ALL, 'serial_consistency' => Cassandra::CONSISTENCY_SERIAL]);
                $query = \sprintf('INSERT INTO %s.%s
                    (
                      cache_uuid, 
                      cache_id, 
                      cache_data, 
                      cache_creation_date, 
                      cache_expiration_date,
                      cache_length
                    )
                  VALUES (:cache_uuid, :cache_id, :cache_data, :cache_creation_date, :cache_expiration_date, :cache_length);
            ', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE);
                $result = $this->instance->execute(new Cassandra\SimpleStatement($query), $options);
                /**
                 * There's no real way atm
                 * to know if the item has
                 * been really upserted
                 */
                return $result instanceof Cassandra\Rows;
            } catch (InvalidArgumentException $e) {
                throw new PhpfastcacheInvalidArgumentException($e, 0, $e);
            }
        } else {
            throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
        }
    }
    /********************
     *
     * PSR-6 Extended Methods
     *
     *******************/
    /**
     * @param CacheItemInterface $item
     * @return bool
     * @throws PhpfastcacheInvalidArgumentException
     */
    protected function driverDelete(CacheItemInterface $item) : bool
    {
        /**
         * Check for Cross-Driver type confusion
         */
        if ($item instanceof Item) {
            try {
                $options = $this->getCompatibleExecutionOptionsArgument(['arguments' => ['cache_id' => $item->getKey()]]);
                $result = $this->instance->execute(new Cassandra\SimpleStatement(\sprintf('DELETE FROM %s.%s WHERE cache_id = :cache_id;', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE)), $options);
                /**
                 * There's no real way atm
                 * to know if the item has
                 * been really deleted
                 */
                return $result instanceof Cassandra\Rows;
            } catch (Exception $e) {
                return \false;
            }
        } else {
            throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
        }
    }
    /**
     * @return bool
     */
    protected function driverClear() : bool
    {
        try {
            $this->instance->execute(new Cassandra\SimpleStatement(\sprintf('TRUNCATE %s.%s;', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE)));
            return \true;
        } catch (Exception $e) {
            return \false;
        }
    }
    /**
     * @param array<string, mixed> $options
     * @return array<string, mixed>|Cassandra\ExecutionOptions
     */
    protected function getCompatibleExecutionOptionsArgument(array $options)
    {
        if ($this->getConfig()->isUseLegacyExecutionOptions()) {
            return new Cassandra\ExecutionOptions($options);
        }
        return $options;
    }
}
