<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use Carbon\Carbon;

use App\Models\Page;
use App\Models\ContentElement;
use App\Models\TextBlock;
use App\Models\Version;
use App\Models\FileUpload;
use App\Models\Role;
use App\Models\User;
use App\Models\Photo;
use App\Models\Tag;
use App\Utilities\Menu;
use App\Models\PhotoBlock;

use App\Events\PageDraftCreated;
use App\Mail\PublishingCompleted;

use Tests\Unit\AppendAttributesTestTrait;
use Tests\Unit\ContentElementsTestTrait;
use Tests\Unit\VersioningTestTrait;
use Tests\Unit\TagsTrait;
use Tests\Unit\PermissionsTestTrait;

class PageTest extends TestCase
{
    use WithFaker;
    use AppendAttributesTestTrait;
    use ContentElementsTestTrait;
    use VersioningTestTrait;
    use TagsTrait;
    use PermissionsTestTrait;

    protected function getClassname()
    {
        return 'page';
    }

    public function test_a_page_has_a_parent_through_a_version()
    {
        $first_level_page = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $version = $second_level_page->getDraftVersion();
        $version->parent_page_id = $first_level_page->id;
        $version->save();
        $first_level_page->publish();
        $second_level_page->publish();

        //dump($first_level_page->id);
        //dump($second_level_page->id);
        //
        $this->assertNotNull($second_level_page->parentPage);
        $this->assertInstanceOf(Page::class, $second_level_page->parentPage);
        $this->assertEquals($first_level_page->id, $second_level_page->parentPage->id);

        $this->assertNotNull($first_level_page->parentPage);
        $this->assertNotNull($second_level_page->parentPage);
        $this->assertEquals($first_level_page->id, $second_level_page->parentPage->id);

        $this->assertNotNull($first_level_page->getPages());
        $this->assertTrue($first_level_page->getPages()->contains('id', $second_level_page->id));

        $this->assertNotNull($first_level_page->pages);
        $this->assertTrue($first_level_page->pages->contains('id', $second_level_page->id));

        $this->signInAdmin();
        $this->enableEditing();

        $other_parent = Page::factory()->create();
        $draft = $second_level_page->getDraftVersion();
        $draft->parent_page_id = $other_parent->id;
        $draft->save();

        $second_level_page->refresh();
        $this->assertEquals($other_parent->id, $second_level_page->parentPage->id);

        $second_level_page->publish();
        $second_level_page->refresh();

        $this->assertEquals($other_parent->id, $second_level_page->parentPage->id);
    }

    public function test_a_parent_page_only_returns_current_versions_for_its_sub_pages()
    {
        // move the page from one parent to another
        // it should only be the sub page for one parent at a time
        $first_level_page = Page::factory()->create();
        $first_level_page2 = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $version = $second_level_page->getDraftVersion();
        $version->parent_page_id = $first_level_page->id;
        $version->save();
        $first_level_page->publish();
        $second_level_page->publish();

        $this->assertEquals(1, $first_level_page->getPages()->count());
        $this->assertEquals(0, $first_level_page2->getPages()->count());

        $this->signInAdmin();
        $this->enableEditing();

        $input = Version::factory()->page()->raw([
            'parent_page_id' => $first_level_page2->id,
        ]);
        $second_level_page->saveVersion(['version' => $input]);
        $draft = $second_level_page->getDraftVersion();

        $this->assertEquals(2, $second_level_page->versions()->count());

        $first_level_page->refresh();
        $first_level_page2->refresh();

        $this->assertEquals(1, $first_level_page2->getPages()->count());
        $this->assertEquals(0, $first_level_page->getPages()->count());

        auth()->logout();

        $second_level_page->refresh();
        $first_level_page->refresh();
        $first_level_page2->refresh();

        $this->assertEquals(1, $first_level_page->getPages()->count());
        $this->assertEquals(0, $first_level_page2->getPages()->count());

        $second_level_page->publish();
        $second_level_page->refresh();
        $first_level_page->refresh();
        $first_level_page2->refresh();

        $this->assertEquals($draft->id, $second_level_page->published_version_id);
        $this->assertEquals($second_level_page->published_version_id, $second_level_page->version->id);
        $this->assertEquals(1, $first_level_page2->getPages()->count());
        $this->assertEquals(0, $first_level_page->getPages()->count());
    }

