<?php

namespace App\Core;

/**
 * Cache helper class providing Redis-based caching with automatic database fallback.
 */
class Cache
{
    private static ?\Redis $client = null;
    private static bool $connectionAttempted = false;

    /**
     * Get initialized Redis client or null if unavailable/disabled.
     */
    private static function getClient(): ?\Redis
    {
        if (self::$connectionAttempted) {
            return self::$client;
        }

        self::$connectionAttempted = true;

        if (!Env::get('REDIS_ENABLED', false)) {
            return null;
        }

        if (!class_exists('\Redis')) {
            error_log('[Cache Warning] phpredis extension is not installed.');
            return null;
        }

        try {
            $redis = new \Redis();
            $host = Env::get('REDIS_HOST', '127.0.0.1');
            $port = (int)Env::get('REDIS_PORT', 6379);
            $password = Env::get('REDIS_PASSWORD', null);
            $db = (int)Env::get('REDIS_DB', 0);

            // Connect with 1.0s timeout to prevent request blocking
            $connected = $redis->connect($host, $port, 1.0);
            if (!$connected) {
                error_log("[Cache Warning] Failed to connect to Redis at {$host}:{$port}");
                return null;
            }

            if ($password !== null && $password !== '' && strtolower($password) !== 'null') {
                $redis->auth($password);
            }

            if ($db > 0) {
                $redis->select($db);
            }

            self::$client = $redis;
        } catch (\Exception $e) {
            error_log('[Cache Error] Redis connection error: ' . $e->getMessage());
            self::$client = null;
        }

        return self::$client;
    }

    /**
     * Helper to get prefixed key
     */
    private static function getPrefixedKey(string $key): string
    {
        return 'nabeh:' . $key;
    }

    /**
     * Retrieve cache item by key.
     */
    public static function get(string $key)
    {
        $client = self::getClient();
        if (!$client) {
            return null;
        }

        try {
            $prefixedKey = self::getPrefixedKey($key);
            $value = $client->get($prefixedKey);
            return $value !== false ? json_decode($value, true) : null;
        } catch (\Exception $e) {
            error_log('[Cache Error] Redis get failed: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Set cache item by key with TTL.
     */
    public static function set(string $key, $value, int $ttl = 3600): bool
    {
        $client = self::getClient();
        if (!$client) {
            return false;
        }

        try {
            $prefixedKey = self::getPrefixedKey($key);
            return $client->setex($prefixedKey, $ttl, json_encode($value));
        } catch (\Exception $e) {
            error_log('[Cache Error] Redis set failed: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Delete cache item by key.
     */
    public static function delete(string $key): bool
    {
        $client = self::getClient();
        if (!$client) {
            return false;
        }

        try {
            $prefixedKey = self::getPrefixedKey($key);
            return $client->del($prefixedKey) > 0;
        } catch (\Exception $e) {
            error_log('[Cache Error] Redis delete failed: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Retrieve cache item, or run callback and store results if cache misses.
     */
    public static function remember(string $key, int $ttl, callable $callback)
    {
        $value = self::get($key);
        if ($value !== null) {
            return $value;
        }

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