<?php

namespace xmvc\libCache;

use xmvc\Cache;

/**
* 缓存 - APCu
*/
class CacheApcu implements InterfaceCache
{
    protected string $guid = '';
    protected string $groupFile = '';
    protected array $groupData = [];
    protected bool $isDataChanged = false;

    public function __construct(string $groupName = 'default', array $options = [])
    {
        $this->guid = Cache::groupNameGUID($groupName);
        $this->groupFile = DIR_CACHE . '/keyValue/' . $this->guid . '.php';
        if (apcu_exists($this->guid)) {
            $this->groupData = apcu_fetch($this->guid);
        } else {
            $this->loadFromFile($this->groupFile);
        }
    }

    protected function error(string $message)
    {
        error($message, null, [__FILE__]);
    }

    /**
     * 加载文件到内存
     *
     * @return self
     */
    public function loadFromFile(string $filename = ''): self
    {
        if ($filename == '') {
            $filename = $this->groupFile;
        }
        if (is_file($this->groupFile)) {
            //如果存在文件,则取文件初始化, 例 viewtimes 
            // - 用 Cache::create('file', 'viewtimes') 写
            // - 用 Cache::create('apcu', 'viewtimes') 读
            $this->groupData = require($filename);
            $this->isDataChanged = true;
            $this->save();
        }
        return $this;
    }

    /**
     * 保存缓存到文件
     *
     * @return self
     */
    public function saveToFile(string $filename = ''): self
    {
        if ($filename == '') {
            $filename = $this->groupFile;
        }
        arrayToFile($filename, $this->groupData);
        return $this;
    }

    protected function validKey(string $key): self
    {
        if (is_null($key) || trim($key) == '') {
            $this->error('不能传入一个空值的key');
        }
        return $this;
    }

    /**
     * 组名是否存在
     */
    public static function groupExists(string $groupName = 'default'): bool
    {
        $guid = Cache::groupNameGUID($groupName);
        return apcu_exists($guid);
    }

    public function exists(string $key): bool
    {
        $this->validKey($key);
        return isset($this->groupData[$key]);
    }

    public function get(string $key): mixed
    {
        $this->validKey($key);
        $hashKey = '';
        if (strpos($key, '.') > 0) {
            list($key, $hashKey) = explode('.', $key);
        }
        if (isset($this->groupData[$key])) {
            if ($hashKey) {
                if (strpos($hashKey, '-') !== false) {
                    list($start, $end) = explode('-', $hashKey);
                    $start = $start ? (int)$start : 0;
                    $end = $end ? (int) $end - $start : count($this->groupData[$key][0]) - $start;
                    return array_slice($this->groupData[$key][0], $start, $end + 1);
                } elseif (isset($this->groupData[$key][0][$hashKey])) {
                    return $this->groupData[$key][0][$hashKey];
                } elseif ($hashKey === 'count') {
                    return count($this->groupData[$key][0]);
                } else {
                    return null;
                }
            } else {
                return $this->groupData[$key][0];
            }
        } else {
            return null;
        }
    }

    public function getTime(string $key): int
    {
        $hashKey = '';
        if (strpos($key, '.') > 0) {
            list($key, $hashKey) = explode('.', $key);
        }
        $this->validKey($key);
        if (isset($this->groupData[$key])) {
            return $this->groupData[$key][1];
        } else {
            return 0;
        }
    }

    public function getBoth(string $key): array
    {
        $this->validKey($key);
        if ($this->exists($key)) {
            return [$this->get($key), $this->getTime($key)];
        } else {
            return [null, 0];
        }
    }

    public function set(string $key, $data): self
    {
        $this->validKey($key);
        $hashKey = '';
        $time = time();
        if (strpos($key, '.') > 0) {
            list($key, $hashKey) = explode('.', $key);
            $this->groupData[$key][0][$hashKey] = $data;
            $this->groupData[$key][1] = $time;
        } else {
            $this->groupData[$key] = [$data, $time];
        }
        $this->isDataChanged = true;
        return $this;
    }


    public function getAll(): array
    {
        return $this->groupData;
    }

    /**
     * 删除指定 key 的缓存单元
     *
     * @param string $key 键名
     */
    public function remove(string $key): self
    {
        $this->validKey($key);
        $hashKey = '';
        if (strpos($key, '.') > 0) {
            list($key, $hashKey) = explode('.', $key);
        }
        if (isset($this->groupData[$key])) {
            if ($hashKey == '') {
                unset($this->groupData[$key]);
            } elseif (isset($this->groupData[$key][0][$hashKey])) {
                unset($this->groupData[$key][0][$hashKey]);
            }
        }
        $this->isDataChanged = true;
        return $this;
    }

    public function clear(): self
    {
        $this->isDataChanged = false;
        $this->groupData = [];
        apcu_delete($this->guid);
        return $this;
    }

    public function save(): self
    {
        if ($this->isDataChanged) {
            apcu_store($this->guid, $this->groupData, 0);
            $this->isDataChanged = false;
        }
        return $this;
    }
}