    public function test_the_page_tree_can_be_created()
    {
        $first_level_page = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $version = $second_level_page->getDraftVersion();
        $version->parent_page_id = $first_level_page->id;
        $version->save();
        $first_level_page->publish();
        $second_level_page->publish();

        $home_page = Page::find(1);
        $this->assertTrue($home_page->versions->count() > 0);
        $this->assertNotNull($home_page->version);
        $this->assertInstanceOf(Page::class, $home_page);
        cache()->tags([cache_name($home_page)])->flush();
        $first_level = $home_page->getPages();
        $this->assertTrue($first_level->contains('id', $first_level_page->id));
        cache()->tags([cache_name($first_level_page)])->flush();
        $second_level = $first_level_page->getPages();
        $this->assertTrue($second_level->contains('id', $second_level_page->id));
    }

    public function test_a_page_has_a_default_slug_attribute()
    {
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->name = 'Foo Bar Baz';
        $version->save();
        $page->publish();

        $this->assertNotNull($page->getSlug());
        $this->assertEquals('foo-bar-baz', $page->getSlug());

        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->name = 'Grade 10';
        $version->save();
        $page->publish();

        $this->assertNotNull($page->getSlug());
        $this->assertEquals('grade-10', $page->getSlug());
    }

    public function test_a_page_has_a_full_slug_attribute()
    {
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->name = 'Rock N Roll';
        $version->save();

        $page2 = Page::factory()->create();
        $version = $page2->versions->first();
        $version->parent_page_id = $page->id;
        $version->name = 'Led Zeppelin';
        $version->save();

        $page3 = Page::factory()->create();
        $version = $page3->versions->first();
        $version->parent_page_id = $page2->id;
        $version->name = 'Jimmy Page';
        $version->save();

        $page->publish();
        $page2->publish();
        $page3->publish();

        $this->assertNotNull($page3->parentPage);
        $this->assertEquals('rock-n-roll/led-zeppelin/jimmy-page', $page3->full_slug);
    }

    public function test_a_page_can_be_found_by_its_full_slug()
    {
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->save();

        $page2 = Page::factory()->create();
        $version = $page2->versions->first();
        $version->parent_page_id = $page->id;
        $version->save();

        $page3 = Page::factory()->create();
        $version = $page3->versions->first();
        $version->parent_page_id = $page2->id;
        $version->save();

        $page->publish();
        $page2->publish();
        $page3->publish();

        $page_slug = $page3->full_slug;

        $found_page = (new Page())->findByFullSlug($page3->full_slug);

        $this->assertInstanceOf(Page::class, $found_page);
        $this->assertEquals($page3->id, $found_page->id);
    }

    public function test_a_page_can_return_its_slug()
    {
        $slug = $this->faker->name;
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->slug = $slug;
        $version->save();

        $page->publish();

        $this->assertEquals($slug, $page->version->slug);
        //$this->assertEquals($slug, $page->slug);
    }

    public function test_a_page_can_be_hidden_from_the_menu()
    {
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->unlisted = 0;
        $version->save();
        $page->publish();

        cache()->tags(['nav-menu'])->flush();
        $this->assertTrue(collect(Menu::getMenu())->contains('id', $page->id));

        $version->unlisted = true;
        $version->save();
        cache()->tags([cache_name($page)])->flush();
        cache()->tags(['nav-menu'])->flush();
        $this->assertEquals(1, $page->version->unlisted);

        $this->assertFalse(collect(Menu::getMenu())->flatten()->contains('id', $page->id));
    }

    public function test_if_user_editing_preview_content_elements_are_appended()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $this->assertEquals(1, $content_element->pages()->count());
        $page = $content_element->pages->first();

