<?php

namespace Tests\Browser;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use Facebook\WebDriver\WebDriverBy;

use Tests\Browser\Pages\Login;
use Tests\Browser\Components\Feedback;
use Tests\Browser\Components\PageEditor;
use Tests\Browser\Components\PageSideMenu;
use Tests\Browser\Components\AddContentElement;
use Tests\Browser\Components\Editor;

use App\Models\Page;
use App\Models\User;
use App\Models\ContentElement;
use App\Models\PhotoBlock;

class PageEditorTest extends DuskTestCase
{
    use WithFaker;

    public function test_a_page_can_save_an_external_redirect_url()
    {
        $this->browse(function ($browser) {
            $page = Page::factory()->create();
            $url = 'https://www.google.com/';
            $user = User::factory()->create();
            $page->createPermission('publish', $user);
            $user->refresh();
            $this->assertTrue($user->can('publish', $page));

            $browser->visit(new Login())
                    ->loginAndEditPage($page, $user)
                    ->click('@debug')
                    ->whenAvailable('@page-editor', function ($browser) use ($url) {
                        $browser->click('@redirect-toggle');
                    })
                    ->with('@modal', function ($browser) use ($url) {
                        $browser->with('@link-menu', function ($browser) use ($url) {
                            $browser->waitFor('@url')
                                ->pause(500)
                                ->type('@url', $url)
                                ->click('@apply-link');
                        });
                    })
                    ->pause(1500) // save debounce
                    ->within(new Feedback(), function ($browser) use ($page) {
                        $browser->assertFeedbackContains($page->name.' Saved');
                    });

            $page->refresh();
            $redirect = $page->getDraftVersion()->redirect;
            $this->assertNotNull($redirect);
            $this->assertEquals($url, $redirect);

            $browser->refresh()
                ->whenAvailable('@page-editor', function ($browser) use ($url) {
                    $browser->waitFor('@redirect-toggle')
                        ->click('@redirect-toggle');
                })
                ->with('@modal', function ($browser) use ($url) {
                    $browser->with('@link-menu', function ($browser) use ($url) {
                        $browser->waitFor('@url')
                                ->pause(500)
                                ->assertInputValue('@url', $url);
                    })
                    ->click('@close-modal')
                    ->pause(500);
                })
            ->click('@publish-page')
            ->acceptDialog()
            ->within(new Feedback(), function ($browser) {
                $browser->assertFeedbackContains('Page Published');
            })
            ->visit($this->community)
            ->waitFor('@user-menu')
            ->click('@user-menu')
            ->pause(500)
            ->waitFor('@logout-button')
            ->click('@logout-button')
            ->within(new Feedback(), function ($browser) {
                $browser->assertFeedbackContains('Logout Complete');
            });

            $page->refresh();

            $browser->visit($page->full_slug)
                ->waitForLocation($url)
                ->assertUrlIs($url);
        });
    }

