<?php

namespace Ignite\Core\Tests\Unit\Auth;

use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Ignite\Core\Auth\Authorization;
use Ignite\Core\Entities\Group;
use Ignite\Core\Entities\Permission;
use Ignite\Core\Entities\User;
use Ignite\Core\Tests\TestCase;

class AuthorizationTest extends TestCase
{
    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();
    }

    /**
     * @test
     * @group Authorization
     * @group Permission
     */
    public function it_can_cache_the_permissions_in_the_configured_cache()
    {
        $cache = $this->app->make(Repository::class);

        $permission = factory(Permission::class)->create(['key' => 'can_eat_shoe']);
        $group = factory(Group::class)->create(['key' => 'admin']);
        $user = factory(User::class)->create();

        $group->givePermissionTo($permission);
        $user->assignToGroup($group);
        $this->actingAs($user);

        $authorization = new Authorization($this->app->make(Gate::class), $cache);
        $authorization->forgetCachedPermissions();
        $authorization->getPermissions();

        $this->assertTrue($cache->get('ignite.permissions.cache')->contains($permission));
    }

    /**
     * @test
     * @group Authorization
     * @group Permission
     */
    public function it_can_cache_the_permissions_in_memory()
    {
        $gate = $this->app->make(Gate::class);
        $cache = \Mockery::mock(Repository::class)->shouldReceive('remember')->once()->andReturn('foo')->getMock();

        $permission = factory(Permission::class)->create(['key' => 'can_eat_shoe']);
        $group = factory(Group::class)->create(['key' => 'admin']);
        $user = factory(User::class)->create();

        $group->givePermissionTo($permission);
        $user->assignToGroup($group);
        $this->actingAs($user);

        $authorization = new Authorization($gate, $cache);
        $authorization->getPermissions();
        $authorization->getPermissions();
    }

    /**
     * @test
     * @group Authorization
     * @group Permission
     */
    public function it_can_forget_the_cached_permissions()
    {
        $gate = $this->app->make(Gate::class);
        $cache = $this->app->make(Repository::class);
        $cache->forget('ignite.permissions.cache');

        $permission = factory(Permission::class)->create(['key' => 'can_eat_shoe']);
        $group = factory(Group::class)->create(['key' => 'admin']);
        $user = factory(User::class)->create();

        $group->givePermissionTo($permission);
        $user->assignToGroup($group);
        $this->actingAs($user);

        $authorization = new Authorization($gate, $cache);
        $authorization->getPermissions();

        $this->assertTrue($cache->has('ignite.permissions.cache'));
        $this->assertTrue($cache->get('ignite.permissions.cache')->contains($permission));
        $authorization->forgetCachedPermissions();
        $this->assertFalse($cache->has('ignite.permissions.cache'));
    }

    /**
     * @test
     * @group Authorization
     * @group Permission
     */
    public function it_registers_the_permissions_to_the_gate_for_the_authenticated_user()
    {
        /** @var Permission $permission */
        $permission = factory(Permission::class)->create(['key' => 'eat_own_head']);
        $group = factory(Group::class)->create(['key' => 'specialist']);
        $user = factory(User::class)->create();

        $group->givePermissionTo($permission);
        $user->assignToGroup($group);
        $user->load('groups');
        $this->actingAs($user);

        $authorization = $this->app->make(Authorization::class);
        $authorization->registerPermissions();

        $this->assertTrue($user->can('eat_own_head'));
    }

    /**
     * @test
     * @group Authorization
     * @group Permission
     */
    public function it_authorizes_super_admins_to_allow_all_abilities()
    {
        $group = factory(Group::class)->create(['key' => 'admin']);
        $user = factory(User::class)->create();

        // We wont assign the 'eat_own_head" ability to the 'Super Admin' group
        // but our expectation is that the Super Admin should still have the ability.
        $user->assignToGroup($group);
        $user->load('groups');
        $this->actingAs($user);

        $authorization = $this->app->make(Authorization::class);
        $authorization->forgetPermissions()->registerPermissions();

        $this->assertTrue($user->can('eat_own_head'));
    }
}
