<?php

namespace Tests\Feature;

use App\Module;
use App\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Tests\TestCase;

class UserTest extends TestCase
{
    // To reset DB to it's status before run test
    use DatabaseTransactions;

    // To create fake sentences to help us test and validation
    use WithFaker;

    private $user;

    protected function setUp() :void
    {
        parent::setUp();

        // to see description
        $this->withoutExceptionHandling();

        // Start session to enables csrf_token()
        Session::start();

        // Authenticate user
        $this->user = factory(User::class)->create(['role'=>'admin']);

        $this->actingAs($this->user);
    }

    /**
     * Method to ensure that the user can access to users panel.
     *
     * @return void
     */
    public function testUserCanAccessUsersPanel() :void
    {
        // Users route which visit by user
        $response = $this->get(route('users.index'));

        // Should be return status 200
        $response->assertStatus(200);

        // View rendered
        $response->assertSee(__('users.head'));
    }

    /**
     * Method to ensure that the user can read all users.
     *
     * @return void
     */
    public function testUserCanReadAllUsers() :void
    {
        //Given we have user in the database
        $user = factory(User::class)->create();

        //When user visit the users
        $response = $this->get(route('users.grid'));
        // status should be 200
        $response->assertStatus(200);

        //He should be able to read the user
        $response->assertSee($user->name);
        $response->assertSee($user->email);

    }

    /**
     * Method to ensure that the create form route exists.
     *
     * @return void
     */
    public function testUserCanCreateUser() :void
    {
        //When user visit the new user form route
        $response = $this->get(route('users.create'));

        //He should be able to see the fields which enable him to add new user
        $response->assertStatus(200);
        $response->assertSee(__('users.name'));
        $response->assertSee(__('users.email'));
        $response->assertSee(__('users.phone'));
        $response->assertSee(__('users.photo'));
        $response->assertSee(__('users.modules'));
    }

    /**
     * Method to ensure that the update form route exists.
     *
     * @return void
     */
    public function testUserCanEditUser() :void
    {
        //Given we have user in the database
        $user = factory(User::class)->create();
        //When user visit the users form route
        // dd( $user->id, $user->toArray(), route('users.edit', $user->id) );
        $response = $this->get(route('users.edit', $user->id));

        //He should be able to see the fields which enable him to edit the user
        $response->assertStatus(200);
        $response->assertSee(__('users.name'));
        $response->assertSee(__('users.email'));
        $response->assertSee(__('users.phone'));
        $response->assertSee(__('users.photo'));
        $response->assertSee(__('users.modules'));

        $response->assertSee($user->name);
        $response->assertSee($user->email);

    }

    /**
     * Method to ensure that the user can add user and user description.
     *
     * @return void
     */
    public function testUserCanAddUser() :void
    {

        $user = factory(User::class)->make();

        $data = $user->toArray();
        $data['password'] = '123';
        $data['password_confirmation'] = '123';
        $data['modules']= [];
        foreach (Module::cursor() as $module) {
            $data['modules'][]= $module->id;
        }
        //When user submits post request to edit user endpoint
        // dd($data);

        //When user submits post request to create user endpoint
        $response= $this->post(route('users.store'), array_merge( $data, ['_token'=> csrf_token()] ) );

        // The redirect response header status is 302
        $response->assertStatus(302);

        // The response redirect to users
        $response->assertRedirect('admin/users');

        //It gets stored in the database
        $this->assertDatabaseHas('users',[ 'name'=> $user->name, 'email'=> $user->email]);

        // Session success message
        $response->assertSessionHas('message', __('dashboard.saveDone'));

        // User appears in the Users panel
        $response = $this->get(route('users.grid'));

        //He should be able to read the user
        $response->assertSee($user->name);

    }

    /**
     * Method to ensure that the user can Edit user.
     *
     * @return void
     */
    public function testUserCanUpdateUser() :void
    {

        //Add a user object
        $user = factory(User::class)->create();

        $user->name = $this->faker->name('male');
        $user->email = $this->faker->unique()->safeEmail();

        $data = $user->toArray();
        $data['password'] = '123';
        $data['password_confirmation'] = '123';
        $data['modules']= [];
        foreach (Module::cursor() as $module) {
            $data['modules'][]= $module->id;
        }
        //When user submits post request to edit user endpoint
        // dd($data);
        $response= $this->put(route('users.update', $user->id),array_merge( $data, ['_token'=> csrf_token()] ) );

        // The redirect response header status is 302
        $response->assertStatus(302);

        // The response redirect to users
        $response->assertRedirect('/admin/users');;

        //It gets stored in the database
        $this->assertDatabaseHas('users',['id'=> $user->id, 'name'=> $user->name, 'email'=> $user->email]);

        // Session success message
        $response->assertSessionHas('message', __('dashboard.saveDone'));

        // User appears in the User grid
        $response = $this->get(route('users.grid'));

        //He should be able to read the user
        $response->assertSee($user->name);
    }