    public function test_a_page_can_save_an_internal_redirect_url()
    {
        $this->browse(function ($browser) {
            $page = Page::factory()->create();
            $redirect_page = Page::factory()->create();
            $redirect_page->publish();
            $user = User::factory()->create();
            $page->createPermission('publish', $user);
            $user->refresh();
            $this->assertTrue($user->can('publish', $page));

            $browser->visit(new Login())
                    ->loginAndEditPage($page, $user)
                    ->whenAvailable('@page-editor', function ($browser) {
                        $browser->click('@redirect-toggle');
                    })
                    ->with('@modal', function ($browser) use ($redirect_page) {
                        $browser->with('@link-menu', function ($browser) use ($redirect_page) {
                            $browser->waitFor('@page-list-'.$redirect_page->id, 20)
                                    ->pause(500)
                                    ->click('@page-list-'.$redirect_page->id);
                        });
                    })
                    ->pause(1500) // save debounce
                    ->within(new Feedback(), function ($browser) use ($page) {
                        $browser->assertFeedbackContains($page->name.' Saved');
                    });

            $this->assertNotNull($page->getDraftVersion()->redirect);
            $this->assertEquals($redirect_page->id, $this->getEditingValue($page->getDraftVersion(), 'redirect'));

            $browser->refresh()
                ->whenAvailable('@page-editor', function ($browser) {
                    $browser->click('@redirect-toggle');
                })
                ->with('@modal', function ($browser) use ($redirect_page) {
                    $browser->with('@link-menu', function ($browser) use ($redirect_page) {
                        $browser->waitFor('@url')
                                ->pause(500)
                                ->assertInputValue('@url', $redirect_page->full_slug);
                    })
                    ->click('@close-modal')
                    ->pause(500);
                })
                ->click('@publish-page')
                ->acceptDialog()
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Page Published');
                })
                ->visit($this->community)
                ->waitFor('@user-menu')
                ->click('@user-menu')
                ->pause(500)
                ->waitFor('@logout-button')
                ->click('@logout-button')
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Logout Complete');
                });

            $page->refresh();

            $browser->visit($page->full_slug)
                ->waitFor('@debug')
                ->click('@debug')
                ->waitForLocation('/'.$redirect_page->full_slug);
        });
    }

    public function test_a_page_can_be_edited()
    {
        $this->browse(function ($browser) {
            $user = User::factory()->create();
            $password = Str::random(8);
            $user->password = bcrypt($password);
            $user->save();

            $page = Page::factory()->create();

            $slug = '/'.$this->getEditingValue($page, 'full_slug');

            $logged_in = count($browser->driver->findElements(WebDriverBy::id('user-menu')));

            if ($logged_in) {
                $browser->refresh()
                        ->waitFor('@user-menu')
                        ->click('@user-menu')
                        ->pause(250)
                        ->clickAndWaitForReload('@logout-button');
            }

            $logged_in = count($browser->driver->findElements(WebDriverBy::id('user-menu')));

            if ($logged_in) {
                $browser->click('@user-menu')
                        ->pause(250)
                        ->clickAndWaitForReload('@logout-button');
            }

            $browser->visit('/login?url=/hub')
                ->pause(500)
                ->waitFor('@login-email')
                ->type('@login-email', $user->email)
                ->type('@login-password', $password)
                ->click('@submit-login-button')
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Login Successful');
                })
                ->pause(1000)
                ->assertMissing('@editing-button');

            $user->addRole('pages-editor');
            $community = Page::whereHas('versions', function ($query) {
                $query->where('name', 'Hub');
            })->first();
            $user->createPermission('update', $community);
            $user->refresh();

            $browser->refresh()
                ->waitFor('@editing-button')
                ->click('@editing-button')
                ->within(new Feedback(), function ($browser) {
                    $browser->assertFeedbackContains('Editing Enabled');
                })
                ->assertTitleContains('Editing')
                ->assertVue('editing', true, '@editing-button')
                ->within(new PageSideMenu(), function ($browser) use ($page) {
                    $browser->loadPage($page);
                })
                ->waitForLocation($slug)
                ->assertMissing('@page-editor');

            $user->createPermission('update', $page);

            $browser->refresh()
                ->waitFor('@page-editor')
                ->assertVisible('@content-elements-editor');
        });
    }

    public function test_a_page_can_be_previewed()
    {
        $this->browse(function ($browser) {
            $page = Page::factory()->create();
            $browser->visit(new Login())
                    ->loginAndEditPage($page)
                    ->waitFor('@page-editor')
                    ->with(new AddContentElement(), function ($browser) {
                        $browser->create('photo-block');
                    });

            $content_element = ContentElement::all()->last();
            $this->assertEquals('photo-block', $content_element->type);
            $input = PhotoBlock::factory()->withText()->raw();

            $browser->with('@content-element-'.$content_element->id, function ($browser) use ($input, $content_element) {
                $browser->click('@toggle-text')
                        ->waitFor('@header')
                        ->type('@header', Arr::get($input, 'header'))
                        ->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('Photo Block Saved');
            })
            ->with('@page-editor', function ($browser) {
                $browser->click('@preview-page');
            });

            $new_tab = collect($browser->driver->getWindowHandles())->last();
            $browser->driver->switchTo()->window($new_tab);

            $full_slug = '/'.$this->getEditingValue($page, 'full_slug');

            $browser->waitForLocation($full_slug)
                    ->pause(500)
                    ->assertTitleContains('Preview')
                    ->waitFor('@content-element-'.$content_element->id)
                    ->assertSee($content_element->content->header)
                    ->assertSourceHas($content_element->content->body);
        });
    }

    public function test_a_page_can_create_a_signed_url()
    {
        $this->browse(function ($browser) {
            $page = Page::factory()->create();
            $this->assertFalse($this->getEditingValue($page->getDraftVersion(), 'signed_url'));
            $user = User::factory()->create();

            $browser->visit(new Login())
                ->loginAndEditPage($page, $user)
                ->whenAvailable('@page-editor', function ($browser) {
                    $browser->click('@toggle-signed-url');
                })
                ->pause(1500)
                ->within(new Feedback(), function ($browser) use ($page) {
                    $browser->assertFeedbackContains($page->name.' Saved');
                });

            $page->refresh();
            $this->assertTrue($this->getEditingValue($page->getDraftVersion(), 'signed_url'));

            $signed_url = $this->getEditingValue($page, 'signed_url');
            $this->assertNotNull($signed_url);

            $browser->with('@page-editor', function ($browser) use ($signed_url) {
                $browser->assertVue('page.version.signed_url', 1)
                    ->waitFor('@signed-url-clipboard')
                    ->assertVue('signedUrl', $signed_url)
                    ->click('@signed-url-clipboard');
            })
            ->within(new Feedback(), function ($browser) use ($page) {
                $browser->assertFeedbackContains('Copied to clipboard');
            });
        });
    }

    public function test_a_page_is_loaded_in_the_editor_when_there_is_no_previous_page()
    {
        $this->browse(function ($browser) {
            $user = User::factory()->create();
            $user->addRole('admin');
            $page = Page::factory()->create();
            $slug = $this->getEditingValue($page, 'full_slug');

            $browser->visit(new Login())
                    ->loginAndEditPage($page, $user)
                    ->waitFor('@editing-button')
                    ->assertVue('editing', true, '@editing-button')
                    ->visitRoute('blogs.manage')
                    ->waitFor('@page-side-menu')
                    ->pause(500)
                    ->within(new PageSideMenu(), function ($browser) use ($page) {
                        $browser->loadPage($page);
                    })
                    ->waitForLocation('/'.$slug);
        });
    }

    public function test_the_page_editor_is_visible_when_editing_but_not_in_preivew()
    {
        $this->browse(function ($browser) {
            $page = Page::factory()->create();

            $browser->visit(new Login())
                ->loginAndEditPage($page)
                ->assertVisible('@page-editor')
                ->within(new PageEditor(), function ($browser) use ($page) {
                    $browser->previewPage($page);
                })
                ->assertMissing('@page-editor')
                ->click('@close-preview');
        });
    }
}
