<?php
namespace Api\PhpUtils\Cache;

use Api\PhpUtils\Redis\RedisUtil;
use Api\PhpUtils\Log\FileLog;

/**
 * CacheUtil 工具类
 *
 * @date 2021-03-30
 */
class CacheUtil
{
    const PREFIX_NONE = '';
    
    const EXPIRE_NEVER              = 0;
    const EXPIRE_AFTER_FIVE_SECONDS = 5;
    const EXPIRE_AFTER_ONE_MINUTE   = 60;
    const EXPIRE_AFTER_FIVE_MINUTES = 300;
    const EXPIRE_AFTER_HALF_HOUR    = 1800;
    const EXPIRE_AFTER_ONE_HOUR     = 3600;
    const EXPIRE_AFTER_SIX_HOURS    = 21600;
    const EXPIRE_AFTER_ONE_DAY      = 86400;
    const EXPIRE_AFTER_THREE_DAYS   = 259200;
    const EXPIRE_AFTER_FIFTEEN_DAYS = 1296000;
    const EXPIRE_AFTER_ONE_MONTH    = 2592000;
    
    private static $instances = [];
    private $handler = null;
    private $expire = 86400; //@todo 讨论一下，如果不设置有效时间，默认1天，不允许设置长期有效

    /**
     * 获取实例
     *
     * @param string $serverName 服务名
     * @param array  $options 其他参数数组，优先级高于yaconf中的相同配置
     * @param string $options['serverRegion']  服务地区
     * @param bool   $options['master']        哨兵模式，默认使用主库，使用从库值为false
     * @param string $options['serializer']    序列化选项，包括 none|php|igbinary, msgpack自己实现msgpack_pack|msgpack_unpack, incr等方法只能使用none，set等方法使用none不能存Array或Object
     * @param boolean $master
     * @return mixed
     */
    public static function getInstance($serverName = '', array $options = []) 
    {
        if (empty($serverName)) {
            return null;
        }
        if (isset(self::$instances[$serverName])) {
            return self::$instances[$serverName];
        }
        return self::$instances[$serverName] = new Self($serverName, $options);
    }

    private function __construct($serverName = '', array $options = [])
    {
        if (empty($this->handler)){
            //获取连接句柄
            $this->handler = RedisUtil::getInstance($serverName, $options);
        }
    }

    public function __call($method, array $args)
    {
        if (!$this->handler) {
            return false;
        }
        return call_user_func_array([$this->handler, $method], $args);
    }

    /**
     * 获取原始数据
     * @param $key
     * @return mixed
     */
    public function getUnChange($key, $prefix = ''){
        return $this->handler->get($this->getCacheKey($prefix, $key));
    }

    /**
     * 保存原始数据
     * @param $key
     * @param $value
     * @param $expire
     * @return mixed
     */
    public function setUnChange($key , $value , $expire = null, $prefix = ''){
        if (is_null($expire)) {
            // $expire = $this->expire;
            return false;
        }
        $key = $this->getCacheKey($prefix, $key);
        if (is_int($expire) && $expire) {
            $result = $this->handler->setex($key, $expire, $value);
        } else {
            $result = $this->handler->set($key, $value);
        }
        return $result;
    }

    /**
     * 读取缓存
     * @access public
     * @param string $key 缓存变量名
     * @param mixed  $default 默认值
     * @return mixed
     */
    public function get($key, $prefix = '', $default = false)
    {
        $value = $this->handler->get($this->getCacheKey($prefix, $key));
        if (is_null($value) || false === $value) {
            return $default;
        }
        $jsonData = json_decode($value, true);
        // 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据
        return (null === $jsonData) ? $value : $jsonData;
    }

    /**
     * 读取多个缓存
     * @access public
     * @param array $keys 缓存变量名数级
     * @param mixed  $default 默认值
     * @return mixed
     */
    public function gets($keys, $prefix = '', $default = false)
    {
        if(empty($keys) || !is_array($keys)) {
            return $default;
        }
        $keys = array_values($keys);
        $values = $this->handler->mGet($this->getCacheKey($prefix, $keys));
        if (is_null($values) || false === $values) {
            return $default;
        }
        $result = [];
        foreach((array)$values as $i => $value) {
            if($value !== false) {
                $jsonData = json_decode($value, true);
                $result[$keys[$i]] = (null === $jsonData) ? $value : $jsonData;
            }
        }
        return $result;
    }

