<?php
/**
 * Hybrid Cache System - Redis with File Fallback
 * Optimized for enterprise scale (50+ concurrent users)
 */
class Cache {
    private static string $cacheDir;
    private static int $defaultTTL = 3600; // 1 hour default
    private static bool $useRedis = false;

    public static function init(): void {
        // Try to use Redis if available
        if (class_exists('RedisCache') && RedisCache::isEnabled()) {
            self::$useRedis = true;
            return;
        }

        // Fall back to file-based cache
        self::$cacheDir = defined('CACHE_PATH') ? CACHE_PATH : (APP_ROOT . '/storage/cache');
        if (!is_dir(self::$cacheDir)) {
            mkdir(self::$cacheDir, 0755, true);
        }
    }

    /**
     * Get cached value
     */
    public static function get(string $key, mixed $default = null): mixed {
        self::init();

        if (self::$useRedis) {
            return RedisCache::get($key, $default);
        }

        return self::getFromFile($key, $default);
    }

    /**
     * Set cached value
     */
    public static function set(string $key, mixed $value, int $ttl = null): bool {
        self::init();

        if (self::$useRedis) {
            return RedisCache::set($key, $value, $ttl);
        }

        return self::setToFile($key, $value, $ttl);
    }

    /**
     * Delete cached value
     */
    public static function delete(string $key): bool {
        self::init();

        if (self::$useRedis) {
            return RedisCache::delete($key);
        }

        $file = self::getCacheFile($key);
        if (file_exists($file)) {
            return @unlink($file);
        }
        return true;
    }

    /**
     * Clear all cache or by pattern
     */
    public static function clear(string $pattern = null): bool {
        self::init();

        if (self::$useRedis) {
            return RedisCache::clear($pattern);
        }

        if ($pattern === null) {
            $files = glob(self::$cacheDir . '/*.cache');
            foreach ($files as $file) {
                @unlink($file);
            }
            return true;
        }

        $files = glob(self::$cacheDir . '/' . md5($pattern) . '*.cache');
        foreach ($files as $file) {
            @unlink($file);
        }

        return true;
    }

    /**
     * Get or set cache (memoization pattern)
     */
    public static function remember(string $key, callable $callback, int $ttl = null): mixed {
        $value = self::get($key);

        if ($value !== null) {
            return $value;
        }

        $value = $callback();
        self::set($key, $value, $ttl);
        return $value;
    }

    /**
     * Clean expired cache files
     */
    public static function cleanExpired(): int {
        self::init();

        if (self::$useRedis) {
            return RedisCache::cleanExpired();
        }

        $count = 0;
        $files = glob(self::$cacheDir . '/*.cache');

        foreach ($files as $file) {
            $data = unserialize(file_get_contents($file));
            if ($data['expires'] < time()) {
                @unlink($file);
                $count++;
            }
        }

        return $count;
    }

    private static function getFromFile(string $key, mixed $default = null): mixed {
        $file = self::getCacheFile($key);

        if (!file_exists($file)) {
            return $default;
        }

        $data = unserialize(file_get_contents($file));

        // Check if expired
        if ($data['expires'] < time()) {
            @unlink($file);
            return $default;
        }

        return $data['value'];
    }

    private static function setToFile(string $key, mixed $value, int $ttl = null): bool {
        $ttl = $ttl ?? self::$defaultTTL;
        $file = self::getCacheFile($key);

        $data = [
            'value' => $value,
            'expires' => time() + $ttl,
            'created' => time()
        ];

        return file_put_contents($file, serialize($data), LOCK_EX) !== false;
    }

    private static function getCacheFile(string $key): string {
        $hash = md5($key);
        return self::$cacheDir . '/' . $hash . '.cache';
    }

    /**
     * Get cache statistics
     */
    public static function getStats(): array {
        self::init();

        if (self::$useRedis) {
            return RedisCache::getStats();
        }

        $files = glob(self::$cacheDir . '/*.cache');
        $totalSize = 0;
        $expiredCount = 0;
        $activeCount = 0;

        foreach ($files as $file) {
            $totalSize += filesize($file);
            $data = unserialize(file_get_contents($file));
            if ($data['expires'] < time()) {
                $expiredCount++;
            } else {
                $activeCount++;
            }
        }

        return [
            'type' => 'file',
            'total_files' => count($files),
            'active_files' => $activeCount,
            'expired_files' => $expiredCount,
            'total_size' => round($totalSize / 1024 / 1024, 2) . ' MB',
            'cache_dir' => self::$cacheDir
        ];
    }

    /**
     * Check if Redis is being used
     */
    public static function usingRedis(): bool {
        self::init();
        return self::$useRedis;
    }
}
