<?php

namespace Tests\Feature;

use App\PageDescription;
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 PageTest 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 pages panel.
     *
     * @return void
     */
    public function testUserCanAccessPagesPanel() :void
    {
//        $gallery = factory(Page::class, 10)->create();
//        $gallery->each(function ($gallery) {
//            foreach (languages() as $language) {
//                $gallery->descriptions()->save(factory(PageDescription::class)->create([
//                    'page_id' => $gallery->id,
//                    'language_id' => $language->id,
//                ]));
//            }
//        });
        // Pages route which visit by user
        $response = $this->get(route('pages.index'));

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

    /**
     * Method to ensure that the user can read all pages.
     *
     * @return void
     */
    public function testUserCanReadAllPages() :void
    {
        //Given we have page and page description in the database
        $page = factory(PageDescription::class)->create();

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

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

    }

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

        //He should be able to see the fields which enable him to add new page
        $response->assertStatus(200);

        $title = metaFields('pages', 'title', getCurrentLocale());
        $slug = metaFields('pages', 'slug', getCurrentLocale());
        $keywords = metaFields('pages', 'keywords', getCurrentLocale());
        $meta_description = metaFields('pages', 'meta_description', getCurrentLocale());

        $response->assertSee($title ?? __('pages.title'));
        $response->assertSee($slug ?? __('pages.slug'));
        $response->assertSee($keywords ?? __('pages.keywords'));
        $response->assertSee($meta_description ?? __('pages.meta_description'));
    }

    /**
     * Method to ensure that the update form route exists.
     *
     * @return void
     */
    public function testUserCanEditPage() :void
    {
        //Given we have page and page description in the database
        $page = factory(PageDescription::class)->create();
        //When user visit the pages form route
        $response = $this->get(route('pages.edit', $page->page_id));

        //He should be able to see the fields which enable him to edit the page
        $response->assertStatus(200);

        $title = metaFields('pages', 'title', getCurrentLocale());
        $slug = metaFields('pages', 'slug', getCurrentLocale());
        $keywords = metaFields('pages', 'keywords', getCurrentLocale());
        $meta_description = metaFields('pages', 'meta_description', getCurrentLocale());

        $response->assertSee($title ?? __('pages.title'));
        $response->assertSee($slug ?? __('pages.slug'));
        $response->assertSee($keywords ?? __('pages.keywords'));
        $response->assertSee($meta_description ?? __('pages.meta_description'));

        $response->assertSee($page->title);
        $response->assertSee($page->slug);

    }

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

        $dataToSave = ['image' => Str::random(19)];

        foreach (languages() as $key => $language) {
            $dataToSave['title_'.$language->local] = Str::random(16);
            $dataToSave['slug_'.$language->local] = Str::random(16);
            $dataToSave['meta_description_'.$language->local] = Str::random(16);
            $dataToSave['keywords_'.$language->local] = Str::random(16);
            $dataToSave['description_'.$language->local] = Str::random(16);
        }

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

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

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


        //It gets stored in the database
        foreach (languages() as $key => $language) {
            $this->assertDatabaseHas('page_descriptions',
                [
                    'title' => $dataToSave['title_' . $language->local],
                    'slug' => $dataToSave['slug_' . $language->local],
                ]);
        }

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

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

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

    }

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

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

        $dataToSave= ['image' => Str::random(16)];

        foreach (languages() as $key => $language) {
            $dataToSave['title_'.$language->local] = Str::random(16);
            $dataToSave['slug_'.$language->local] = Str::random(16);
            $dataToSave['meta_description_'.$language->local] = Str::random(16);
            $dataToSave['keywords_'.$language->local] = Str::random(16);
            $dataToSave['description_'.$language->local] = Str::random(16);
        }

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

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

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

        //It gets stored in the database
        foreach (languages() as $key => $language) {
            $this->assertDatabaseHas('page_descriptions',
                [
                    'title'=> $dataToSave['title_'.$language->local],
                    'slug'=> $dataToSave['slug_'.$language->local],
                    'language_id'=> $language->id,
                    'page_id'=> $page->page_id,
                ]
            );
        }


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

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

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

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

        //Add a page object
        $page = factory(PageDescription::class)->make();

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

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

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

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

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

        //The page should be deleted from the database.

        $this->assertDatabaseMissing('pages',['id'=> $page->page_id]);

        $this->assertDatabaseMissing('page_descriptions',['page_id'=> $page->page_id]);
    }

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

        $ids= $pages->pluck('page_id')->toArray();

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

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

        $ids= $pages->pluck('page_id')->toArray();

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

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

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

        $this->assertDatabaseMissing('page_descriptions',['page_id'=> $ids]);

    }

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

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

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

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

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

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

        $ids = $pages->pluck('page_id')->toArray();

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

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

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

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

    }
}
