<?php
/**
 * HuSNS - 缓存管理类
 * 
 * 支持文件缓存和Redis缓存
 * 
 * @author  HYR
 * @QQ      281900864
 * @website https://huyourui.com
 * @license MIT
 */

class Cache
{
    /**
     * 缓存驱动类型
     */
    const DRIVER_FILE = 'file';
    const DRIVER_REDIS = 'redis';

    /**
     * 当前使用的驱动
     */
    private static $driver = self::DRIVER_FILE;

    /**
     * Redis 连接实例
     */
    private static $redis = null;

    /**
     * 缓存配置
     */
    private static $config = [
        'driver' => self::DRIVER_FILE,
        'path' => null,        // 文件缓存目录
        'prefix' => 'husns_',  // 缓存键前缀
        'expire' => 3600,      // 默认过期时间（秒）
        // Redis配置
        'redis' => [
            'host' => '127.0.0.1',
            'port' => 6379,
            'password' => null,
            'database' => 0,
            'timeout' => 0,
        ],
    ];

    /**
     * 缓存目录
     */
    private static $cacheDir = null;

    /**
     * 初始化缓存
     */
    public static function init(array $config = []): void
    {
        self::$config = array_merge(self::$config, $config);
        self::$driver = self::$config['driver'];
        
        // 设置缓存目录
        if (self::$config['path']) {
            self::$cacheDir = rtrim(self::$config['path'], '/') . '/';
        } else {
            self::$cacheDir = defined('ROOT_PATH') ? ROOT_PATH . 'cache/' : __DIR__ . '/../cache/';
        }
        
        // 确保缓存目录存在
        if (!is_dir(self::$cacheDir)) {
            mkdir(self::$cacheDir, 0755, true);
        }
        
        // 初始化Redis连接
        if (self::$driver === self::DRIVER_REDIS) {
            self::initRedis();
        }
    }

    /**
     * 初始化Redis连接
     */
    private static function initRedis(): void
    {
        if (self::$redis !== null) {
            return;
        }
        
        if (!class_exists('Redis')) {
            throw new Exception('Redis extension is not installed');
        }
        
        $redisConfig = self::$config['redis'];
        
        self::$redis = new Redis();
        
        try {
            self::$redis->connect(
                $redisConfig['host'],
                $redisConfig['port'],
                $redisConfig['timeout']
            );
            
            if (!empty($redisConfig['password'])) {
                self::$redis->auth($redisConfig['password']);
            }
            
            if ($redisConfig['database'] > 0) {
                self::$redis->select($redisConfig['database']);
            }
        } catch (Exception $e) {
            throw new Exception('Redis connection failed: ' . $e->getMessage());
        }
    }

    /**
     * 生成缓存键
     */
    private static function buildKey(string $key): string
    {
        return self::$config['prefix'] . $key;
    }

    /**
     * 获取缓存
     * 
     * @param string $key 缓存键
     * @param mixed $default 默认值（当缓存不存在时返回）
     * @return mixed
     */
    public static function get(string $key, $default = null)
    {
        if (self::$driver === self::DRIVER_REDIS) {
            return self::getFromRedis($key, $default);
        }
        return self::getFromFile($key, $default);
    }

    /**
     * 设置缓存
     * 
     * @param string $key 缓存键
     * @param mixed $value 缓存值
     * @param int|null $expire 过期时间（秒），null使用默认值
     * @return bool
     */
    public static function set(string $key, $value, ?int $expire = null): bool
    {
        $expire = $expire ?? self::$config['expire'];
        
        if (self::$driver === self::DRIVER_REDIS) {
            return self::setToRedis($key, $value, $expire);
        }
        return self::setToFile($key, $value, $expire);
    }

    /**
     * 删除缓存
     */
    public static function delete(string $key): bool
    {
        if (self::$driver === self::DRIVER_REDIS) {
            return self::$redis->del(self::buildKey($key)) > 0;
        }
        
        $file = self::$cacheDir . md5(self::buildKey($key)) . '.cache';
        if (file_exists($file)) {
            return unlink($file);
        }
        return true;
    }

    /**
     * 检查缓存是否存在
     */
    public static function has(string $key): bool
    {
        if (self::$driver === self::DRIVER_REDIS) {
            return self::$redis->exists(self::buildKey($key)) > 0;
        }
        
        $file = self::$cacheDir . md5(self::buildKey($key)) . '.cache';
        if (!file_exists($file)) {
            return false;
        }
        
        // 检查是否过期
        $data = @unserialize(file_get_contents($file));
        if (!$data || !isset($data['expire'])) {
            return false;
        }
        
        if ($data['expire'] > 0 && $data['expire'] < time()) {
            @unlink($file);
            return false;
        }
        
        return true;
    }

