<?php

class Rbac_model extends Cf_Model
{
    public const USER_PERMISSIONS_URL = '/rbac/user-permissions';
    public const ROLE_PERMISSIONS_URL = '/role/%s/permissions';
    public const PERMISSION_LIST_URL = '/rbac';
    public const USER_PERMISSIONS_CACHE_TTL = 60; // 60 seconds
    public const USER_PERMISSIONS_CACHE_PREFIX = 'rbac_user_permissions_';

    private $permissions = false;

    public function __construct()
    {
        parent::__construct();
    }

    public function getUserPermissions()
    {
        // per-request memoization
        if (is_array($this->permissions) && !empty($this->permissions)) {
            return $this->permissions;
        }

        $cacheKey = $this->getUserPermissionsCacheKey();

        // cross-request cache
        if ($cacheKey !== null) {
            $cached = $this->cache->get($cacheKey);
            if (is_array($cached) && !empty($cached)) {
                $this->permissions = $cached;
                return $this->permissions;
            }
        }

        try {
            $response = $this->getRestClient()->get(self::USER_PERMISSIONS_URL);
            $permissions = json_decode($response, JSON_OBJECT_AS_ARRAY);

            // only cache non-empty results
            if (is_array($permissions) && !empty($permissions)) {
                $this->permissions = $permissions;
                if ($cacheKey !== null) {
                    $this->cache->save($cacheKey, $permissions, self::USER_PERMISSIONS_CACHE_TTL);
                }
                return $permissions;
            }

            return is_array($permissions) ? $permissions : [];
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    private function getUserPermissionsCacheKey()
    {
        $username = $this->session->userdata('username');
        return $username ? self::USER_PERMISSIONS_CACHE_PREFIX . $username : null;
    }

    public function getPermissionList()
    {
        try {
            $permissions = json_decode($this->getRestClient()->get(self::PERMISSION_LIST_URL), JSON_OBJECT_AS_ARRAY);
            $result = [];

            // group by application and group
            foreach ($permissions as $permission) {
                $result[$permission['application']][$permission['group']][] = $permission;
            }

            return $result;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    public function getRolePermissions($role)
    {
        try {
            $url = sprintf(self::ROLE_PERMISSIONS_URL, $role);
            $permissions = $this->getRestClient()->get($url);
            return json_decode($permissions, JSON_OBJECT_AS_ARRAY);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    public function assignPermissions($role, array $permissions)
    {
        try {
            $url = sprintf(self::ROLE_PERMISSIONS_URL, $role);
            $this->getRestClient()->put($url, $permissions);
            $this->cache->clean();
            return true;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    public function revokePermissions($role, array $permissions)
    {
        try {
            $url = sprintf(self::ROLE_PERMISSIONS_URL, $role);
            $this->getRestClient()->delete($url, $permissions);
            $this->cache->clean();
            return true;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }
}
