<?php

namespace WordpressModelsTests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\ExpressionLanguage\SyntaxError;
use WordpressModels\Rest\Permissions\PermissionExpressionLanguage;
use WordpressModels\Rest\Permissions\PermissionFunctionsInterface;


/**
 * Unit tests for syntax and result tests for PermissionFunctions and ExpressionLanguage
 */
class PermissionExpressionTest extends TestCase
{

    final public const TEST_IS_AUTHORIZED_RESULT = true;

    private \WordpressModels\Rest\Permissions\PermissionFunctionsInterface $permissions;

    protected function setUp(): void
    {
        $this->permissions = new class implements PermissionFunctionsInterface {

            static function isAuthorized(): bool
            {
                return PermissionExpressionTest::TEST_IS_AUTHORIZED_RESULT;
            }

            static function userHasRole(string $role, ?int $userId = null): bool
            {
                return $role === 'test_role';
            }

            static function userHasCapability(string $capability, ?int $userId = null): bool
            {
                return $capability === 'test_capability';
            }
        };
    }

    /**
     * Use a valid capability with the `capability` expression function (CapabilityExpressionFunctionProvider).
     *
     * Expect true result.
     *
     * @return void
     */
    public function testCapabilityIsTrue()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $result = $lang->evaluate("capability(c)", ['c' => 'test_capability']);
        self::assertTrue($result);
    }

    /**
     * Use an invalid capability with the `capability` expression function (CapabilityExpressionFunctionProvider).
     *
     * Expect False result.
     *
     * @return void
     */
    public function testCapabilityIsFalse()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $result = $lang->evaluate("capability(c)", ['c' => 'not_a_valid_capability']);
        self::assertFalse($result);
    }


    /**
     * Use a valid role with the `role` expression function (RoleExpressionFunctionProvider).
     *
     * Expect true result.
     *
     * @return void
     */
    public function testRoleIsTrue()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $result = $lang->evaluate("role(r)", ['r' => 'test_role']);
        self::assertTrue($result);
    }

    /**
     * Use an invalid role with the `role` expression function (RoleExpressionFunctionProvider).
     *
     * Expect False result.
     *
     * @return void
     */
    public function testRoleIsFalse()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $result = $lang->evaluate("role(r)", ['r' => 'not_a_valid_role']);
        self::assertFalse($result);
    }


    /**
     * Use a valid capability with the `capability` expression function (CapabilityExpressionFunctionProvider).
     *
     * Expect the value of `PermissionExpressionTest::TEST_IS_AUTHORIZED_RESULT`.
     *
     * @return void
     */
    public function testIsAuthorized()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $result = $lang->evaluate("is_authorized()", []);
        self::assertEquals(PermissionExpressionTest::TEST_IS_AUTHORIZED_RESULT, $result);
    }

    /**
     * Regression test to verify ExpressionLanguage is working correctly.
     *
     * Should throw a SyntaxError.
     *
     * @return void
     */
    public function testIllegalToken()
    {
        $lang = new PermissionExpressionLanguage($this->permissions);

        $this->expectException(SyntaxError::class);
        $lang->evaluate('invalid_function()', []);
    }
}