    /**
     * 清空所有缓存
     */
    public static function clear(): bool
    {
        if (self::$driver === self::DRIVER_REDIS) {
            $keys = self::$redis->keys(self::$config['prefix'] . '*');
            if (!empty($keys)) {
                return self::$redis->del($keys) > 0;
            }
            return true;
        }
        
        $files = glob(self::$cacheDir . '*.cache');
        $result = true;
        
        foreach ($files as $file) {
            if (!@unlink($file)) {
                $result = false;
            }
        }
        
        return $result;
    }

    /**
     * 递增
     */
    public static function increment(string $key, int $step = 1): int
    {
        if (self::$driver === self::DRIVER_REDIS) {
            return self::$redis->incrby(self::buildKey($key), $step);
        }
        
        $value = self::get($key, 0);
        $value += $step;
        self::set($key, $value);
        return $value;
    }

    /**
     * 递减
     */
    public static function decrement(string $key, int $step = 1): int
    {
        return self::increment($key, -$step);
    }

    /**
     * 记住 - 如果缓存不存在则执行回调并缓存结果
     * 
     * @param string $key 缓存键
     * @param callable $callback 回调函数
     * @param int|null $expire 过期时间
     * @return mixed
     */
    public static function remember(string $key, callable $callback, ?int $expire = null)
    {
        if (self::has($key)) {
            return self::get($key);
        }
        
        $value = $callback();
        self::set($key, $value, $expire);
        return $value;
    }

    /**
     * 获取多個缓存
     * 
     * @param array $keys 缓存键数组
     * @param mixed $default 默认值
     * @return array
     */
    public static function getMultiple(array $keys, $default = null): array
    {
        $result = [];
        foreach ($keys as $key) {
            $result[$key] = self::get($key, $default);
        }
        return $result;
    }

    /**
     * 设置多个缓存
     * 
     * @param array $values 键值对数组
     * @param int|null $expire 过期时间
     * @return bool
     */
    public static function setMultiple(array $values, ?int $expire = null): bool
    {
        foreach ($values as $key => $value) {
            self::set($key, $value, $expire);
        }
        return true;
    }

    /**
     * 删除多个缓存
     * 
     * @param array $keys 缓存键数组
     * @return bool
     */
    public static function deleteMultiple(array $keys): bool
    {
        foreach ($keys as $key) {
            self::delete($key);
        }
        return true;
    }

    // ==================== 文件缓存实现 ====================

    private static function getFromFile(string $key, $default)
    {
        $file = self::$cacheDir . md5(self::buildKey($key)) . '.cache';
        
        if (!file_exists($file)) {
            return $default;
        }
        
        $data = @unserialize(file_get_contents($file));
        
        if (!$data) {
            return $default;
        }
        
        // 检查是否过期
        if (isset($data['expire']) && $data['expire'] > 0 && $data['expire'] < time()) {
            @unlink($file);
            return $default;
        }
        
        return $data['value'];
    }

    private static function setToFile(string $key, $value, int $expire): bool
    {
        $file = self::$cacheDir . md5(self::buildKey($key)) . '.cache';
        
        $data = [
            'value' => $value,
            'expire' => $expire > 0 ? time() + $expire : 0,
            'create_time' => time(),
        ];
        
        return file_put_contents($file, serialize($data), LOCK_EX) !== false;
    }

    // ==================== Redis缓存实现 ====================

    private static function getFromRedis(string $key, $default)
    {
        $value = self::$redis->get(self::buildKey($key));
        
        if ($value === false) {
            return $default;
        }
        
        $decoded = @unserialize($value);
        return $decoded !== false ? $decoded : $value;
    }

    private static function setToRedis(string $key, $value, int $expire): bool
    {
        $key = self::buildKey($key);
        
        if (is_array($value) || is_object($value)) {
            $value = serialize($value);
        }
        
        if ($expire > 0) {
            return self::$redis->setex($key, $expire, $value);
        }
        
        return self::$redis->set($key, $value);
    }

    /**
     * 获取缓存统计信息
     */
    public static function stats(): array
    {
        if (self::$driver === self::DRIVER_REDIS) {
            return [
                'driver' => 'redis',
                'info' => self::$redis->info(),
            ];
        }
        
        $files = glob(self::$cacheDir . '*.cache');
        $totalSize = 0;
        $expired = 0;
        $valid = 0;
        
        foreach ($files as $file) {
            $totalSize += filesize($file);
            $data = @unserialize(file_get_contents($file));
            
            if (!$data || !isset($data['expire'])) {
                $expired++;
                continue;
            }
            
            if ($data['expire'] > 0 && $data['expire'] < time()) {
                $expired++;
            } else {
                $valid++;
            }
        }
        
        return [
            'driver' => 'file',
            'total_files' => count($files),
            'valid' => $valid,
            'expired' => $expired,
            'total_size' => $totalSize,
            'total_size_formatted' => self::formatBytes($totalSize),
        ];
    }

    /**
     * 格式化字节大小
     */
    private static function formatBytes(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $i = 0;
        
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }
        
        return round($bytes, 2) . ' ' . $units[$i];
    }

    /**
     * 获取当前驱动类型
     */
    public static function getDriver(): string
    {
        return self::$driver;
    }
}