        //$this->assertFalse($page->preview_content_elements->contains('id', $content_element->id));
        $this->signInAdmin();
        $this->enableEditing();
        request()->merge(['preview' => true]);
        $this->assertTrue($page->preview_content_elements->contains('id', $content_element->id));
    }

    public function test_a_page_can_have_a_footer_fg()
    {
        Storage::fake();
        $page = Page::factory()->create();
        $version = $page->versions->first();

        $fg_file_name = Str::random().'.jpg';
        $fg_file = UploadedFile::fake()->image($fg_file_name);
        $fg_file_upload = (new FileUpload())->saveFile($fg_file, 'photos', true);
        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $fg_file_upload->id;
        $photo = (new Photo())->savePhoto($input, null, $version);

        $this->assertInstanceOf(Photo::class, $photo);

        $version->footer_fg_photo_id = $photo->id;
        $version->save();
        $page->publish();

        $this->assertNotNull($page->footerFgPhoto);
        $page->footerFgPhoto->createImage(100, 'large');
        $this->assertInstanceOf(Photo::class, $page->footerFgPhoto);
        $this->assertTrue(strpos($page->footerFgPhoto->large, $fg_file_upload->filename) > 0);
        Storage::disk('public')->assertExists($page->footerFgPhoto->large);
    }

    public function test_a_page_can_have_a_footer_bg()
    {
        Storage::fake();
        $page = Page::factory()->create();
        $version = $page->versions->first();

        $bg_file_name = Str::random().'.jpg';
        $bg_file = UploadedFile::fake()->image($bg_file_name);
        $bg_file_upload = (new FileUpload())->saveFile($bg_file, 'photos', true);
        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $bg_file_upload->id;
        $photo = (new Photo())->savePhoto($input, null, $version);

        $this->assertInstanceOf(Photo::class, $photo);

        $version->footer_bg_photo_id = $photo->id;
        $version->save();
        $page->publish();

        $this->assertNotNull($page->footerBgPhoto);
        $page->footerBgPhoto->createImage(100, 'large');
        $this->assertInstanceOf(Photo::class, $page->footerBgPhoto);
        $this->assertTrue(strpos($page->footerBgPhoto->large, $bg_file_upload->filename) > 0);
        Storage::disk('public')->assertExists($page->footerBgPhoto->large);
    }

    /* we are ditching inheritance here as it is slow and using the home page footer as a default
    public function a_page_has_footer_images_and_a_footer_color_that_can_be_inherited()
    {
        Storage::fake();
        $fg_file_name = Str::random().'.jpg';
        $fg_file = UploadedFile::fake()->image($fg_file_name);
        $fg_file_upload = (new FileUpload())->saveFile($fg_file, 'photos', true);
        $bg_file_name = Str::random().'.jpg';
        $bg_file = UploadedFile::fake()->image($bg_file_name);
        $bg_file_upload = (new FileUpload())->saveFile($bg_file, 'photos', true);
        $footer_color = $this->faker->hexcolor;

        $home_page = Page::find(1);
        $version = $home_page->version;

        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $fg_file_upload->id;
        $fg_photo = (new Photo())->savePhoto($input, null, $version);

        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $bg_file_upload->id;
        $bg_photo = (new Photo())->savePhoto($input, null, $version);

        $version->footer_fg_photo_id = $fg_photo->id;
        $version->footer_bg_photo_id = $bg_photo->id;
        $version->footer_color = $footer_color;
        $version->save();

        cache()->tags([cache_name($home_page)])->flush();
        $version->refresh();

        $page = Page::factory()->create();
        $page_version = $page->versions->first();
        $page->publish();
        $this->assertEquals($home_page->id, $page->version->parent_page_id);

        // make sure that the first level page inherits from the home page
        $this->assertEquals($fg_file_upload->id, $page->footer_fg_photo->fileUpload->id);
        $this->assertEquals($bg_file_upload->id, $page->footer_bg_photo->fileUpload->id);
        $this->assertEquals($footer_color, $page->footer_color);

        // create another batch of pics and color for a sub page
        $fg_file_name2 = Str::random().'.jpg';
        $fg_file2 = UploadedFile::fake()->image($fg_file_name2);
        $fg_file_upload2 = (new FileUpload())->saveFile($fg_file2, 'photos', true);
        $bg_file_name2 = Str::random().'.jpg';
        $bg_file2 = UploadedFile::fake()->image($bg_file_name2);
        $bg_file_upload2 = (new FileUpload())->saveFile($bg_file2, 'photos', true);
        $footer_color2 = $this->faker->hexcolor;

        $sub_page = Page::factory()->create();
        $sub_version = $sub_page->versions->first();
        $sub_version->parent_page_id = $page->id;
        $sub_version->save();
        $sub_page->publish();

        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $fg_file_upload2->id;
        $fg_photo2 = (new Photo())->savePhoto($input, null, $page_version);

        $input = Photo::factory()->raw();
        $input['file_upload_id'] = $bg_file_upload2->id;
        $bg_photo2 = (new Photo())->savePhoto($input, null, $page_version);

        $this->assertEquals($fg_file_upload->id, $sub_page->footer_fg_photo->fileUpload->id);
        $this->assertEquals($bg_file_upload->id, $sub_page->footer_bg_photo->fileUpload->id);
        $this->assertEquals($footer_color, $sub_page->footer_color);

        $page_version->footer_fg_photo_id = $fg_photo2->id;
        $page_version->footer_bg_photo_id = $bg_photo2->id;
        $page_version->footer_color = $footer_color2;
        $page_version->save();
        $page->refresh();
        $sub_page->refresh();

        cache()->tags([cache_name($sub_page)])->flush();
        cache()->tags([cache_name($page)])->flush();
        cache()->tags([cache_name($sub_version)])->flush();

        $this->assertEquals($fg_file_upload2->id, $sub_page->footer_fg_photo->fileUpload->id);
        $this->assertEquals($bg_file_upload2->id, $sub_page->footer_bg_photo->fileUpload->id);
        $this->assertEquals($footer_color2, $sub_page->footer_color);
    }
     */


    public function test_a_page_has_a_sub_menu()
    {
        $page = Page::factory()->create();
        $parent_version = $page->versions->first();
        $parent_version->show_sub_menu = true;
        $parent_version->save();
        $sub_page = Page::factory()->create();
        $version = $sub_page->versions->first();
        $version->parent_page_id = $page->id;
        $version->unlisted = 0;
        $version->save();
        $page->publish();

        $this->assertFalse($page->sub_menu->contains('name', $sub_page->name));
        $this->assertFalse($sub_page->visible_sub_menu);

        $sub_page->publish();

        cache()->tags([cache_name($page)])->flush();

        $this->assertInstanceOf(Collection::class, $page->sub_menu);
        $this->assertTrue($page->sub_menu->contains('name', $sub_page->name));
        $sub_page->refresh();
        $this->assertEquals($page->id, $sub_page->parentPage->id);
        $this->assertTrue($page->version->show_sub_menu);
        $this->assertTrue($sub_page->visible_sub_menu);

        $version->unlisted = true;
        $version->save();

        cache()->tags([cache_name($page)])->flush();
        cache()->tags([cache_name($sub_page)])->flush();
        $this->assertFalse($page->sub_menu->contains('name', $sub_page->name));
        $sub_page->refresh();
        $this->assertFalse($sub_page->visible_sub_menu);
    }

    /* TODO we may want to try this again after cleaning up all the appends and with properties on objects
    public function a_page_can_recursively_append_attributes()
    {
        $page1 = Page::factory()->create();

        $page2 = Page::factory()->create();
        $version = $page2->versions->first();
        $version->parent_page_id = $page1->id;
        $version->save();

        $page3 = Page::factory()->create();
        $version = $page3->versions->first();
        $version->parent_page_id = $page2->id;
        $version->save();

        $page1->publish();
        $page2->publish();
        $page3->publish();

        $page1->refresh();
        $page2->refresh();
        $page3->refresh();

        //dump('1:'.$page1->id);
        //dump('2:'.$page2->id);
        //dump('3:'.$page3->id);

        $page1->appendRecursive(['full_slug']);
        $page_array = $page1->toArray();

        $this->assertNotNull(Arr::get($page_array, 'full_slug'));
        $this->assertEquals($page1->full_slug, Arr::get($page_array, 'full_slug'));

        $this->assertNotNull(Arr::get($page_array, 'pages'));
        $this->assertNotNull(Arr::get($page_array['pages'][0], 'full_slug'));
        $this->assertEquals($page2->full_slug, Arr::get($page_array['pages'][0], 'full_slug'));

        $this->assertNotNull(Arr::get($page_array['pages'][0]['pages'][0], 'full_slug'));
        $this->assertEquals($page3->full_slug, Arr::get($page_array['pages'][0]['pages'][0], 'full_slug'));
    }
     */

    public function test_a_pages_full_slug_removes_any_non_word_characters()
    {
        $unsanitized_name = 'FOOBAR, baz, "Foo"';

        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->name = $unsanitized_name;
        $version->save();
        $page->publish();

        $this->assertEquals('foobar-baz-foo', $page->full_slug);
    }

    public function test_a_page_has_a_footer_text_color()
    {
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->footer_color = '50,50,50';
        $version->save();
        $page->publish();

        $page->refresh();

        $this->assertNotNull($page->footer_text_color);
        $this->assertEquals('text-gray-200', $page->footer_text_color);
    }

    /*
    public function loading_a_page_includes_its_tags()
    {
        $page = Page::factory()->create();
        $tag = Tag::factory()->create();

        $page->addTag($tag);

        $page->refresh();
        $this->assertTrue($page->tags->contains('id', $tag->id));

        $data = Page::find($page->id)->toArray();

        $this->assertNotNull(Arr::get($data, 'tags'));
    }
     */

    public function test_slugs_can_have_dashes()
    {
        $parent = Page::factory()->create();
        $version = $parent->versions->first();
        $version->slug = 'parent-page';
        $version->save();
        $parent->publish();

        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->slug = 'full-slug-page';
        $version->parent_page_id = $parent->id;
        $version->save();
        $page->publish();

        $this->assertEquals($page->full_slug, 'parent-page/full-slug-page');
    }

    public function test_the_home_page_can_be_found()
    {
        $home_page = Page::getHomePage();
        $this->assertInstanceOf(Page::class, $home_page);
        $this->assertEquals(1, $home_page->id);
    }

    public function test_a_page_can_get_its_permissions_from_its_parent()
    {
        $user = User::factory()->create();
        $home_page = Page::find(1);
        $parent_page = Page::factory()->create();
        $parent_page->publish();

        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->save();
        $page->publish();

        $sub_page = Page::factory()->create();
        $version = $sub_page->versions->first();
        $version->parent_page_id = $page->id;
        $version->save();
        $sub_page->publish();

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

        $this->assertTrue($user->can('update', $parent_page));
        $this->assertTrue($user->can('update', $page));
        $this->assertTrue($user->can('update', $sub_page));
        $this->assertFalse($user->can('update', $home_page));
    }

    public function test_pages_are_sorted_properly_when_sorted_over_more_than_one_digit()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $parent_page = Page::factory()->create();

        $page1 = Page::factory()->create();
        $version = $page1->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 1;
        $version->save();
        $page1->publish();

        $page2 = Page::factory()->create();
        $version = $page2->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 2;
        $version->save();
        $page2->publish();

        $page3 = Page::factory()->create();
        $version = $page3->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 10;
        $version->save();
        $page3->publish();

        (new Page())->sortPages($page1, [
            'parent_page_id' => $parent_page->id,
            'sort_order' => 1,
        ]);

        $page1->refresh();
        $page2->refresh();
        $page3->refresh();

        $this->assertNotNull($page1->getDraftVersion()->sort_order);

        $this->assertEquals(1, $page1->getDraftVersion()->sort_order);
        $this->assertEquals(2, $page2->getDraftVersion()->sort_order);
        $this->assertEquals(3, $page3->getDraftVersion()->sort_order);
    }


    public function test_a_page_can_get_its_breadcrumbs()
    {
        $parent = Page::factory()->create();
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->parent_page_id = $parent->id;
        $version->save();

        $child = Page::factory()->create();
        $version = $child->versions->first();
        $version->parent_page_id = $page->id;
        $version->save();

        $parent->publish();
        $page->publish();
        $child->publish();

        $this->assertNotNull($child->getBreadcrumbs());
        $breadcrumbs = $child->getBreadcrumbs();

        $this->assertEquals($breadcrumbs[0]->name, $parent->version->title ?? $parent->version->name);
        $this->assertEquals($breadcrumbs[1]->name, $page->version->title ?? $page->version->name);
        $this->assertEquals($breadcrumbs[2]->name, $child->version->title ?? $child->version->name);
    }


    public function test_a_full_slug_cache_is_cleared_properly()
    {
        $page1 = Page::factory()->create();
        $content_element1 = $this->createContentElement(TextBlock::factory(), $page1);

        $page2 = Page::factory()->create();
        $content_element2 = $this->createContentElement(TextBlock::factory(), $page2);

        $page1->publish();
        $page2->publish();

        $this->signInAdmin();


        $old_name = $page1->version->name;

        $this->assertNotNull($old_name);

        cache()->flush();

        $foo = (new Page())->findByFullSlug($page1->full_slug);

        $this->assertInstanceOf(Page::class, $foo);
        $this->assertEquals($page1->id, $foo->id);

        $this->enableEditing();

        $page1->load('version');
        $input = $page1->toArray();
        $this->assertNotNull(Arr::get($input, 'version.name'));
        $new_name = Str::random();
        $input['version']['name'] = $new_name;

        $this->json('POST', route('pages.update', ['id' => $page1->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $page1->refresh();

        $this->assertEquals($page1->version->name, $new_name);

        // set page2 to the old page1 name and see if the full slug cache was cleared

        $page2->load('version');
        $input = $page2->toArray();
        $this->assertNotNull(Arr::get($input, 'version.name'));
        $input['version']['name'] = $old_name;

        $this->json('POST', route('pages.update', ['id' => $page2->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $page2->refresh();

        $this->assertEquals($page2->version->name, $old_name);

        $page_check = (new Page())->findByFullSlug($page2->full_slug);

        $this->assertEquals($page2->id, $page_check->id);
    }

    public function test_a_page_can_flush_its_slug_cache_heiarchy()
    {
        $page = Page::factory()->create();

        $page2 = Page::factory()->create();
        $version = $page2->versions->first();
        $version->parent_page_id = $page->id;
        $version->save();

        $page3 = Page::factory()->create();
        $version1 = $page3->versions->first();
        $version1->parent_page_id = $page2->id;
        $version1->save();

        $page4 = Page::factory()->create();
        $version2 = $page4->versions->first();
        $version2->parent_page_id = $page3->id;
        $version2->save();

        $page->publish();
        $page2->publish();
        $page3->publish();
        $page4->publish();

        $slug1 = $page->full_slug;
        $slug2 = $page2->full_slug;
        $slug3 = $page3->full_slug;
        $slug4 = $page4->full_slug;

        $this->assertNotNull($slug1);
        $this->assertNotNull($slug2);
        $this->assertNotNull($slug3);
        $this->assertNotNull($slug4);

        $new_slug = 'foobar'.rand(100, 999);
        $version->name = $new_slug;
        $version->save();

        $page2->clearSlugCache();

        $page->refresh();
        $page2->refresh();
        $page3->refresh();
        $page4->refresh();

        $this->assertEquals($slug1, $page->full_slug);
        $this->assertNotEquals($slug2, $page2->full_slug);
        $this->assertNotEquals($slug3, $page3->full_slug);
        $this->assertNotEquals($slug4, $page4->full_slug);

        $this->assertEquals($slug1.'/'.$new_slug, $page2->full_slug);
        $this->assertTrue(Str::contains($page3->full_slug, $new_slug));
        $this->assertTrue(Str::contains($page4->full_slug, $new_slug));
    }

    public function test_a_page_has_a_sub_pages_count_attribute()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $page = Page::factory()->create();

        $sub_page1 = Page::factory()->create();
        $version1 = $sub_page1->versions()->first();
        $version1->parent_page_id = $page->id;
        $version1->save();

        $sub_page2 = Page::factory()->create();
        $version2 = $sub_page2->versions()->first();
        $version2->parent_page_id = $page->id;
        $version2->save();

        $page->refresh();

        $this->assertEquals(2, $page->getPages()->count());
        $this->assertNotNull($page->pages_count);
        $this->assertEquals(2, $page->pages_count);
    }

    public function test_a_page_has_a_parent_page_ids_attribute()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $page = Page::factory()->create();

        $sub_page1 = Page::factory()->create();
        $version1 = $sub_page1->versions()->first();
        $version1->parent_page_id = $page->id;
        $version1->save();

        $sub_page2 = Page::factory()->create();
        $version2 = $sub_page2->versions()->first();
        $version2->parent_page_id = $sub_page1->id;
        $version2->save();

        $page->refresh();

        $this->assertNotNull($sub_page2->parent_page_ids);
        $this->assertEquals(collect([$page->id, $sub_page1->id]), $sub_page2->parent_page_ids);
    }

    public function test_a_page_has_a_create_sub_page_action()
    {
        $page = Page::factory()->create();
        $user = User::factory()->create();
        $this->assertFalse($user->can('createSubPages', $page));
        $user->createPermission('update', $page);
        $user->refresh();
        $page->refresh();
        $this->assertTrue($user->can('createSubPages', $page));

        cache()->tags([cache_name($user)])->flush();

        $this->signIn($user);

        $actions = $page->actions->toArray();
        $this->assertTrue(Arr::get($actions, 'createSubPages'));
    }


    public function test_a_page_can_be_found_by_its_slug()
    {
        $slug = $this->faker->word();
        $page = Page::factory()->create();
        $version = $page->getDraftVersion();
        $version->slug = $slug;
        $version->save();
        $page->publish();

        $found_page = (new Page())->findByFullSlug($slug);
        $this->assertEquals($page->id, $found_page->id);
    }

    public function test_a_page_has_a_version_if_we_are_editing_a_brand_new_page(): void
    {
        $page = Page::factory()->create();

        $this->signInAdmin();
        $this->enableEditing();

        $this->assertNotNull($page->version);
    }

    public function test_a_page_can_get_its_previous_and_next_pages_for_linking_nav(): void
    {
        $this->signInAdmin();
        $this->enableEditing();

        $parent = Page::factory()->create();
        $version = $parent->getDraftVersion();

        $page1 = Page::factory()->create();
        $version1 = $page1->getDraftVersion();
        $version1->parent_page_id = $parent->id;
        $version1->sort_order = 1;
        $version1->unlisted = false;
        $version1->save();
        $page1->refresh();

        $page2 = Page::factory()->create();
        $version2 = $page2->getDraftVersion();
        $version2->parent_page_id = $parent->id;
        $version2->sort_order = 2;
        $version2->unlisted = false;
        $version2->save();
        $page2->refresh();

        $page3 = Page::factory()->create();
        $version3 = $page3->getDraftVersion();
        $version3->parent_page_id = $parent->id;
        $version3->sort_order = 3;
        $version3->unlisted = false;
        $version3->save();
        $page3->refresh();

        $page4 = Page::factory()->create();
        $version4 = $page4->getDraftVersion();
        $version4->parent_page_id = $parent->id;
        $version4->sort_order = 4;
        $version4->unlisted = true;
        $version4->save();
        $page4->refresh();

        $page5 = Page::factory()->create();
        $version5 = $page5->getDraftVersion();
        $version5->parent_page_id = $parent->id;
        $version5->sort_order = 5;
        $version5->unlisted = false;
        $version5->save();
        $page5->refresh();

        $parent->refresh();

        $this->assertEquals(5, $parent->getPages()->count());
        $this->assertEquals($parent->id, $page1->version->parent_page_id);

        $this->assertFalse($page1->version->unlisted);
        $this->assertFalse($page2->version->unlisted);
        $this->assertFalse($page3->version->unlisted);
        $this->assertTrue($page4->version->unlisted);
        $this->assertFalse($page5->version->unlisted);

        $this->assertEquals($page1->parentPage->id, $parent->id);
        $this->assertEquals($page2->parentPage->id, $parent->id);
        $this->assertEquals($page3->parentPage->id, $parent->id);
        $this->assertEquals($page4->parentPage->id, $parent->id);
        $this->assertEquals($page5->parentPage->id, $parent->id);
        $this->assertEquals(4, $parent->sub_menu->count());
        $this->assertEquals(4, $page2->parentPage->sub_menu->count());
        $this->assertNotNull($page3->previous_page);
        $this->assertNotNull($page3->next_page);

        $this->assertInstanceOf(Page::class, $page3->previous_page);
        $this->assertInstanceOf(Page::class, $page3->next_page);

        $this->assertEquals($page2->id, $page3->previous_page->id);
        $this->assertEquals($page5->id, $page3->next_page->id);

        $this->assertNotNull($page1->next_page2);
        $this->assertEquals($page3->id, $page1->next_page2->id);

        $this->assertNotNull($page5->previous_page2);
        $this->assertEquals($page2->id, $page5->previous_page2->id);
    }

    public function test_a_page_can_have_a_photo_link(): void
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'));
        $photo_block = $content_element->content;
        $photo = $photo_block->photos()->first();
        $page = $content_element->pages->first();

        $this->assertInstanceOf(ContentElement::class, $content_element);
        $this->assertInstanceOf(PhotoBlock::class, $photo_block);
        $this->assertInstanceOf(Photo::class, $photo);
        $this->assertInstanceOf(Page::class, $page);

        $page->refresh();

        $this->assertNotNull($page->photo);
        $this->assertNotNull($page->photo_link);
        $photo_link = $page->photo_link;
        $this->assertEquals($page->full_slug, $photo_link->link);
        $this->assertEquals($page->name, $photo_link->title);
    }

    public function test_a_pages_photo_uses_sort_order(): void
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'));
        $photo_block = $content_element->content;
        $photo = $photo_block->photos()->first();
        $page = $content_element->pages->first();

        sleep(1);

        $content_element2 = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'), $page);
        $photo_block2 = $content_element2->content;
        $photo2 = $photo_block2->photos()->first();

        Storage::fake();
        $file_name = Str::random().'jpg';
        $file = UploadedFile::fake()->image($file_name);
        $file_upload = (new FileUpload())->saveFile($file, 'photos', true);

        $input = Photo::factory()->raw([
            'file_upload_id' => $file_upload->id,
            'sort_order' => 2,
        ]);

        $photo3 = (new Photo())->savePhoto($input, null, $photo_block2);
        $this->assertInstanceOf(Photo::class, $photo3);

        $this->assertEquals(2, $photo_block2->photos()->count());

        $contentable1 = $content_element->contentables->first();
        $contentable2 = $content_element2->contentables->first();

        $contentable2->sort_order = 2;
        $contentable2->save();

        cache()->tags([cache_name($page)])->flush();
        $page->refresh();
        $this->assertEquals(3, $page->photos->count());
        $this->assertEquals($photo->id, $page->photo->id);

        // swap the sort order of the content elements
        $contentable1->sort_order = 2;
        $contentable1->save();
        $contentable2->sort_order = 1;
        $contentable2->save();

        cache()->tags([cache_name($page)])->flush();
        $page->refresh();
        $this->assertEquals($photo2->id, $page->photo->id);

        $photo2->sort_order = 2;
        $photo2->save();
        $photo3->sort_order = 1;
        $photo3->save();

        cache()->tags([cache_name($page)])->flush();
        $page->refresh();
        $this->assertEquals($photo3->id, $page->photo->id);
    }

    public function test_a_page_completed_notifcation_can_be_sent_without_a_logged_in_user(): void
    {
        $page = Page::factory()->create();
        $user = User::factory()->create();
        $version = $page->getDraftVersion();
        $version->publishing_requested_at = now();
        $version->publishing_requested_user_id = $user->id;
        $version->save();

        Mail::fake();

        $page->refresh();
        $page->publish();

        Mail::assertQueued(PublishingCompleted::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });
    }
}
