<?php

namespace Tests\Feature;

use App\User;
use App\Category;
use App\Newsletter;
use Tests\TestCase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class NewsletterTest 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 newsletters panel.
     *
     * @return void
     */
    public function testUserCanAccessNewslettersPanel() :void
    {

        // Newsletters route which visit by user
        $response = $this->get(route('newsletters.index'));

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

    /**
     * Method to ensure that the user can read all newsletters.
     *
     * @return void
     */
    public function testUserCanReadAllNewsletters() :void
    {
        //Given we have newsletter and newsletter
        $newsletter = factory(Newsletter::class)->create();

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

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

    }

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

        $send_to = metaFields('newsletters', 'email', getCurrentLocale());
        $subject = metaFields('newsletters', 'subject', getCurrentLocale());
        $message = metaFields('newsletters', 'message', getCurrentLocale());
        $category = metaFields('newsletters', 'categories', getCurrentLocale());

        //He should be able to see the fields which enable him to add new newsletter
        $response->assertStatus(200);
        $response->assertSee($send_to ?? __('newsletters.send_to'));
        $response->assertSee($subject ?? __('newsletters.subject'));
        $response->assertSee($message ?? __('newsletters.message'));
        $response->assertSee($category ?? __('newsletters.categories'));
    }

    /**
     * Method to ensure that the update form route exists.
     *
     * @return void
     */
    public function testUserCanEditNewsletter() :void
    {
        //Given we have newsletter and newsletter
        $newsletter = factory(Newsletter::class)->create();
        //When user visit the newsletters form route
        $response = $this->get(route('newsletters.edit', $newsletter->id));

        //He should be able to see the fields which enable him to edit the newsletter
        $send_to = metaFields('newsletters', 'email', getCurrentLocale());
        $subject = metaFields('newsletters', 'subject', getCurrentLocale());
        $message = metaFields('newsletters', 'message', getCurrentLocale());
        $category = metaFields('newsletters', 'categories', getCurrentLocale());

        //He should be able to see the fields which enable him to add new newsletter
        $response->assertStatus(200);
        $response->assertSee($send_to ?? __('newsletters.send_to'));
        $response->assertSee($subject ?? __('newsletters.subject'));
        $response->assertSee($message ?? __('newsletters.message'));
        $response->assertSee($category ?? __('newsletters.categories'));

        $response->assertSee($newsletter->email);

    }

    /**
     * Method to ensure that the user can add newsletter.
     *
     * @return void
     */
    public function testUserCanAddNewsletter() :void
    {
        $newsletter = factory(Newsletter::class)->create();
        $categories = factory(Category::class, 3)->create()->pluck('id')->toArray();
        $categoriesName = DB::table('category_descriptions')
            ->whereIn('category_id', $categories)
            ->where('language_id', currentLanguage()->id)->pluck('name')->toArray();

        $dataToSave = [
            'email' => $newsletter->email,
            'subject' => $this->faker->sentence,
            'message' => $this->faker->paragraph,
            'categories' => $categories
        ];

        //When user submits post request to create newsletter endpoint
        $response= $this->post(route('newsletters.store'), array_merge( $dataToSave, ['_token'=> csrf_token()] ) );
        // The redirect response header status is 302
        $response->assertStatus(302);

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


        //It gets stored in the database
        $this->assertDatabaseHas('mail_lists',
            [
                'send_to' => $dataToSave['email'],
                'subject' => $dataToSave['subject'],
                'message' => $dataToSave['message'],
                'categories' => json_encode($categoriesName),
            ]);

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

        // newsletter appears in the Newsletters panel
        $response = $this->get(route('newsletters.grid'));

        //He should be able to read the newsletter
        $response->assertSee($dataToSave['email']);

        // user should be able to read this newsletter
        $response = $this->get(route('newsletters.mailsList'));

        $response->assertStatus(200);

    }

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

        $newsletter = factory(Newsletter::class)->create();

        $categories = factory(Category::class, 3)->create()->pluck('id')->toArray();
        $categoriesName = DB::table('category_descriptions')
            ->whereIn('category_id', $categories)
            ->where('language_id', currentLanguage()->id)->pluck('name')->toArray();
        $dataToSave = [
            'email' => $newsletter->email,
            'subject' => $this->faker->sentence,
            'message' => $this->faker->paragraph,
            'categories' => $categories
        ];

        //When user submits post request to edit newsletter endpoint
        $response= $this->put(route('newsletters.update', $newsletter->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/newsletters');;

        //It gets stored in the database
        $this->assertDatabaseHas('mail_lists',
            [
                'subject' => $dataToSave['subject'],
                'send_to' => $dataToSave['email'],
                'categories' => json_encode($categoriesName),
            ]);


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

        // newsletter appears in the Newsletters panel
        $response = $this->get(route('newsletters.grid'));

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

        // user should be able to read this newsletter
        $response = $this->get(route('newsletters.mailsList'));

        $response->assertStatus(200);
    }

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

        //Add a newsletter object
        $newsletter = factory(Newsletter::class)->create();

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

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

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

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

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

        //The newsletter should be deleted from the database.

        $this->assertDatabaseMissing('newsletters',['id'=> $newsletter->id]);

    }

    /**
     * Method to ensure that the user can send multiple newsletters to trash.
     *
     * @return void
     */
    public function testNewslettersMultiTrash() :void
    {
        //Add a newsletter object
        $newsletters = factory(Newsletter::class, 3)->create();

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

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

    /**
     * Method to ensure that the user can delete multiple newsletters.
     *
     * @return void
     */
    public function testNewslettersMultiDelete() :void
    {
        //Add a newsletter object
        $newsletters = factory(Newsletter::class, 3)->create();

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

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

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

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

    }

    /**
     * Method to ensure that the user can restore the newsletter from trash.
     *
     * @return void
     */
    public function testNewslettersRestore() :void
    {
        //Add a newsletter object
        $newsletters = factory(Newsletter::class)->create();

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

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

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

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

    /**
     * Method to ensure that the user can restore multiple newsletters.
     *
     * @return void
     */
    public function testNewslettersMultiRestore() :void
    {
        //Add a newsletter object
        $newsletters = factory(Newsletter::class, 3)->create();

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

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

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

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

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

    }
}
