<?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 Illuminate\Support\Str;

use App\Models\User;
use App\Models\Page;
use App\Models\StaffProfile;
use App\Models\StaffProfileList;
use App\Models\Tag;
use App\Models\Photo;
use App\Models\ContentElement;
use App\Models\PhotoBlock;

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

class StaffProfileTest extends DuskTestCase
{
    use WithFaker;

    public function test_a_staff_profile_can_be_created_updated_and_viewed()
    {
        $this->browse(function ($browser, $browser2) {
            $user = User::factory()->create();
            $user->password = bcrypt('password');
            $user->save();

            auth()->login($user);
            $page = Page::find(1);
            $user->createPermission('update', $page);
            $existing_photo = Photo::getPhotos()->first();
            $this->assertNotNull($existing_photo);
            auth()->logout();

            $input = StaffProfile::factory()->raw();
            $tag = Tag::factory()->create();
            $full_name = Arr::get($input, 'first_name').' '.Arr::get($input, 'last_name');

            $staff = User::factory()->create([
                'name' => $full_name,
            ]);

            $browser->visit('/staff-profiles-manage')
                    ->pause(1000)
                    ->assertMissing('@staff-profiles')
                    ->waitFor('@login-form')
                    ->assertVisible('@login-form')
                    ->type('@login-email', $user->email)
                    ->type('@login-password', 'password')
                    ->press('@submit-login-button')
                    ->pause(1000)
                    ->assertMissing('@staff-profiles');

            $user->addRole('staff-profiles-manager');
            $user->refresh();

            $browser->visit('/staff-profiles-manage')
                    ->waitFor('@staff-profiles-paginator')
                    ->waitFor('@create-staff-profile')
                    ->pause(1000)
                    ->click('@create-staff-profile')
                ->waitFor('@modal')
                ->with('@modal', function ($browser) use ($input, $tag, $staff, $existing_photo) {
                    $browser->waitFor('@staff-profile-new')
                       ->with('@staff-profile-new', function ($browser) use ($input, $tag, $staff, $existing_photo) {
                           $browser->type('@first-name', Arr::get($input, 'first_name'))
                               ->type('@last-name', Arr::get($input, 'last_name'))
                               ->type('@credentials', Arr::get($input, 'credentials'))
                               ->type('@departments', Arr::get($input, 'departments'))
                               ->with(new Autocomplete('tags'), function ($browser) use ($tag) {
                                   $browser->searchAndSelectMultiple($tag);
                               })
                               ->with(new Autocomplete('user'), function ($browser) use ($staff) {
                                   $browser->searchAndSelectSingle($staff);
                               })
                               ->assertValue('@autocomplete-search-user', $staff->search_label)
                               ->within(new Editor('bio_draft', 0), function ($browser) use ($input) {
                                   $browser->typeInEditor(Arr::get($input, 'bio'));
                               })
                               ->waitFor('@toggle-upload-form')
                               ->click('@toggle-upload-form')
                                ->elsewhereWhenAvailable('@modal', function ($browser) use ($existing_photo) {
                                    $browser->with('@file-uploads-form', function ($browser) {
                                        $browser->waitFor('@upload-new-images');
                                    })
                                    ->waitFor('@photo-container-'.$existing_photo->id, 10)
                                        ->click('@photo-'.$existing_photo->id)
                                        ->click('@add-images');
                                })
                                ->click('@save-staff-profile');
                       });
                })
                ->pause(1500)
                ->within(new Feedback(), function ($browser) use ($full_name) {
                    $browser->assertFeedbackContains($full_name.' Saved');
                });

            $staff_profile = StaffProfile::all()->last();
            $new_bio = $this->faker->paragraph();

            $browser->with('@staff-profiles-paginator', function ($browser) use ($full_name, $staff_profile, $new_bio) {
                $browser->type('@paginator-search', $full_name)
                        ->waitFor('@staff-profile-'.$staff_profile->id, 10)
                        ->click('@edit-staff-profile-'.$staff_profile->id);
            })
                ->with('@modal', function ($browser) use ($staff_profile, $new_bio) {
                    $browser->waitFor('@bio_draft-editor-'.$staff_profile->id)
                        ->within(new Editor('bio_draft', $staff_profile->id), function ($browser) use ($new_bio) {
                            $browser->typeInEditor($new_bio);
                        });
                })
                ->pause(1500)
                ->within(new Feedback(), function ($browser) use ($full_name) {
                    $browser->assertFeedbackContains($full_name.' Saved');
                });

            $staff_profile->refresh();
            $this->assertEquals('<p>'.$new_bio.'</p>', $staff_profile->bio_draft);

            $browser->click('@close-modal');

            $page = Page::factory()->create();
            $editor = User::factory()->create();
            $editor->createPermission('publish', $page);

            $browser2->visit(new Login())
                ->loginAndEditPage($page, $editor)
                ->waitFor('@content-elements-editor')
                ->with(new AddContentElement(), function ($browser) {
                    $browser->create('staff-profile-list');
                });

            $content_element = ContentElement::all()->last();
            $header = $this->faker->words(3, true);
            $body = $this->faker->paragraph();

            $browser2->waitFor('@content-element-'.$content_element->id)
                     ->with('@content-element-'.$content_element->id, function ($browser2) use ($tag, $content_element, $header, $body) {
                         $browser2->type('@header', $header)
                                ->within(new Editor('body', $content_element->id), function ($browser2) use ($body) {
                                    $browser2->typeInEditor($body);
                                })
                                ->pause(2500)
                             ->with(new Autocomplete('tags'), function ($browser2) use ($tag) {
                                 $browser2->searchAndSelectMultiple($tag);
                             });
                     })

                ->pause(2500)
                ->within(new Feedback(), function ($browser2) use ($full_name) {
                    $browser2->assertFeedbackContains('Staff Profile List Saved');
                });

            $content_element->refresh();
            $this->assertEquals($header, $content_element->content->header);
            $this->assertEquals($body, strip_tags($content_element->content->body));

            $browser2->pause(1000)
                ->click('@publish-page')
                ->acceptDialog()
                ->clickAndWaitForReload('@editing-button')
                ->waitFor('@user-menu')
                ->click('@user-menu')
                ->pause(500)
                ->click('@logout-button')
                ->pause(1000)
                ->visit('/'.$this->getEditingValue($page, 'full_slug'))
                ->waitFor('@content-element-'.$content_element->id)
                ->pause(1000)
                ->assertMissing('@staff-profile-link-'.$staff_profile->id);

            $browser->click('@publish-staff-profile-'.$staff_profile->id)
                ->within(new Feedback(), function ($browser) use ($full_name) {
                    $browser->assertFeedbackContains($full_name.' Published');
                });

            $browser2->refresh()
                ->waitFor('@content-element-'.$content_element->id)
                ->pause(1000)
                ->assertSee($header)
                ->assertSee($body)
                ->assertVisible('@staff-profile-link-'.$staff_profile->id);

            $browser->click('@delete-staff-profile-'.$staff_profile->id)
                ->acceptDialog()
                ->within(new Feedback(), function ($browser) use ($full_name) {
                    $browser->assertFeedbackContains('Staff Profile Removed');
                });

            $browser2->refresh()
                ->waitFor('@content-element-'.$content_element->id)
                ->pause(1000)
                ->assertMissing('@staff-profile-link-'.$staff_profile->id);
        });
    }

