<?php

namespace Tests\Feature;

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

use Illuminate\Support\Arr;

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

use Tests\Feature\SearchTestTrait;

class RoleTest extends TestCase
{
    use SearchTestTrait;

    protected function getClassname()
    {
        return 'role';
    }

    public function test_a_role_can_be_created()
    {
        $input = Role::factory()->raw();

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

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

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

        $this->signInAdmin();

        $this->json('POST', route('roles.store'), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'name',
             ]);

        $this->withoutExceptionHandling();
        $this->json('POST', route('roles.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
             ]);

        $role = Role::all()->last();

        $this->assertInstanceOf(Role::class, $role);
        $this->assertEquals(Arr::get($input, 'name'), $role->name);
    }

    public function test_a_role_can_be_edited()
    {
        $role = Role::factory()->create();
        $user = User::factory()->create();
        $input = Role::factory()->raw();
        $input['users'] = [
            $user->toArray(),
        ];

        $this->json('POST', route('roles.update', ['id' => $role->id]), [])
             ->assertStatus(401);

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

        $this->json('POST', route('roles.update', ['id' => $role->id]), [])
             ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('roles.update', ['id' => $role->id]), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'name',
             ]);

        $this->withoutExceptionHandling();
        $this->json('POST', route('roles.update', ['id' => $role->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
                'name' => Arr::get($input, 'name'),
             ]);

        $role->refresh();
        $this->assertEquals(Arr::get($input, 'name'), $role->name);
        $this->assertNotNull($role->users);
        $this->assertTrue($role->users->contains('name', $user->name));
    }


    public function test_the_roles_index_can_be_loaded()
    {
        $role = Role::all()->random();
        $user = User::factory()->create();
        $user->addRole($role);

        $this->get(route('roles.index'))
             ->assertRedirect('/login');

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

        $this->withoutExceptionHandling();
        $this->get(route('roles.index'))
             ->assertRedirect('/');

        $this->signInAdmin();

        $this->get(route('roles.index'))
             ->assertSuccessful();

        $this->json('GET', route('roles.index'))
             ->assertSuccessful()
            ->assertJsonFragment([
                'id' => $role->id,
                'name' => $role->name,
            ]);
    }

    /*
    public function a_user_can_be_removed_from_a_role()
    {
        $user = factory(User::class)->create();
        $role = factory(Role::class)->create();

        $user->addRole($role);

        $user->refresh();
        $role->refresh();

        $this->assertTrue($role->users->contains('id', $user->id));

        $this->signInAdmin();

        $input = [
            'user_id' => $user->id,
        ];

        $this->withoutExceptionHandling();

        $this->json('POST', route('roles.remove-user', ['id' => $role->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => $user->name.' Removed From '.$role->name,
             ]);

        $role->refresh();

        $this->assertFalse($role->users->contains('id', $user->id));
    }
     */


    public function test_a_roles_user_is_removed_if_it_is_not_included_in_the_save_input()
    {
        $user = User::factory()->create();
        $role = Role::factory()->create();

        $user->addRole($role);
        $user->refresh();

        $this->assertTrue($user->hasRole($role));

        $input = Role::factory()->raw();
        $input['users'] = [];

        $this->signInAdmin();

        $this->withoutExceptionHandling();

        $this->json('POST', route('roles.update', ['id' => $role->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Arr::get($input, 'name').' Saved',
             ]);

        $role->refresh();
        $user->refresh();
        $this->assertFalse($role->users->contains('id', $user->id));
        $this->assertFalse($user->hasRole($role));
    }

    public function test_a_role_can_be_deleted(): void
    {
        $role = Role::factory()->create();
        $user = User::factory()->create();
        $page = Page::factory()->create();

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

        $user->refresh();

        $this->assertTrue($user->hasRole($role->name));
        $this->assertTrue($user->can('update', $page));

        $this->json('POST', route('roles.remove', ['id' => $role->id]))
            ->assertStatus(401);

        $this->signIn($user);

        $this->json('POST', route('roles.remove', ['id' => $role->id]))
            ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('roles.remove', ['id' => $role->id]))
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $role->name.' & Permissions Removed',
            ]);

        $user->refresh();
        $page->refresh();

        $this->assertFalse($user->roles()->get()->contains('id', $role->id));
        $this->assertFalse($user->can('update', $page));
    }
}
