<?php
namespace Concrete\Core\Permission\Response;

use Exception;
use Concrete\Core\User\User;
use Concrete\Core\Support\Facade\Application;
use Concrete\Core\Permission\Category as PermissionKeyCategory;
use Core;

class Response
{
    /** @var \Concrete\Core\Permission\ObjectInterface */
    protected $object;
    /** @var PermissionKeyCategory */
    protected $category;
    public static $cache = array();

    /**
     * Sets the current permission object to the object provided, this object should implement the Permission ObjectInterface.
     *
     * @param \Concrete\Core\Permission\ObjectInterface $object
     */
    public function setPermissionObject($object)
    {
        $this->object = $object;
    }

    /**
     * Retrieves the current permission object.
     */
    public function getPermissionObject()
    {
        return $this->object;
    }

    /**
     * Sets the current Permission Category object to an appropriate PermissionKeyCategory.
     *
     * @param PermissionKeyCategory $category
     */
    public function setPermissionCategoryObject($category)
    {
        $this->category = $category;
    }

    /**
     * Returns an error constant if an error is present, false if there are no errors.
     *
     * @return bool|int
     */
    public function testForErrors()
    {
        return false;
    }

    /**
     * Passing in any object that implements the ObjectInterface, retrieve the Permission Response object.
     *
     * @param \Concrete\Core\Permission\ObjectInterface $object
     *
     * @return \Concrete\Core\Permission\Response\Response
     */
    public static function getResponse($object)
    {
        $cache = Core::make('cache/request');
        $identifier = sprintf('permission/response/%s/%s', get_class($object), $object->getPermissionObjectIdentifier());
        $item = $cache->getItem($identifier);
        if (!$item->isMiss()) {
            return $item->get();
        }

        $className = $object->getPermissionResponseClassName();
        /** @var \Concrete\Core\Permission\Response\Response $pr */
        $pr = Core::make($className);
        if ($object->getPermissionObjectKeyCategoryHandle()) {
            $category = PermissionKeyCategory::getByHandle($object->getPermissionObjectKeyCategoryHandle());
            $pr->setPermissionCategoryObject($category);
        }
        $pr->setPermissionObject($object);
        $cache->save($item->set($pr));

        return $pr;
    }

    /**
     * This function returns true if the user has permission to the object, or false if they do not have access.
     *
     * @param string $permissionHandle A Permission Key Handle
     * @param array $args Arguments to pass to the PermissionKey object's validate function
     *
     * @return bool
     *
     * @throws Exception
     */
    public function validate($permissionHandle, $args = array())
    {
        $app = Application::getFacadeApplication();
        $u = $app->make(User::class);
        if ($u->isSuperUser()) {
            return true;
        }
        if (!is_object($this->category)) {
            throw new Exception(t('Unable to get category for permission %s', $permissionHandle));
        }
        $pk = $this->category->getPermissionKeyByHandle($permissionHandle);
        if (!$pk) {
            throw new Exception(t('Unable to get permission key for %s', $permissionHandle));
        }
        $pk->setPermissionObject($this->object);

        return call_user_func_array(array($pk, 'validate'), $args);
    }

    public function __call($f, $a)
    {
        $permission = substr($f, 3);
        /** @var \Concrete\Core\Utility\Service\Text $textHelper */
        $textHelper = Core::make('helper/text');
        $permission = $textHelper->uncamelcase($permission);

        return $this->validate($permission, $a);
    }
}