    /**
     * Method to ensure that the user can send the user to trash.
     *
     * @return void
     */
    public function testUsersTrash() :void
    {

        //Add a user object
        $user = factory(User::class)->make();

        //When the user hit's wethe endpoint to delete the user
        $this->delete(route('users.destroy', $user->id), ['_token'=> csrf_token()]);

        //The user should be deleted from the database.
        $this->assertSoftDeleted('users',['id'=> $user->id]);
    }

    /**
     * Method to ensure that the user can delete the user from database.
     *
     * @return void
     */
    public function testUsersDelete() :void
    {
        //Add a user object
        $user =  factory(User::class)->make();

        // user sent the category to trash first, as he can not delete it from the first click
        $this->delete(route('users.destroy', $user->id), ['_token'=> csrf_token()]);

        //When the user hit's the endpoint to delete the category
        $this->delete(route('users.destroy', $user->id), ['_token'=> csrf_token()]);

        //The user should be deleted from the database.

        $this->assertDatabaseMissing('users',['id'=> $user->id]);
    }

    /**
     * Method to ensure that the user can send multiple users to trash.
     *
     * @return void
     */
    public function testUsersMultiTrash() :void
    {
        //Add a user object
        $users = factory(User::class, 3)->make();

        $ids= $users->pluck('id')->toArray();

        //When the user hit's the endpoint to send the users to trash
        $this->delete(route('users.destroyAll'), ['_token'=> csrf_token(), 'ids'=> $ids]);
        //The user should be deleted from the database.
        $this->assertSoftDeleted('users',['id'=> $ids]);
    }

    /**
     * Method to ensure that the user can delete multiple users.
     *
     * @return void
     */
    public function testUsersMultiDelete() :void
    {
        //Add a user object
        $users = factory(User::class, 3)->make();

        $ids= $users->pluck('id')->toArray();

        //When the user hit's the endpoint to send the users to trash
        $this->delete(route('users.destroyAll'), ['_token'=> csrf_token(), 'ids'=> $ids]);

        //When the user hit's the endpoint to delete the users from the db
        $this->delete(route('users.destroyAll'), ['_token'=> csrf_token(), 'ids'=> $ids, 'force' => true]);

        //The user should be deleted from the database.
        $this->assertDatabaseMissing('users',['id'=> $ids]);

    }

    /**
     * Method to ensure that the user can restore the user from trash.
     *
     * @return void
     */
    public function testUsersRestore() :void
    {
        //Add a user object
        $users = factory(User::class)->make();

        //the user send the user to trash
        $this->delete(route('users.destroy', $users->id), ['_token'=> csrf_token()]);

        $this->assertSoftDeleted('users',['id'=> $users->id]);

        //the user restore the user
        $this->put(route('users.restore', $users->id), ['_token'=> csrf_token()]);

        //The user should be restored .
        $this->assertDatabaseHas('users',['id'=> $users->id, 'deleted_at'=> null]);
    }

    /**
     * Method to ensure that the user can restore multiple users.
     *
     * @return void
     */
    public function testUsersMultiRestore() :void
    {
        //Add a user object
        $users = factory(User::class, 3)->make();

        $ids = $users->pluck('id')->toArray();

        //When the user hit's the endpoint to send the user to trash
        $response = $this->delete(route('users.destroyAll'), ['_token' => csrf_token(), 'ids' => $ids]);

        // Test last one to ensure soft deleted process done successfully
        $this->assertSoftDeleted('users', ['id' => $ids]);

        //When the user hit's the endpoint to restore the users from the trash
        $this->put(route('users.restoreAll'), ['_token' => csrf_token(), 'ids' => $ids]);

        // Test last one to ensure restore process done successfully
        $this->assertDatabaseHas('users', ['id' => $ids, 'deleted_at' => null]);

    }
}