    public function test_clicking_a_tag_in_the_paginator_filters_to_those_items_with_that_tag()
    {
        $this->browse(function ($browser) {
            $staff_profile1 = StaffProfile::factory()->create([
                'last_name' => 'aaa'.Str::random(5),
            ]);
            $staff_profile2 = StaffProfile::factory()->create([
                'last_name' => 'zzz'.Str::random(5),
            ]);
            $staff_profile3 = StaffProfile::factory()->create([
                'last_name' => 'aaa'.Str::random(5),
            ]);
            $tag = Tag::factory()->create();
            $staff_profile1->addTag($tag);
            $staff_profile2->addTag($tag);
            $user = User::factory()->create();
            $user->addRole('staff-profiles-manager');

            $browser->visit(new Login())
                ->loginUser($user)
                ->visitRoute('staff-profiles.manage')
                ->whenAvailable('@staff-profiles-paginator', function ($browser) use ($staff_profile1, $staff_profile2, $staff_profile3, $tag) {
                    $browser->click('@clear-paginator-search')
                        ->pause(1000)
                        ->waitFor('@staff-profile-'.$staff_profile1->id, 10)
                        ->assertMissing('@staff-profile-'.$staff_profile2->id)
                        ->assertVisible('@staff-profile-'.$staff_profile3->id)
                        ->whenAvailable('@staff-profile-'.$staff_profile1->id, function ($browser) use ($tag) {
                            $browser->click('@tag-'.$tag->id);
                        })
                        ->pause(1000)
                        ->assertVisible('@staff-profile-'.$staff_profile1->id)
                        ->assertVisible('@staff-profile-'.$staff_profile2->id)
                        ->assertMissing('@staff-profile-'.$staff_profile3->id);
                });
        });
    }

