<?php

namespace App\Services\Auth;

use App\Entities\User;
use Config\Database;

/**
 * Resolves whether a user has a given permission slug, combining:
 *   1. Role defaults (role_permissions)
 *   2. Per-user overrides (user_permission_overrides — grant/revoke)
 *
 * This is called both from RoleGuard (route layer) AND directly from
 * Service classes before any sensitive write — see 04-roles-permissions-matrix.md §4.
 * Results are cached per-request (static array) since a single request may
 * check several permissions across its Service calls.
 */
class PermissionService
{
    private static array $requestCache = [];

    public function can(User $user, string $permissionSlug): bool
    {
        if ($user->isSuperAdmin()) {
            return true; // Super Admin implicitly has all permissions
        }

        $cacheKey = "{$user->id}:{$permissionSlug}";
        if (isset(self::$requestCache[$cacheKey])) {
            return self::$requestCache[$cacheKey];
        }

        $db = Database::connect();

        // Explicit revoke always wins over role default.
        $override = $db->table('user_permission_overrides uo')
            ->select('uo.effect')
            ->join('permissions p', 'p.id = uo.permission_id')
            ->where('uo.user_id', $user->id)
            ->where('p.slug', $permissionSlug)
            ->get()->getRow();

        if ($override) {
            $result = $override->effect === 'grant';
            self::$requestCache[$cacheKey] = $result;
            return $result;
        }

        $rolePermission = $db->table('role_permissions rp')
            ->join('permissions p', 'p.id = rp.permission_id')
            ->where('rp.role_id', $user->role_id)
            ->where('p.slug', $permissionSlug)
            ->get()->getRow();

        $result = $rolePermission !== null;
        self::$requestCache[$cacheKey] = $result;
        return $result;
    }

    /**
     * Throws-style guard for use inside Service methods:
     *   $this->permissions->authorize($user, 'jobs.create');
     */
    public function authorize(User $user, string $permissionSlug): void
    {
        if (! $this->can($user, $permissionSlug)) {
            throw new \App\Exceptions\ForbiddenException(
                "User {$user->id} lacks permission: {$permissionSlug}"
            );
        }
    }
}