    /**
     * 写入缓存
     * @access public
     * @param string    $key 缓存变量名
     * @param mixed     $value  存储数据
     * @param integer   $expire  有效时间（秒）
     * @return boolean
     */
    public function set($key, $value, $expire = null, $prefix = '')
    {
        if (is_null($expire) || empty($expire)) {
            // $expire = $this->expire;
            return false;
        }
        $key = $this->getCacheKey($prefix, $key);
        //对数组/对象数据进行缓存处理，保证数据完整性
        $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
        if (is_int($expire) && $expire) {
            $result = $this->handler->setex($key, $expire, $value);
        } else {
            $result = $this->handler->set($key, $value);
        }
        return $result;
    }

    /**
     * 写入多个缓存
     * @access public
     * @param array    $items 缓存数组
     * @param integer   $expire  有效时间（秒）
     * @return boolean
     */
    public function sets($items, $expire = null, $prefix = '')
    {
        if (is_null($expire) || empty($expire)) {
            return false;
        }
        $this->handler->multi();
        foreach($items as $key => $value) {
            $key = $this->getCacheKey($prefix, $key);
            $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
            $this->handler->setex($key, $expire, $value);
        }
        return $this->handler->exec();
    }

    /**
     * 新增缓存，只有不存在时才写入成功
     * @access public
     * @param string    $key 缓存变量名
     * @param mixed     $value  存储数据
     * @param integer   $expire  有效时间（秒）
     * @return boolean
     */
    public function add($key, $value, $expire = null, $prefix = '')
    {
        if (is_null($expire) || empty($expire)) {
            // $expire = $this->expire;
        }
        $key = $this->getCacheKey($prefix, $key);
        //对数组/对象数据进行缓存处理，保证数据完整性
        $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
        if(is_int($expire) && $expire) {
            $result = $this->handler->setenx($key, $expire, $value);
        } else {
            $result = $this->handler->setnx($key, $value);
        }
        return $result;
    }

    /**
     * 自增缓存（针对数值缓存，且不自动序列化）
     * @access public
     * @param string    $key 缓存变量名
     * @return false|int
     */
    public function incr($key, $prefix = ''){
        return $this->handler->incr($this->getCacheKey($prefix, $key));
    }

    /**
     * 自减缓存（针对数值缓存，且不自动序列化）
     * @access public
     * @param string    $key 缓存变量名
     * @return false|int
     */
    public function decr($key, $prefix = ''){
        return $this->handler->decr($this->getCacheKey($prefix, $key));
    }

    /**
     * 自增缓存（针对数值缓存，且不自动序列化）
     * @access public
     * @param string    $key 缓存变量名
     * @param int       $step 步长
     * @return false|int
     */
    public function incrby($key, $step = 1, $prefix = '')
    {
        $key = $this->getCacheKey($prefix, $key);
        return $this->handler->incrby($key, $step);
    }

    /**
     * 自减缓存（针对数值缓存，且不自动序列化）
     * @access public
     * @param string    $key 缓存变量名
     * @param int       $step 步长
     * @return false|int
     */
    public function decrby($key, $step = 1, $prefix = '')
    {
        $key = $this->getCacheKey($prefix, $key);
        return $this->handler->decrby($key, $step);
    }

    /**
     * 删除缓存
     * @access public
     * @param string $key 缓存变量名
     * @return boolean
     */
    public function del($key, $prefix = '')
    {
        return $this->handler->del($this->getCacheKey($prefix, $key));
    }

    /**
     * 判断缓存
     * @access public
     * @param string $key 缓存变量名
     * @return bool
     */
    public function has($key, $prefix = '')
    {
        return $this->handler->get($this->getCacheKey($prefix, $key)) ? true : false;
    }

    /**
     * 设置有效期
     *
     * @param string $key
     * @param integer $expire
     * @param string $prefix
     * @return bool
     */
    public function expire($key , $expire = 3600, $prefix = ''){
        $cacheKey = $this->getCacheKey($prefix, $key);
        return $this->handler->expire($cacheKey, $expire);
    }

    /**
     * 返回句柄对象，可执行其它高级方法
     *
     * @access public
     * @return object
     */
    public function handler()
    {
        return $this->handler;
    }

    /**
     * 获取实际的缓存标识
     * @access public
     * @param string|array $key 缓存名
     * @return mixed
     */
    public function getCacheKey($prefix, $key)
    {
        if ($prefix === '') {
            return $key;
        }
        if (is_array($key)) {
            $keyArr = [];
            foreach ($key as $val) {
                $keyArr[] = $prefix . $val;
            }
            return $keyArr;
        } 
        return $prefix . $key;
    }
}