    public function test_a_staff_profile_can_be_edited_by_its_user()
    {
        $this->browse(function ($browser) {
            $staff_profile = StaffProfile::factory()->create();
            $staff_profile->publish();
            $user = $staff_profile->user;
            $this->assertInstanceOf(User::class, $user);
            $input = StaffProfile::factory()->raw();

            $browser->visit(new Login())
                    ->loginUser($user)
                    ->waitFor('@user-menu')
                    ->click('@user-menu')
                    ->with('@user-menu-items', function ($browser) {
                        $browser->waitFor('@edit-profile')
                            ->click('@edit-profile');
                    })
                    ->whenAvailable('@staff-profile-'.$staff_profile->id, function ($browser) use ($staff_profile, $input) {
                        $browser->waitFor('@first-name')
                                ->pause(500)
                                ->assertValue('@first-name', $staff_profile->first_name)
                                ->assertValue('@last-name', $staff_profile->last_name)
                                ->assertValue('@credentials', $staff_profile->credentials)
                                ->assertValue('@departments', $staff_profile->departments)
                                ->assertSee(strip_tags($staff_profile->bio_draft))
                                ->type('@first-name', Arr::get($input, 'first_name'))
                               ->type('@last-name', Arr::get($input, 'last_name'))
                               ->type('@credentials', Arr::get($input, 'credentials'))
                               ->type('@departments', Arr::get($input, 'departments'))
                               ->within(new Editor('bio_draft', $staff_profile->id), function ($browser) use ($input) {
                                   $browser->typeInEditor(Arr::get($input, 'bio_draft'));
                               })
                               ->pause(1500);
                    })
                    ->within(new Feedback(), function ($browser) use ($input) {
                        $browser->assertFeedbackContains(Arr::get($input, 'first_name').' '. Arr::get($input, 'last_name').' Saved');
                    });


            /* What is this for??
                    ->assertVisible('@menu')
                    ->click('@menu')
                    ->pause(500);

            $top_level_page = Page::find(1)->getPages()->filter(function ($page) {
                return !$page->version->unlisted;
            })->random();

            $browser->assertVisible('@top-menu-'.$top_level_page->id);
             */

            $staff_profile->refresh();
            $this->assertEquals(Arr::get($input, 'first_name'), $staff_profile->first_name);
            $this->assertEquals(Arr::get($input, 'last_name'), $staff_profile->last_name);
            $this->assertEquals(Arr::get($input, 'credentials'), $staff_profile->credentials);
            $this->assertEquals(Arr::get($input, 'departments'), $staff_profile->departments);
            $this->assertEquals('<p>'.Arr::get($input, 'bio_draft').'</p>', $staff_profile->bio_draft);
            $this->assertNotEquals($staff_profile->bio, $staff_profile->bio_draft);
        });
    }
}
