<?php

namespace Tests\Feature;

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

class VideoTest 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 video.
     *
     * @return void
     */
    public function testUserCanAccessVideoPage() :void
    {

        // Video gallery route which visit by user
        $response = $this->get(route('videos.index'));

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

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

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

        //He should be able to read the video category
        $response->assertSee($videoDescription->title);

    }

    /**
     * Method to ensure that the create form route exists.
     *
     * @return void
     */

    public function testUserCanCreateVideo()
    {
        //When user visit the new video form route
        $response = $this->get(route('videos.create'));

        //He should be able to see the fields which enable him to add new video
        $response->assertStatus(200);
        $title = metaFields('videos', 'title', getCurrentLocale());

        $response->assertSee($title || __('videos.name'));

    }

    /**
     * Method to ensure that the update form route exists.
     *
     * @return void
     */

    public function testUserCanEditVideo()
    {
        //Given we have video in the database
        $videoDescription = factory(VideoDescription::class)->create();

        //When user visit the video form route
        $response = $this->get(route('videos.edit', $videoDescription->video_id));

        //He should be able to see the fields which enable him to edit the video
        $response->assertStatus(200);
        $title = metaFields('videos', 'title', getCurrentLocale());
        $response->assertSee($title || __('videos.name'));

        $response->assertSee($videoDescription['title_'.getCurrentLocale()]);
    }

    /**
     * Method to ensure that the user can add video and video description.
     *
     * @return void
     */
    public function testUserCanAddVideo() :void
    {
        $videoDescription = factory(VideoDescription::class)->make();

        $dataToSave = [
            'type' => rand(1,2),
            'video_category_id' => $videoDescription->video->video_category_id,
        ];

        $code = Str::random(16);

        if ($dataToSave['type'] == 1){
            $dataToSave['video_url'] = 'https://www.youtube.com/embed/'.$code;
            $dataToSave['image'] = 'https://img.youtube.com/vi/'.$code.'/hqdefault.jpg';

        } else {
            $dataToSave['image'] = $this->faker->sentence;
            $dataToSave['video'] = $this->faker->sentence;
        }


        foreach (languages() as $key => $language) {
            $dataToSave['title_'.$language->local] = $this->faker->word;
        }


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

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

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


        //It gets stored in the database
        $this->assertDatabaseHas('videos',
            [
                'video'=> $dataToSave['type'] == 1 ? $dataToSave['video_url'] : $dataToSave['video'],
                'image'=> $dataToSave['image'],
                'type'=> $dataToSave['type'],
                'video_category_id'=> $dataToSave['video_category_id'],
            ]
        );

        foreach (languages() as $key => $language) {
            $this->assertDatabaseHas('video_descriptions',
                [
                    'title' => $dataToSave['title_' . $language->local],
                ]
            );
        }

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

        // Page appears in the Pages panel
        $response = $this->get(route('videos.grid'));

        //He should be able to read the video
        $response->assertSee($dataToSave['title_'.getCurrentLocale()]);

    }

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

        //Add a video object
        $videoDescription = factory(VideoDescription::class)->make(['language_id' => 0]);

        $dataToSave = [
            'type' => rand(1,2),
            'video_category_id' => $videoDescription->video->video_category_id,
        ];

        $code = Str::random(16);

        if ($dataToSave['type'] == 1){
            $dataToSave['video_url'] = 'https://www.youtube.com/embed/'.$code;
            $dataToSave['image'] = 'https://img.youtube.com/vi/'.$code.'/hqdefault.jpg';

        } else {
            $dataToSave['image'] = $this->faker->sentence;
            $dataToSave['video'] = $this->faker->sentence;
        }

        foreach (languages() as $key => $language) {
            $dataToSave['title_'.$language->local] = $this->faker->name;

        }

        //When user submits post request to edit video endpoint
        $response= $this->put(route('videos.update', $videoDescription->video_id),array_merge( $dataToSave, ['_token'=> csrf_token()] ) );

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

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

        //It gets stored in the database
        $this->assertDatabaseHas('videos',
            [
                'video'=> $dataToSave['type'] == 1 ? $dataToSave['video_url'] : $dataToSave['video'],
                'image'=> $dataToSave['image'],
                'type'=> $dataToSave['type'],
                'video_category_id'=> $dataToSave['video_category_id'],
            ]
        );

        foreach (languages() as $key => $language) {
            $this->assertDatabaseHas('video_descriptions',
                [
                    'title'=> $dataToSave['title_'.$language->local],
                ]
            );
        }


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

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

        //He should be able to read the video
        $response->assertSee($dataToSave['title_'.getCurrentLocale()]);
    }

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

        //Add a video object
        $videoDescription = factory(VideoDescription::class)->make();

        //When the user hit's with endpoint to delete the video
        $this->delete(route('videos.destroy', $videoDescription->video_id), ['_token'=> csrf_token()]);

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

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

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

        //When the user hit's the endpoint to delete the video
        $this->delete(route('videos.destroy', $videoDescription->video_id), ['_token'=> csrf_token()]);

        //The video should be deleted from the database.

        $this->assertDatabaseMissing('videos',['id'=> $videoDescription->video_id]);

        $this->assertDatabaseMissing('video_descriptions',['video_id'=> $videoDescription->video_id]);
    }

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

        $ids= $videoDescription->pluck('video_id')->toArray();

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

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

        $ids= $videoDescription->pluck('video_id')->toArray();

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

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

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

        $this->assertDatabaseMissing('video_descriptions',['video_id'=> $ids]);

    }

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

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

        $this->assertSoftDeleted('videos',['id'=> $videoDescription->video_id]);

        //the user restore the video
        $this->put(route('videos.restore', $videoDescription->video_id), ['_token'=> csrf_token()]);

        //The video should be restored .
        $this->assertDatabaseHas('videos',['id'=> $videoDescription->video_id, 'deleted_at'=> null]);
    }

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

        $ids= $videoDescription->pluck('video_id')->toArray();

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

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

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

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

    }
}
