<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

use App\Models\User;
use App\Models\Page;
use App\Models\Role;
use App\Models\Permission;

class PermissionTest extends TestCase
{
    public function test_the_page_permissions_index_can_be_loaded()
    {
        $this->get(route('permissions.pages'))
             ->assertRedirect(route('login'));

        $this->signIn(User::factory()->create());

        $this->withoutExceptionHandling();
        $this->get(route('permissions.pages'))
             ->assertRedirect('/');

        $this->signInAdmin();

        $this->get(route('permissions.pages'))
            ->assertSuccessful();
    }

    public function test_permission_can_be_created_for_a_user()
    {
        $user = User::factory()->create();
        $page = Page::factory()->create();

        $this->json('POST', route('permissions.store'), [])
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('permissions.store'), [])
            ->assertStatus(403);

        $this->signInAdmin();
        $this->enableEditing();

        $this->json('POST', route('permissions.store'), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                 'objectables',
                 //'objectable_id',
                 //'objectable_type',
                 'users',
                 'roles',
                 'action',
             ]);

        $input = [
            'objectables' => [
                [ 'objectable_id' => $page->id, 'objectable_type' => $page->type ],
            ],
            'users' => [
                $user->toArray(),
            ],
            'action' => 'view',
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('permissions.store'), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Permissions Saved',
                'name' => $page->version->name,
            ]);

        $this->assertTrue($user->can('view', $page));
    }

    public function test_permission_can_be_created_for_a_role()
    {
        $role = Role::factory()->create();
        $page = Page::factory()->create();

        $this->signInAdmin();

        $input = [
            'objectables' => [
                ['objectable_id' => $page->id, 'objectable_type' => $page->type],
            ],
            'roles' => [
                $role->toArray(),
            ],
            'action' => 'update',
        ];

        $this->json('POST', route('permissions.store'), $input)
            //->assertOK()
            ->assertJsonFragment([
                'success' => 'Permissions Saved',
            ]);

        $this->assertTrue($role->canPerformAction('update', $page));
    }

    public function test_a_pages_permissions_can_be_loaded()
    {
        $page = Page::factory()->create();
        $role = Role::factory()->create();

        $page->createPermission('update', $role);

        $this->assertTrue($role->canPerformAction('update', $page));

        $page->refresh();
        $permission = $page->permissions->last();

        $this->assertInstanceOf(Permission::class, $permission);
        $this->assertEquals($page->id, $permission->objectable_id);
        $this->assertInstanceOf(get_class($page), $permission->objectable);
        $this->assertEquals($role->id, $permission->accessable->id);

        $input = [
            'objectables' => [
                ['objectable_id' => $page->id, 'objectable_type' => $page->type],
            ],
        ];

        $this->json('POST', route('permissions.load'), $input)
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('permissions.load'), $input)
            ->assertStatus(403);

        $this->signInAdmin();

        $actions = $page->getPolicyMethods();

        $this->withoutExceptionHandling();
        $this->json('POST', route('permissions.load'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'id' => $permission->id,
                'accessable_id' => $role->id,
                'name' => $role->name,
                'available_actions' => $page->getPolicyMethods(),
             ]);
    }

    public function test_permission_can_be_removed()
    {
        $page = Page::factory()->create();
        $role = Role::factory()->create();

        $page->createPermission('update', $role);

        $this->assertTrue($role->canPerformAction('update', $page));

        $page->refresh();
        $permission = $page->permissions->last();

        $this->assertInstanceOf(Permission::class, $permission);
        $this->assertEquals($page->id, $permission->objectable->id);
        $this->assertEquals($role->id, $permission->accessable->id);

        $this->json('POST', route('permissions.destroy', ['id' => $permission->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('permissions.destroy', ['id' => $permission->id]))
            ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('permissions.destroy', ['id' => $permission->id]))
             ->assertSuccessful()
             ->assertJsonFragment(['success' => 'Permission Removed']);

        $this->assertNull(Permission::find($permission->id));

        $role->refresh();
        $page->refresh();
        $this->assertFalse($role->canPerformAction('update', $page));
    }

    public function test_random_actions_cant_be_added()
    {
        $user = User::factory()->create();
        $page = Page::factory()->create();

        $this->signInAdmin();

        $input = [
            'objectables' => [
                [ 'objectable_id' => $page->id, 'objectable_type' => $page->type ],
            ],
            'users' => [
                $user->toArray(),
            ],
            'action' => 'foobar',
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('permissions.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                 'success' => 'Permissions Saved',
                 'errors' => [
                     'action' => ['No foobar permission available'],
                 ],
             ]);

        $this->assertFalse($user->canPerformAction('foobar', $page));
    }
}
