<?php

declare(strict_types=1);

namespace SHServ\Integrations\GAuth;

final class PermissionResolver
{
    /** @var array<string, array<int, string>> */
    private static array $cache = [];

    /**
     * Resolve effective permissions for a user.
     * Algorithm: union(role_defaults, group_permissions, user_grants) minus explicit_denies.
     * Results are cached for the lifetime of the current request.
     */
    public function resolve(int $userId, string $systemRole): array
    {
        $cacheKey = "{$userId}:{$systemRole}";
        if (isset(self::$cache[$cacheKey])) {
            return self::$cache[$cacheKey];
        }

        // Fast path: superadmin
        if ($systemRole === 'superadmin') {
            $result = $this->getAllPermissionSlugs();
            self::$cache[$cacheKey] = $result;
            return $result;
        }

        $effective = [];

        // 1. Role defaults
        $rolePerms = $this->getRolePermissions($systemRole);
        foreach ($rolePerms as $slug => $granted) {
            if ($granted) {
                $effective[$slug] = true;
            }
        }

        // 2. Group permissions (union)
        $groupPerms = $this->getGroupPermissions($userId);
        foreach ($groupPerms as $slug => $granted) {
            if ($granted) {
                $effective[$slug] = true;
            }
        }

        // 3. User-level overrides
        $userOverrides = $this->getUserOverrides($userId);
        foreach ($userOverrides as $slug => $granted) {
            if ($granted) {
                $effective[$slug] = true;
            } else {
                unset($effective[$slug]);
            }
        }

        $result = array_keys($effective);
        self::$cache[$cacheKey] = $result;
        return $result;
    }

    /**
     * Check if user has a specific permission.
     */
    public function has(int $userId, string $systemRole, string $permissionSlug): bool
    {
        if ($systemRole === 'superadmin') {
            return true;
        }
        $perms = $this->resolve($userId, $systemRole);
        return in_array($permissionSlug, $perms, true);
    }

    private function getRolePermissions(string $roleSlug): array
    {
        $tb = app()->thin_builder;
        $result = $tb->select('shserv_roles', ['default_permissions'], [['slug', '=', $roleSlug]]);
        if (!$result) {
            return $roleSlug === 'superadmin' ? ['*' => true] : [];
        }

        $perms = json_decode($result[0]['default_permissions'] ?? '[]', true);
        $map = [];
        foreach ($perms as $slug) {
            $map[$slug] = true;
        }

        // Handle wildcard '*' for superadmin
        if (in_array('*', $perms, true)) {
            try {
                $all = $tb->select('shserv_permissions', ['slug']);
                if ($all) {
                    foreach ($all as $row) {
                        $map[$row['slug']] = true;
                    }
                }
            } catch (\Throwable $e) {
                $map['*'] = true;
            }
        }

        return $map;
    }

    private function getGroupPermissions(int $userId): array
    {
        $tb = app()->thin_builder;
        try {
            $result = $tb->query("
                SELECT p.permission_slug, p.granted
                FROM shserv_group_permissions p
                JOIN shserv_group_members m ON m.group_id = p.group_id
                WHERE m.user_id = {$userId}
            ");
        } catch (\Throwable $e) {
            return [];
        }

        if (!$result) {
            return [];
        }

        $map = [];
        foreach ($result as $row) {
            $map[$row['permission_slug']] = (bool) $row['granted'];
        }
        return $map;
    }

    private function getUserOverrides(int $userId): array
    {
        $tb = app()->thin_builder;
        $result = $tb->select('shserv_user_permissions', ['permission_slug', 'granted'], [['user_id', '=', $userId]]);
        if (!$result) {
            return [];
        }

        $map = [];
        foreach ($result as $row) {
            $map[$row['permission_slug']] = (bool) $row['granted'];
        }
        return $map;
    }

    private function getAllPermissionSlugs(): array
    {
        $tb = app()->thin_builder;
        try {
            $result = $tb->select('shserv_permissions', ['slug']);
        } catch (\Throwable $e) {
            return ['*'];
        }
        if (!$result) {
            return ['*'];
        }
        return array_column($result, 'slug');
    }
}
