<?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 Tests\Browser\Components\Feedback;
use Tests\Browser\Components\Editor;
use Tests\Browser\Components\Autocomplete;
use Tests\Browser\Components\AddContentElement;
use Tests\Browser\Pages\Login;

use App\Models\User;
use App\Models\Course;
use App\Models\CourseList;
use App\Models\CourseDescription;
use App\Models\Tag;
use App\Models\Page;
use App\Models\ContentElement;

class CourseTest extends DuskTestCase
{
    use WithFaker;

    public function test_a_course_can_be_created()
    {
        $this->browse(function ($browser) {
            $user = User::factory()->create();

            $browser->visit(new Login())
                ->loginUser($user)
                ->assertAuthenticatedAs($user)
                ->visitRoute('courses.manage')
                ->assertPathIsNot('/courses-manage');

            $user->addRole('courses-manager');
            $user->refresh();

            $this->assertTrue($user->can('create', Course::class));
            $input = CourseDescription::factory()->raw();
            $tag = Tag::factory()->create();

            $browser->visitRoute('courses.manage')
                ->waitForRoute('courses.manage')
                ->waitFor('@courses-paginator')
                ->waitUntilMissing('@paginator-loading-bar')
                ->pause(1500)
                ->click('@show-create-course')
                ->with('@modal', function ($browser) use ($input) {
                    $browser->waitFor('@form-new-course')
                        ->click('@create-course');
                })
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('name field is required');
                })
                ->with('@modal', function ($browser) use ($input, $tag) {
                    $browser->type('@header', Arr::get($input, 'header'))
                        ->within(new Editor('body', 1), function ($browser) use ($input) {
                            $browser->typeInEditor(Arr::get($input, 'body'));
                        })
                        ->type('@credit', Arr::get($input, 'credit'))
                        ->with(new Autocomplete('tags'), function ($browser) use ($tag) {
                            $browser->searchAndSelectMultiple($tag);
                        })
                        ->click('@create-course');
                })
                ->within(new Feedback(), function ($browser) use ($input) {
                    $browser->assertFeedbackContains(Arr::get($input, 'header').' Saved');
                })
                ->pause(1500);

            $course = Course::all()->last();
            $course_description = $course->contentElements->first()->content;
            $this->assertInstanceOf(CourseDescription::class, $course_description);
            $this->assertEquals(Arr::get($input, 'header'), $course_description->header);
            $this->assertEquals('<p>'.Arr::get($input, 'body').'</p>', $course_description->body);
            $this->assertEquals(Arr::get($input, 'credit'), $course_description->credit);
            $this->assertTrue($course->tags->contains('id', $tag->id));
            $this->assertEquals(Arr::get($input, 'header'), $course->name);

            $this->assertFalse($user->can('publish', $course));

            $slug = $this->getEditingValue($course, 'full_slug');

            $body = $this->faker->paragraph();

            $browser->with('@courses-paginator', function ($browser) use ($course) {
                $browser->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->click('@course-name');
            })
                //->click('@debug')
                ->waitForLocation('/'.$slug, 10)
                ->waitFor('@editing-button')
                ->assertVue('editing', true, '@editing-button')
                ->waitFor('@content-element-'.$course_description->contentElement->id)
                ->pause(500)
                ->within(new Editor('body', $course_description->contentElement->id), function ($browser) use ($body) {
                    $browser->typeInEditor($body);
                })
                ->pause(1500)
                ->within(new Feedback(), function ($browser) use ($course) {
                    $browser->assertFeedbackContains('Course Description Saved');
                })
                ->assertSee('Submit For Publishing');

            $user->addRole('courses-publisher');
            $user->refresh();
            $this->assertTrue($user->can('publish', $course));

            $browser->refresh()
                ->waitFor('@publish-page')
                ->click('@publish-page')
                ->acceptDialog()
                ->pause(1000)
                ->within(new Feedback(), function ($browser) use ($course) {
                    $browser->assertFeedbackContains('Course Published');
                })
                ->click('@editing-button')
                ->waitForReload()
                ->assertSee($course_description->header)
                ->assertSee($body)
                ->assertSee($course_description->credit)
                ->assertSee($tag->name);
        });
    }

    public function test_a_course_can_be_hidden_and_revealed()
    {
        $this->browse(function ($browser, $viewer) {
            $user = User::factory()->create();
            $user->addRole('courses-manager');
            $user->refresh();
            $course = Course::factory()->create();
            $tag = Tag::factory()->create();
            $course->addTag($tag);
            $course_description = $this->createContentElement(CourseDescription::factory(), $course)->content;
            $this->assertInstanceOf(CourseDescription::class, $course_description);
            $this->assertNotNull($course_description->header);

            $course->publish();
            $course->refresh();
            $this->assertFalse($course->version->unlisted);

            $page = Page::factory()->create();
            $user->addRole('pages-editor');
            $user->createPermission('update', $page);
            $user->createPermission('publish', $page);
            $slug = $this->getEditingValue($page, 'full_slug');

            $browser->visit(new Login())
                ->loginUser($user)
                ->assertAuthenticatedAs($user)
                ->visit('/'.$slug.'?editing=true')
                ->waitForLocation('/'.$slug)
                ->waitFor('@editing-button')
                ->assertVue('editing', true, '@editing-button')
                ->waitFor('@content-elements-editor')
                ->with(new AddContentElement(), function ($browser) {
                    $browser->create('course-list');
                });

            $content_element = ContentElement::all()->last();
            $this->assertInstanceOf(CourseList::class, $content_element->content);
            $input = CourseList::factory()->raw();
            $this->assertNotNull(Arr::get($input, 'header'));

            $browser->with('@content-element-'.$content_element->id, function ($browser) use ($content_element, $tag, $input) {
                $browser->waitFor('@header')
                        ->type('@header', Arr::get($input, 'header'))
                        ->click('@select-inline')
                        ->with(new Autocomplete('tags'), function ($browser) use ($tag) {
                            $browser->searchAndSelectMultiple($tag, strlen($tag->search_label));
                        })
                        ->within(new Editor('body', $content_element->id), function ($browser) use ($input) {
                            $browser->typeInEditor(Arr::get($input, 'body'));
                        });
            })
                ->pause(1500) // so that the debounce triggers
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Course List Saved');
                })
                ->click('@debug')
                ->pause(500)
                ->waitFor('@course-description-'.$course_description->id, 10)
                ->with('@course-description-'.$course_description->id, function ($browser) use ($course_description) {
                    $browser->assertValue('@header', $course_description->header);
                })
                ->pause(1500)
                ->click('@publish-page')
                ->acceptDialog()
                ->pause(1000)
                ->click('@editing-button')
                ->waitForReload()
                ->waitFor('@course-description-'.$course_description->id)
                ->assertSee($course_description->header)

                ->visitRoute('courses.manage')
                ->assertPathIs('/courses-manage')
                ->with('@courses-paginator', function ($browser) use ($course) {
                    $browser->waitFor('@paginator-search')
                        ->pause(500)
                        ->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->with('@course-'.$course->id, function ($browser) use ($course) {
                            $browser->waitFor('@course-'.$course->id.'-hide')
                                ->click('@course-'.$course->id.'-hide');
                        });
                })
                ->within(new Feedback(), function ($browser) use ($course) {
                    $browser->assertFeedbackContains('Course Hidden');
                })
                ->pause(2500)
                ->assertMissing('@course-'.$course->id.'-hide')
                ->assertVisible('@course-'.$course->id.'-unhide')

                ->visitRoute('courses.manage')
                ->with('@courses-paginator', function ($browser) use ($course) {
                    $browser->waitFor('@paginator-search')
                        ->pause(500)
                        //->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->with('@course-'.$course->id, function ($browser) use ($course) {
                            $browser->click('@course-'.$course->id.'-publish');
                        });
                })
                ->acceptDialog()
                ->pause(1000)
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('You do not have permission to publish that page');
                });

            $user->addRole('courses-publisher');
            $user->refresh();

            $browser->visitRoute('courses.manage')
                ->with('@courses-paginator', function ($browser) use ($course) {
                    $browser->waitFor('@paginator-search')
                        ->pause(500)
                        //->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->with('@course-'.$course->id, function ($browser) use ($course) {
                            $browser->click('@course-'.$course->id.'-publish');
                        });
                })
                ->acceptDialog()
                ->pause(1000)
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Course Published');
                });

            $viewer->visit('/'.$slug)
                ->waitFor('@content-element-'.$content_element->id)
                ->assertMissing('@course-description-'.$course_description->id);

            $browser->visitRoute('courses.manage')
                ->assertPathIs('/courses-manage')
                ->with('@courses-paginator', function ($browser) use ($course) {
                    $browser->waitFor('@paginator-search')
                        ->pause(500)
                        //->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->with('@course-'.$course->id, function ($browser) use ($course) {
                            $browser->waitFor('@course-'.$course->id.'-unhide')
                                ->click('@course-'.$course->id.'-unhide');
                        });
                })
                ->within(new Feedback(), function ($browser) use ($course) {
                    $browser->assertFeedbackContains('Course Revealed');
                })
                ->pause(1500)
                ->assertMissing('@course-'.$course->id.'-unhide')
                ->assertVisible('@course-'.$course->id.'-hide');

            $viewer->visit('/'.$slug)
                ->waitFor('@content-element-'.$content_element->id)
                ->assertMissing('@course-description-'.$course_description->id);

            $browser->visitRoute('courses.manage')
                ->with('@courses-paginator', function ($browser) use ($course) {
                    $browser->waitFor('@paginator-search')
                        ->pause(500)
                        //->type('@paginator-search', preg_replace('/\d+/u', '', $course->name))
                        ->waitFor('@course-'.$course->id, 10)
                        ->pause(500)
                        ->with('@course-'.$course->id, function ($browser) use ($course) {
                            $browser->waitFor('@course-'.$course->id.'-publish')
                                ->click('@course-'.$course->id.'-publish');
                        });
                })
                ->acceptDialog()
                ->pause(1000)
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Course Published');
                });

            //cache()->tags([cache_name($content_element->content)])->flush();

            $viewer->visit('/'.$slug)
                ->click('@debug')
                ->waitFor('@content-element-'.$content_element->id)
                ->assertVisible('@course-description-'.$course_description->id);
        });
    }
}
