<?php
namespace {
    if (!defined('WD')) {
        define('WD', dirname(__DIR__, 2));
    }
    $_SERVER['REQUEST_URI'] = '/test';
}

// Setup mock environment
namespace classes {
    class authentication {
        public static $user = false;
        public static $subuser = false;
        public function get_user() { return self::$user; }
        public function get_subuser() { return self::$subuser; }
    }
    class response {
        public function error($msg, $code) { throw new \Exception("Response Error ($code): $msg"); }
        public function forbidden(array $permissions) { throw new \Exception("Response Error (403): Missing permission(s): " . implode(', ', $permissions)); }
        public function add_meta($k, $v) {}
    }
    class permission_node {
        public $permission;
        public $subusers_node_key;
        public static function create($p, $k = null) {
            $n = new self();
            $n->permission = $p;
            $n->subusers_node_key = $k;
            return $n;
        }
    }
}

namespace objects {
    class logs_o {
        public function add($a, $b, $c, $d, $e, $f) {}
    }
    class users_o {
        public $id = 1;
        public $group_id;
        public function __construct() {
            $this->group_id = new class { public function value() { return 2; } };
        }
        public function hasPermission($p) {
            echo "[DB] Checking permission: $p\n";
            return $p === 'allowed_perm';
        }
    }
    class subusers_o {
        public $id = 10;
        public function hasPermission($node) {
            echo "[DB] Checking subuser node: " . $node->name . "\n";
            return $node->name === 'ALLOWED_NODE';
        }
    }
}

namespace {
    // Include Redis classes first so we can mock REDIS_CONFIG
    global $REDIS_CONFIG;
    $REDIS_CONFIG = [
        'host' => getenv('REDIS_CONFIG_HOST') ?: 'redis',
        'user' => getenv('REDIS_CONFIG_USER') ?: 'default',
        'database' => (int)(getenv('REDIS_CONFIG_DATABASE') ?: 0),
        'password' => getenv('REDIS_CONFIG_PASSWORD') ?: '',
        'port' => (int)(getenv('REDIS_CONFIG_PORT') ?: 6379),
    ];

    require_once WD . '/vendor/autoload.php';
    require_once WD . '/interfaces/redis_i.php';
    require_once WD . '/traits/redis_t.php';
    require_once WD . '/classes/redis.php';
    require_once WD . '/traits/route_t.php';

    // Set the redis constant
    global $response;
    $response = new classes\response();

    try {
        $redis_instance = (new classes\redis())->connect();
        if (!defined('redis')) {
            define("redis", $redis_instance);
        }
    } catch (Exception $e) {
        die("Could not connect to Redis: " . $e->getMessage());
    }

    use traits\route_t;
    use classes\authentication;
    use classes\permission_node;
    use objects\users_o;
    use objects\subusers_o;

    class TestRoute {
        use route_t;
        public function testEval($p, $c = null, $t = false) {
            return $this->evaluatePermission($p, $c, $t);
        }
        // Mocking resolveCustomerNumberForSubuser as it's private and uses headers
        private function resolveCustomerNumberForSubuser($auth, ?int $customer_number = null): ?int {
            return $customer_number ?? 999;
        }
    }

    $route = new TestRoute();

    function assertResult($actual, $expected, $msg) {
        if ($actual === $expected) {
            echo "\033[32m✔ $msg\033[0m\n";
        } else {
            echo "\033[31m✖ $msg (Expected " . var_export($expected, true) . ", got " . var_export($actual, true) . ")\033[0m\n";
            exit(1);
        }
    }

    // 1. Test classic user permission caching
    echo "--- Testing Classic User Permission Caching ---\n";
    $user = new users_o();
    authentication::$user = $user;
    
    // Clear previous cache if any
    redis->clear_permission("user:1:allowed_perm");
    redis->clear_permission("user:1:denied_perm");

    echo "First call (should hit DB):\n";
    assertResult($route->testEval('allowed_perm'), true, "User should have allowed_perm");
    
    echo "Second call (should hit memory cache, no DB output expected):\n";
    assertResult($route->testEval('allowed_perm'), true, "User should still have allowed_perm (from memory)");

    // Clear memory cache by creating new route object
    $route = new TestRoute();
    echo "Third call after new object (should hit Redis, no DB output expected):\n";
    assertResult($route->testEval('allowed_perm'), true, "User should still have allowed_perm (from Redis)");

    // Test denied perm
    echo "\nTesting denied perm:\n";
    assertResult($route->testEval('denied_perm'), false, "User should NOT have denied_perm");
    $route = new TestRoute();
    echo "Call after new object (should hit Redis, no DB output expected):\n";
    assertResult($route->testEval('denied_perm'), false, "User should still NOT have denied_perm (from Redis)");

    // 2. Test subuser node caching
    echo "\n--- Testing Subuser Node Caching ---\n";
    $subuser = new subusers_o();
    authentication::$subuser = $subuser;
    
    $allowedNode = new stdClass(); $allowedNode->name = 'ALLOWED_NODE';
    $deniedNode = new stdClass(); $deniedNode->name = 'DENIED_NODE';
    
    $allowedPermNode = permission_node::create('allowed_node_perm', $allowedNode);
    $deniedPermNode = permission_node::create('denied_node_perm', $deniedNode);

    redis->clear_permission("subuser:10:999:ALLOWED_NODE");
    redis->clear_permission("subuser:10:999:DENIED_NODE");

    echo "First call (should hit DB):\n";
    assertResult($route->testEval($allowedPermNode), true, "Subuser should have ALLOWED_NODE");

    $route = new TestRoute();
    echo "Second call after new object (should hit Redis, no DB output expected):\n";
    assertResult($route->testEval($allowedPermNode), true, "Subuser should still have ALLOWED_NODE (from Redis)");

    echo "\nTesting denied node:\n";
    assertResult($route->testEval($deniedPermNode), false, "Subuser should NOT have DENIED_NODE");
    $route = new TestRoute();
    echo "Call after new object (should hit Redis, no DB output expected):\n";
    assertResult($route->testEval($deniedPermNode), false, "Subuser should still NOT have DENIED_NODE (from Redis)");

    echo "\nPermissionRedisCacheTest completed successfully.\n";
}
