<?php

namespace Tests\Browser;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\WithFaker;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use Illuminate\Support\Arr;

use Tests\Browser\Components\Feedback;
use Tests\Browser\Components\Editor;
use Tests\Browser\Components\Autocomplete;
use Tests\Browser\Components\PhotoUpload;
use Tests\Browser\Components\AddContentElement;
use Tests\Browser\Pages\Login;
use Tests\Browser\Scroll;

use App\Models\User;
use App\Models\Page;
use App\Models\ContentElement;
use App\Models\InquiryForm;
use App\Models\Inquiry;
use App\Models\Tag;
use App\Models\Photo;
use App\Models\Livestream;
use App\Models\PhotoBlock;
use App\Models\TextBlock;
use App\Models\Quote;
use App\Models\LivestreamRegistration;

class InquiryTest extends DuskTestCase
{
    use WithFaker;

    public function test_an_inquiry_can_be_created()
    {
        $this->browse(function ($browser, $guest) {
            $inquiry_content_page = Inquiry::findPage();
            $inquiry_page = Page::factory()->create();
            $manager = User::factory()->create();
            $manager->addRole('inquiries-manager');
            $manager->refresh();
            $manager->createPermission('update', $inquiry_page);
            $manager->createPermission('publish', $inquiry_page);
            $manager->createPermission('update', $inquiry_content_page);
            $manager->createPermission('publish', $inquiry_content_page);

            $livestream = Livestream::factory()->privateChat()->create();
            $tag = Tag::where('name', 'Online Open Houses')->first();
            $this->assertInstanceOf(Tag::class, $tag);
            $livestream->addTag($tag);

            $content_tag1 = Tag::factory()->create();
            $content_tag2 = Tag::factory()->create();
            $content_tag3 = Tag::factory()->create();

            // create the selectable inquiry content
            $browser->visit(new Login())
                ->loginAndEditPage($inquiry_content_page, $manager)
                ->waitFor('@content-elements-editor')
                ->click('@debug')
                ->with(new AddContentElement(0), function ($browser) {
                    $browser->create('photo-block');
                });

            $content_element1 = ContentElement::all()->last();

            $browser->pause(2500)
                ->with(new AddContentElement(1), function ($browser) {
                    $browser->create('text-block');
                });

            $content_element2 = ContentElement::all()->last();

            $browser->pause(500)
                ->with(new AddContentElement(2), function ($browser) {
                    $browser->create('quote');
                });

            $content_element3 = ContentElement::all()->last();

            // create content in photo block
            $input1 = PhotoBlock::factory()->withText()->raw();

            $browser->pause(2500);

            Scroll::centerInView($browser, '@content-element-'.$content_element1->id);
            $browser->with('@content-element-'.$content_element1->id, function ($browser) use ($content_element1, $input1, $content_tag1) {
                $browser->waitFor('@toggle-text', 5)
                        ->pause(250)
                        ->click('@toggle-text')
                        ->pause(250)
                        ->waitFor('@header')
                        ->type('@header', Arr::get($input1, 'header'))
                        ->within(new Editor('body', $content_element1->id), function ($browser) use ($input1) {
                            $browser->typeInEditor(Arr::get($input1, 'body'));
                        })
                        ->with(new Autocomplete('tags'), function ($browser) use ($content_tag1) {
                            $browser->searchAndSelectMultiple($content_tag1);
                        });
            });

            // create content in text block
            $input2 = TextBlock::factory()->raw();
            Scroll::centerInView($browser, '@content-element-'.$content_element2->id);
            $browser->with('@content-element-'.$content_element2->id, function ($browser) use ($content_element2, $input2, $content_tag2) {
                $browser ->waitFor('@header')
                        ->type('@header', Arr::get($input2, 'header'))
                        ->within(new Editor('body', $content_element2->id), function ($browser) use ($input2) {
                            $browser->typeInEditor(Arr::get($input2, 'body'));
                        })
                        ->with(new Autocomplete('tags'), function ($browser) use ($content_tag2) {
                            $browser->searchAndSelectMultiple($content_tag2);
                        });
            });

            // create content in text block
            $input3 = Quote::factory()->raw();
            Scroll::centerInView($browser, '@content-element-'.$content_element3->id);
            $browser->with('@content-element-'.$content_element3->id, function ($browser) use ($content_element3, $input3, $content_tag3) {
                $browser->within(new Editor('body', $content_element3->id), function ($browser) use ($input3) {
                    $browser->typeInEditor(Arr::get($input3, 'body'));
                })
                        ->type('@author-name', Arr::get($input3, 'author_name'))
                        ->type('@author-details', Arr::get($input3, 'author_details'))
                        ->with(new Autocomplete('tags'), function ($browser) use ($content_tag3) {
                            $browser->searchAndSelectMultiple($content_tag3);
                        });
            })
            ->pause(1500)
            ->within(new Feedback(), function ($browser) {
                $browser->assertFeedbackContains('Quote Saved');
            });

            $browser->click('@publish-page')
                ->acceptDialog()
                ->pause(5000)
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Page Published');
                });

            // add the inquiry form to a page
            $browser->visit($this->getEditingValue($inquiry_page, 'full_slug'))
                ->waitFor('@content-elements-editor')
                ->pause(500)
                ->with(new AddContentElement(), function ($browser) {
                    $browser->create('inquiry-form');
                })
                ->pause(5000);

            $content_element = ContentElement::all()->last();
            $this->assertInstanceOf(InquiryForm::class, $content_element->content);
            $inquiry_form = $content_element->content;
            $input = InquiryForm::factory()->raw();

            $browser->with('@content-element-'.$content_element->id, function ($browser) use ($content_element, $input, $tag) {
                $browser->type('@header', Arr::get($input, 'header'))
                        ->within(new Editor('body', $content_element->id), function ($browser) use ($input) {
                            $browser->typeInEditor(Arr::get($input, 'body'));
                        })
                        ->with(new Autocomplete('tags'), function ($browser) use ($tag) {
                            $browser->searchAndSelectMultiple($tag);
                        })
                        ->click('@checkbox-show-type')
                        ->click('@checkbox-show-student-info')
                        ->click('@checkbox-show-interests')
                        ->click('@checkbox-show-livestreams')
                        ->click('@checkbox-show-question');
            })
            ->pause(1500)
            ->with(new Feedback(), function ($browser) {
                $browser->assertFeedbackContains('Inquiry Form Saved');
            });

            $content_element->refresh();
            $inquiry_form->refresh();

            $this->assertEquals(Arr::get($input, 'header'), $inquiry_form->header);
            $this->assertEquals('<p>'.Arr::get($input, 'body').'</p>', $inquiry_form->body);
            $this->assertTrue($inquiry_form->tags->contains('id', $tag->id));
            $this->assertTrue($inquiry_form->show_student_info);
            $this->assertTrue($inquiry_form->show_interests);
            $this->assertTrue($inquiry_form->show_livestreams);
            $this->assertFalse($inquiry_form->show_livestreams_first);
            $this->assertTrue($inquiry_form->show_question);
            $this->assertFalse($inquiry_form->create_password);

            $browser->with(new PhotoUpload($content_element), function ($browser) {
                $browser->addPhoto();
            });

            $photo = Photo::all()->last();

            $content_element->refresh();
            $inquiry_form->refresh();

            $this->assertTrue($inquiry_form->photos()->count() > 0);

            $browser->click('@publish-page')
                ->acceptDialog()
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Page Published');
                })
                ->clickAndWaitForReload('@editing-button')
                ->assertNotPresent('@page-editor')
                ->assertVisible('@content-element-'.$content_element->id);

            $inquiry_page->refresh();

            $guest_input = User::factory()->raw();
            $secondary_name = $this->faker->name;
            $question = $this->faker->paragraph();
            $grade = collect([8, 9, 10, 11, 12])->random();

            $guest->visit($inquiry_page->full_slug)
                ->waitFor('@content-element-'.$content_element->id)
                ->with('@content-element-'.$content_element->id, function ($guest) use ($guest_input, $input, $inquiry_form, $grade, $livestream, $question, $content_tag1, $content_tag2, $content_tag3, $secondary_name) {
                    $guest->assertSee($inquiry_form->header)
                          ->assertSee(Arr::get($input, 'body'))
                          ->waitFor('@inquiry-form')
                          ->with('@inquiry-form', function ($guest) use ($guest_input, $grade, $livestream, $question, $content_tag1, $content_tag2, $content_tag3, $secondary_name) {
                              // step 1
                              $guest->pause(500)
                                    ->assertVue('currentStep', 'Contact Information')
                                    ->pause(500)
                                    ->waitFor('@next')
                                    ->click('@next')
                                    ->pause(500)
                                    ->waitFor('@error-type')
                                    ->assertSeeIn('@error-type', 'Please select the type of person creating the inquiry')
                                    ->click('@type-parent/guardian')
                                    ->pause(500)
                                    ->click('@next')
                                    ->waitFor('@error-name')
                                    ->assertSeeIn('@error-name', 'Please provide a name')
                                    ->assertSeeIn('@error-email', 'Please provide an email')
                                    ->type('@name', Arr::get($guest_input, 'name'))
                                    ->type('@email', 'foobar')
                                    ->click('@next')
                                    ->pause(500)
                                    ->assertSeeIn('@error-email', 'Please provide a valid email address')
                                    ->type('@email', Arr::get($guest_input, 'email'))
                                    ->keys('@email', ['{enter}'])
                                    ->assertVue('currentStep', 'Student Information')
                                    ->click('@back')
                                    ->assertVue('currentStep', 'Contact Information')
                                    ->type('@secondary-name', $secondary_name)
                                    ->click('@next')
                                    // step 2
                                    ->pause(500)
                                    ->assertVue('currentStep', 'Student Information')
                                    ->waitFor('@start-year-'.now()->addYears(1)->format('Y'))
                                    ->click('@next')
                                    ->pause(500)
                                    ->assertSeeIn('@error-target_year', 'Please select an entry year')
                                    ->assertSeeIn('@error-target_grade', 'Please select an entry grade')
                                    ->click('@start-year-'.now()->addYears(1)->format('Y'))
                                    ->click('@grade-'.$grade)
                                    ->click('@next')
                                    // step 3
                                    ->pause(500)
                                    ->assertVue('currentStep', 'Student Interests')
                                    ->assertVisible('@select-tag-'.$content_tag1->id)
                                    ->assertVisible('@select-tag-'.$content_tag2->id)
                                    ->assertVisible('@select-tag-'.$content_tag3->id)
                                    ->click('@select-tag-'.$content_tag1->id)
                                    ->click('@select-tag-'.$content_tag3->id)
                                    ->click('@next')
                                    // step 4
                                    ->pause(500)
                                    ->assertVue('currentStep', 'Livestreams')
                                    ->assertVue('livestreamTitle', 'Online Open Houses')
                                    ->assertSee('Upcoming Online Open Houses')
                                    ->waitFor('@upcoming-livestream-'.$livestream->id)
                                    ->click('@upcoming-livestream-'.$livestream->id)
                                    ->click('@next')
                                    // step 4
                                    ->pause(500)
                                    ->assertVue('currentStep', 'Ask A Question')
                                    ->waitFor('@question')
                                    ->type('@question', $question)
                                    ->click('@next')
                                    // step 5
                                    ->pause(500)
                                    ->assertVue('currentStep', 'Review Information')
                                    ->waitFor('@review-information')
                                    ->pause(500)
                                    ->with('@review-information', function ($guest) use ($guest_input, $grade, $livestream, $question) {
                                        $guest->assertSee(Arr::get($guest_input, 'name'))
                                              ->assertSee(Arr::get($guest_input, 'email'))
                                              ->assertSee(now()->addYears(1)->format('Y').'/'.now()->addYears(2)->format('Y'))
                                              ->assertSee('Grade '.$grade)
                                              ->assertSee($question)
                                              ->assertSee($livestream->start_date->setTimezone('America/Vancouver')->format('l F j, Y g:i a'));
                                    })
                                    ->waitFor('@finish')
                                    ->click('@finish');
                          });
                })
                ->waitFor('@inquiry-personalized');

            $inquiry = Inquiry::all()->last();
            $inquiry_user = $inquiry->user;
            $this->assertInstanceOf(User::class, $inquiry_user);
            $this->assertEquals(Arr::get($guest_input, 'email'), $inquiry->user->email);
            $this->assertEquals($secondary_name, $inquiry->secondary_name);
            $this->assertEquals('Parent/Guardian', $inquiry->type);

            $guest->waitForRoute('inquiries.view', ['id' => $inquiry->id])
                  ->with('@inquiry-personalized', function ($guest) use ($inquiry, $grade, $livestream) {
                      $guest->assertSee($inquiry->user->name)
                          ->assertSee($inquiry->user->email)
                          ->assertSee(now()->addYears(1)->format('Y').'/'.now()->addYears(2)->format('y'))
                          ->assertSee('Grade '.$grade)
                        ->assertVisible('@livestream-registration')
                        ->with('@livestream-registration', function ($guest) use ($livestream) {
                            $guest->assertSee($livestream->name)
                                  ->assertSee($livestream->start_date->setTimezone('America/Vancouver')->format('l F j, Y g:i a'));
                        });
                  });

            $guest->driver->executeScript('window.scrollTo({ left: 0, top: document.body.scrollHeight, behavior: "smooth" });');

            $guest->waitFor('@content-element-'.$content_element1->id)
                  ->pause(1000)
                  ->assertSeeIn('@content-element-'.$content_element1->id, $content_element1->content->header)
                  ->assertSeeIn('@content-element-'.$content_element1->id, strip_tags($content_element1->content->body))
                  ->assertMissing('@content-element-'.$content_element2->id)
                  ->assertVisible('@content-element-'.$content_element3->id)
                  ->assertSeeIn('@content-element-'.$content_element3->id, strip_tags($content_element3->content->body))
                  ->assertSeeIn('@content-element-'.$content_element3->id, $content_element3->content->author_name)
                  ->assertSeeIn('@content-element-'.$content_element3->id, $content_element3->content->author_details);

            $livestream_registration = LivestreamRegistration::all()->last();
            $this->assertNotNull($inquiry->user);
            $this->assertEquals($livestream_registration->user->id, $inquiry->user->id);
            $this->assertEquals($livestream_registration->livestream_id, $livestream->id);
            $guest->visit($livestream_registration->url)
                  ->waitForRoute('livestreams.view', ['id' => $livestream->id])
                  ->assertAuthenticatedAs($inquiry_user);
        }); // end of test
    }

    public function test_registering_for_an_account_and_then_logging_in_doesnt_give_a_401()
    {
        $this->browse(function ($browser, $browser2) {
            $register_page = Page::whereHas('versions', function ($query) {
                $query->where('name', 'Register');
            })->first();
            $this->assertInstanceOf(Page::class, $register_page);

            $editor = User::factory()->create();
            $editor->addRole('inquiries-manager');
            $editor->createPermission('publish', $register_page);

            //$version = $register_page->getDraftVersion();

            $browser->visit(new Login())
                ->loginAndEditPage($register_page, $editor)
                ->waitFor('@content-elements-editor')
                ->pause(500)
                ->with(new AddContentElement(), function ($browser) {
                    $browser->create('inquiry-form');
                })
                ->pause(3000);

            $content_element = ContentElement::all()->last();
            $this->assertInstanceOf(InquiryForm::class, $content_element->content);
            $inquiry_form = $content_element->content;

            $browser->with('@content-element-'.$content_element->id, function ($browser) use ($content_element) {
                $browser->pause(1000)
                    ->type('@header', 'Create Account')
                    ->click('@checkbox-create-password');
            })
                ->pause(1500)
                ->with(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Inquiry Form Saved');
                })
                ->click('@publish-page')
                ->acceptDialog()
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Page Published');
                });

            $inquiry_form->refresh();
            $this->assertTrue($inquiry_form->create_password);

            $input = [
                'name' => $this->faker->name(),
                'email' => $this->faker->unique()->safeEmail,
                'password' => 'password',
            ];

            $browser2->visitRoute('users.register')
                 ->waitFor('@content-element-'.$content_element->id)
                 ->with('@content-element-'.$content_element->id, function ($browser2) use ($input) {
                     $browser2->type('@name', Arr::get($input, 'name'))
                             ->type('@email', Arr::get($input, 'email'))
                             ->type('@password', Arr::get($input, 'password'))
                             ->pause(200)
                             ->type('@confirm-password', Arr::get($input, 'password'))
                             ->click('@next')
                             ->pause(500)
                             ->click('@finish');
                 })
                ->waitFor('@inquiry-personalized');

            $new_user = User::all()->last();

            $browser2->click('@login')
                ->waitFor('@login-form')
                ->waitFor('@login-email', 20)
                ->type('@login-email', $new_user->email)
                ->type('@login-password', Arr::get($input, 'password'))
                ->pause(500)
                ->click('@submit-login-button')
                ->waitFor('@user-menu')
                ->click('@user-menu')
                ->pause(250)
                ->with('@user-menu', function ($browser2) use ($new_user) {
                    $browser2->assertSee($new_user->name);
                });
        });
    }
}
