<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Event;

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

use App\Events\PagePublished;
use App\Events\PageSaved;

use Tests\Feature\SoftDeletesTestTrait;
use Tests\Feature\VersioningTestTrait;
use Tests\Feature\PagesTestTrait;

class PageTest extends TestCase
{
    use WithFaker;
    use SoftDeletesTestTrait;
    use VersioningTestTrait;
    use PagesTestTrait;

    protected function getModel()
    {
        return Page::factory()->create();
    }

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

    public function test_a_page_can_be_created()
    {
        $input['version'] = Version::factory()->page()->randomize()->raw();

        $this->postJson(route('pages.store'), [])
            ->assertStatus(401);

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

        $this->json('POST', route('pages.store'), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors([
                'version.name',
                'version.parent_page_id',
                'version.sort_order',
            ]);

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.store'), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => Page::all()->last()->full_slug,
            ]);

        $page = Page::all()->last();

        $this->assertNotNull($page->version->name);
        $this->assertNotNull($page->version->title);
        $this->assertNotNull($page->version->parent_page_id);
        $this->assertNotNull($page->version->sort_order);
        $this->assertNotNull($page->version->show_sub_menu);
        $this->assertNotNull($page->version->signed_url);
        $this->assertNotNull($page->version->sibling_nav);
        $this->assertNotNull($page->version->randomize);

        $this->assertEquals(Arr::get($input, 'version.name'), $page->version->name);
        $this->assertEquals(Arr::get($input, 'version.title'), $page->version->title);
        $this->assertEquals(Arr::get($input, 'version.theme'), $page->version->theme);
        $this->assertEquals(Arr::get($input, 'version.unlisted'), $page->version->unlisted);
        $this->assertEquals(Arr::get($input, 'version.show_sub_menu'), $page->version->show_sub_menu);
        $this->assertEquals(Arr::get($input, 'version.parent_page_id'), $page->version->parent_page_id);
        // pages are resorted so we cant expect to get the same value back after saving
        //$this->assertEquals(Arr::get($input, 'version.sort_order'), $page->version->sort_order);
        $this->assertEquals(Arr::get($input, 'version.footer_color'), $page->version->footer_color);
        $this->assertEquals(Arr::get($input, 'version.signed_url'), $page->version->signed_url);
        $this->assertEquals(Arr::get($input, 'version.sibling_nav'), $page->version->sibling_nav);
        $this->assertEquals(Arr::get($input, 'version.randomize'), $page->version->randomize);

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

    public function test_the_page_tree_is_sorted_when_creating_a_new_page()
    {
        $parent = Page::factory()->create();
        $page1 = Page::factory()->create();
        $version = $page1->versions->first();
        $version->parent_page_id = $parent->id;
        $version->sort_order = 1;
        $version->save();

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

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

        cache()->tags([cache_name($page1)])->flush();
        cache()->tags([cache_name($page2)])->flush();
        cache()->tags([cache_name($page3)])->flush();

        $input['version'] = Version::factory()->page()->raw([
            'parent_page_id' => $parent->id,
            'sort_order' => 2,
        ]);

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

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.store'), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => Page::all()->last()->full_slug,
            ]);

        $page = Page::all()->last();
        $this->assertEquals($parent->id, $page->version->parent_page_id);

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

        //cache()->tags([cache_name($page)])->flush();
        //cache()->tags([cache_name($page1)])->flush();
        //cache()->tags([cache_name($page2)])->flush();
        //cache()->tags([cache_name($page3)])->flush();

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

    public function test_the_page_tree_can_be_loaded()
    {
        /*
        $first_level_page = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $version = $second_level_page->versions->first();
        $version->parent_page_id = $first_level_page->id;
        $version->save();

        $first_level_page->publish();
        $second_level_page->publish();
         */

        $home_page = Page::find(1);
        $content_element = $this->createContentElement(TextBlock::factory(), $home_page);
        $home_page->publish();

        $this->withoutExceptionHandling();
        $this->json('GET', route('pages.index'))
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $home_page->version->name,
             ]);
    }

    /*
     * This test is useless now for checking the full page tree as
     * we now load every pages sub pages in an ajax call
     *
    public function the_page_tree_preview_can_be_loaded()
    {
        $first_level_page = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $second_level_page = Page::factory()->create();
        $version = $second_level_page->versions->first();
        $version->parent_page_id = $first_level_page->id;
        $version->save();

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

        $home_page = Page::find(1);
        $content_element = $this->createContentElement(TextBlock::factory(), $home_page);
        $home_page->publish();

        $this->withoutExceptionHandling();
        $this->json('GET', route('pages.index', ['preview' => 'true']))
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $home_page->version->name,
                //'name' => $second_level_page->version->name,
             ])
             ->assertJsonMissing([
                 'body' => $content_element->content->body,
             ]);

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

        $this->withoutExceptionHandling();
        $this->json('GET', route('pages.index', ['preview' => 'true']))
             ->assertSuccessful()
             ->assertJsonFragment([
                //'name' => $first_level_page->version->name,
                //'name' => $second_level_page->version->name,
                //'name' => $second_level_page->version->name,
                'name' => $home_page->version->name,
                'body' => $content_element->content->body,
             ]);
    }
     */

    public function test_the_home_page_can_be_loaded()
    {
        $home_page = Page::find(1);
        $this->assertInstanceOf(Page::class, $home_page);

        $version = $home_page->version;
        $version->redirect = null;
        $version->save();
        $home_page->publish();
        $version->refresh();
        $home_page->refresh();

        $this->assertNotNull($home_page->version);
        $this->assertNotNull($home_page->full_slug);

        $this->withoutExceptionHandling();
        $this->get('/')
             ->assertSuccessful()
             ->assertViewHas([
                'page' => $home_page,
             ]);
    }

    public function test_a_page_can_be_updated()
    {
        $page = Page::factory()->create();
        $input['version'] = Version::factory()->page()->raw();

        $this->postJson(route('pages.update', ['id' => $page->id]), [])
            ->assertStatus(401);

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

        $this->postJson(route('pages.update', ['id' => $page->id]), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors([
                'version.name',
                'version.parent_page_id',
                'version.sort_order',
            ]);

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

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

        $this->assertEquals(Arr::get($input, 'version.name'), $page->version->name);
        $this->assertEquals(Arr::get($input, 'version.parent_page_id'), $page->version->parent_page_id);
        $this->assertEquals(Arr::get($input, 'version.sort_order'), $page->version->sort_order);
    }

    public function test_a_page_can_be_hidden()
    {
        $page = Page::factory()->create();
        $input['version'] = Version::factory()->page()->raw([
            'unlisted' => true,
        ]);

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

        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        $this->assertEquals(1, $page->version->unlisted);

        $input['version'] = Version::factory()->page()->raw([
            'unlisted' => false,
        ]);

        $this->signInAdmin();

        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        $this->assertEquals(0, $page->version->unlisted);
    }

    public function test_a_page_gets_404_response_if_no_page_can_be_found()
    {
        $slug = Str::random(8);

        //$this->withoutExceptionHandling();
        $this->get($slug)
            ->assertStatus(404);
        // TODO some sort of 404 error message
    }

    public function test_the_home_page_important_fields_cannot_be_changed()
    {
        $home_page = Page::whereHas('versions', function ($query) {
            $query->where('slug', '/');
        })->first();
        $this->assertInstanceOf(Page::class, $home_page);

        $home_page_slug = $home_page->slug;
        $home_page_parent_page_id = $home_page->parent_page_id;

        $this->signInAdmin();

        $input = ['version' => [
            'name' => $this->faker->firstName,
            'slug' => $this->faker->firstName,
            'parent_page_id' => 0,
            'sort_order' => $this->faker->numberBetween(10, 100),
        ]];

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

        $home_page->refresh();

        $this->assertEquals($home_page_slug, $home_page->slug);
        $this->assertEquals($home_page_parent_page_id, $home_page->parent_page_id);
    }

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

        $this->assertNotNull($page->publishedVersion->published_at);
        $content_element = $this->createContentElement(TextBlock::factory(), $page, $page->publishedVersion);
        $this->assertEquals($page->published_version_id, $content_element->getPageVersion($page)->id);
        $this->assertNotNull($content_element->getPageVersion($page)->published_at);

        $content_element['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => null,
            'guest' => false,
        ];
         */

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

        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful();

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

        $this->assertNotEquals($content_element->id, $new_content_element->id);
        $page->refresh();
        $this->assertEquals($page->getDraftVersion()->id, $new_content_element->getPageVersion($page)->id);

        $this->assertEquals(Arr::get($input, 'header'), $new_content_element->header);
        $this->assertEquals(Arr::get($input, 'body'), $new_content_element->body);
        $this->assertEquals($page->getDraftVersion()->id, $new_content_element->getPageVersion($page)->id);

        $this->json('POST', route('pages.publish', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Page Published',
             ]);

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

        $this->assertNotNull($page->published_version_id);
        $this->assertEquals($page->published_version_id, $new_content_element->getPageVersion($page)->id);
    }


    public function test_a_page_returns_a_404_if_it_has_not_been_published()
    {
        $page = Page::factory()->unpublished()->create();
        $version = $page->versions->first();
        $slug = Str::random(8);
        $version->name = $slug;
        $version->save();

        $this->get($slug)
            ->assertStatus(404);

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

        $this->assertNotNull($page->published_at);

        //$this->withoutExceptionHandling();
        $this->get($page->full_slug)
            ->assertSuccessful();
    }

    public function test_the_home_page_cannot_be_deleted()
    {
        $home_page = Page::findOrFail(1);

        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.remove', ['id' => $home_page->id]))
             ->assertStatus(403)
             ->assertJsonFragment([
                'error' => 'The home page cannot be deleted',
             ]);
    }

    public function test_a_page_can_save_a_footer_image()
    {
        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);

        $page = Page::factory()->create();
        $input['version'] = Version::factory()->page()->raw();

        $fg_photo_input = Photo::factory()->raw();
        $fg_photo_input['file_upload_id'] = $fg_file_upload->id;

        $bg_photo_input = Photo::factory()->raw();
        $bg_photo_input['file_upload_id'] = $bg_file_upload->id;

        $input['version']['footer_fg_photo'] = $fg_photo_input;
        $input['version']['footer_bg_photo'] = $bg_photo_input;

        $input['version']['footer_color'] = $this->faker->hexcolor;

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

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        $this->assertEquals(Arr::get($input, 'version.name'), $page->version->name);
        $this->assertEquals(Arr::get($input, 'version.parent_page_id'), $page->version->parent_page_id);
        $this->assertEquals(Arr::get($input, 'version.sort_order'), $page->version->sort_order);
        $this->assertEquals(Arr::get($input, 'version.footer_color'), $page->version->footer_color);

        $this->assertNotNull(Arr::get($input, 'version.footer_fg_photo.file_upload_id'));
        $this->assertNotNull(Arr::get($input, 'version.footer_bg_photo.file_upload_id'));

        $this->assertEquals(Arr::get($input, 'version.footer_fg_photo.file_upload_id'), $page->version->getFooterFgPhoto()->fileUpload->id);
        $this->assertEquals(Arr::get($input, 'version.footer_bg_photo.file_upload_id'), $page->version->getFooterBgPhoto()->fileUpload->id);

        $page_fg_photo = $page->version->getFooterFgPhoto();
        $page_bg_photo = $page->version->getFooterBgPhoto();

        $this->assertInstanceOf(Photo::class, $page_fg_photo);
        $this->assertInstanceOf(Photo::class, $page_bg_photo);
    }

    public function test_a_user_without_update_permission_cannot_save_a_page()
    {
        $page = Page::factory()->create();
        $user = User::factory()->create();

        $input['version'] = Version::factory()->page()->raw();

        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
             ->assertStatus(401);

        $this->signIn($user);

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
             ->assertStatus(403);

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

        $user->refresh();

        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful();
    }

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

        $user = User::factory()->create();
        $this->signIn($user);
        $this->enableEditing();

        $input = [
            'sort_order' => 1,
            'parent_page_id' => $page->id,
        ];

        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
            ->assertStatus(403);

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

        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
            ->assertStatus(403);

        $user->addRole('pages-sort');
        $user->refresh();

        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'parent_page_id',
             ]);

        $input = [
            'sort_order' => 1,
            'parent_page_id' => $parent_page->id,
        ];

        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $page->version->name.' Saved',
            ]);
    }


    public function test_sorting_a_page_below_reorders_pages()
    {
        $parent_page = Page::factory()->create();
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 2;
        $version->save();

        $this->assertInstanceOf(Page::class, $parent_page);

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

        $page_below = Page::factory()->create();
        $version = $page_below->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 3;
        $version->save();

        $page_last = Page::factory()->create();
        $version = $page_last->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 4;
        $version->save();

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

        $input = [
            // This sets the position, after the below page
            'sort_order' => $page_below->version->sort_order + .5,
            'parent_page_id' => $parent_page->id,
        ];

        //dump('ABOVE: '.$page_above->id);
        //dump('BELOW: '.$page_below->id);
        //dump('PAGE: '.$page->id);
        //dump('LAST: '.$page_last->id);

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $page->version->name.' Saved',
            ]);

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

        $page->refresh();
        $page_above->refresh();
        $page_below->refresh();
        $page_last->refresh();

        $this->assertEquals(1, $page_above->version->sort_order);
        $this->assertEquals(2, $page_below->version->sort_order);
        $this->assertEquals(3, $page->version->sort_order);
        $this->assertEquals(4, $page_last->version->sort_order);
    }

    public function test_sorting_a_page_above_reorders_pages()
    {
        $parent_page = Page::factory()->create();
        $page = Page::factory()->create();
        $version = $page->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 2;
        $version->save();

        $this->assertInstanceOf(Page::class, $parent_page);

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

        $page_below = Page::factory()->create();
        $version = $page_below->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 3;
        $version->save();

        $page_last = Page::factory()->create();
        $version = $page_last->versions->first();
        $version->parent_page_id = $parent_page->id;
        $version->sort_order = 4;
        $version->save();

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

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

        $input = [
            // This sets the position, after the below page
            'sort_order' => .5,
            'parent_page_id' => $parent_page->id,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $page->version->name.' Saved',
            ]);

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

        $page->refresh();
        $page_above->refresh();
        $page_below->refresh();
        $page_last->refresh();

        $this->assertEquals(1, $page->version->sort_order);
        $this->assertEquals(2, $page_above->version->sort_order);
        $this->assertEquals(3, $page_below->version->sort_order);
        $this->assertEquals(4, $page_last->version->sort_order);
    }

    public function test_sorting_a_page_into_a_new_parent_sorts_the_old_parent_pages()
    {
        $parent_old = Page::factory()->create();
        $parent_new = Page::factory()->create();

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

        $old_page1 = Page::factory()->create();
        $version = $old_page1->versions->first();
        $version->parent_page_id = $parent_old->id;
        $version->sort_order = 1;
        $version->save();

        $old_page2 = Page::factory()->create();
        $version = $old_page2->versions->first();
        $version->parent_page_id = $parent_old->id;
        $version->sort_order = 3;
        $version->save();

        $new_page1 = Page::factory()->create();
        $version = $new_page1->versions->first();
        $version->parent_page_id = $parent_new->id;
        $version->sort_order = 1;
        $version->save();

        $new_page2 = Page::factory()->create();
        $version = $new_page2->versions->first();
        $version->parent_page_id = $parent_new->id;
        $version->sort_order = 2;
        $version->save();

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

        $input = [
            // This sets the position, after the below page
            'sort_order' => 1.5,
            'parent_page_id' => $parent_new->id,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.sort', ['id' => $page->id]), $input)
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $page->version->name.' Saved',
            ]);

        $page->refresh();
        $old_page1->refresh();
        $old_page2->refresh();
        $new_page1->refresh();
        $new_page2->refresh();

        $this->assertEquals(2, $page->version->sort_order);
        $this->assertEquals(1, $new_page1->version->sort_order);
        $this->assertEquals(3, $new_page2->version->sort_order);
        $this->assertEquals(1, $old_page1->version->sort_order);
        $this->assertEquals(2, $old_page2->version->sort_order);
    }

    public function test_sorting_a_page_only_creates_new_versions_for_all_pages_if_the_sort_is_different()
    {
        $parent = Page::factory()->create();

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

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

        $page3 = Page::factory()->create();
        $version = $page3->versions->first();
        $version->parent_page_id = $parent->id;
        $version->sort_order = 3;
        $version->save();
        $page3_version_id = $version->id;

        $page4 = Page::factory()->create();
        $version = $page4->versions->first();
        $version->parent_page_id = $parent->id;
        $version->sort_order = 4;
        $version->save();
        $page4_version_id = $version->id;

        $page5 = Page::factory()->create();
        $version = $page5->versions->first();
        $version->parent_page_id = $parent->id;
        $version->sort_order = 5;
        $version->save();
        $page5_version_id = $version->id;

        $page6 = Page::factory()->create();
        $version = $page6->versions->first();
        $version->parent_page_id = $parent->id;
        $version->sort_order = 6;
        $version->save();
        $page6_version_id = $version->id;

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

        $page1->publish();
        $page2->publish();
        $page3->publish();
        $page4->publish();
        $page5->publish();
        $page6->publish();

        $input = [
            'sort_order' => 2.5,
            'parent_page_id' => $parent->id,
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('pages.sort', ['id' => $page4->id]), $input)
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $page4->version->name.' Saved',
            ]);

        $page1->refresh();
        $page2->refresh();
        $page3->refresh();
        $page4->refresh();
        $page5->refresh();
        $page6->refresh();

        $this->assertEquals(1, $page1->versions()->get()->last()->sort_order);
        $this->assertEquals(2, $page2->versions()->get()->last()->sort_order);
        $this->assertEquals(4, $page3->versions()->get()->last()->sort_order);
        $this->assertEquals(3, $page4->versions()->get()->last()->sort_order);
        $this->assertEquals(5, $page5->versions()->get()->last()->sort_order);
        $this->assertEquals(6, $page6->versions()->get()->last()->sort_order);

        $this->assertEquals(1, $page1->versions()->count());
        $this->assertEquals(1, $page2->versions()->count());
        $this->assertEquals(2, $page3->versions()->count());
        $this->assertEquals(2, $page4->versions()->count());
        $this->assertEquals(1, $page5->versions()->count());
        $this->assertEquals(1, $page6->versions()->count());
    }

    /*
    public function sorting_a_first_level_page()
    {
        $page = factory(Page::class)->create([
            'parent_page_id' => 1,
            'sort_order' => 2,
        ]);

        $this->signInAdmin();

        $input = [
            // This sets the position, after the below page
            'sort_order' => 1.5,
            'parent_page_id' => 1,
        ];

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

        $page->refresh();

        $this->assertEquals(1, $page->parentPage->id);
        dump($page->parentPage->pages()->get()->pluck('sort_order')->toArray());
        $this->assertEquals(1, $page->sort_order);

    }
     */



    public function test_the_home_page_can_be_saved()
    {
        $input = [
            "content_elements" => [],
            "version" => [
                "footer_bg_photo" => null,
                "footer_color" => null,
                "footer_fg_photo" => null,
                "name" => "d",
                "parent_page_id" => 0,
                "sort_order" => 0,
                "unlisted" => false,
            ],
        ];

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

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


    public function test_individual_content_elements_can_be_published()
    {
        $content_element1 = $this->createContentElement(TextBlock::factory());
        $text_block = $content_element1->content;
        $page = $content_element1->pages->first();

        $this->assertInstanceOf(Page::class, $page);
        $this->assertInstanceOf(ContentElement::class, $content_element1);

        $content_element2 = $this->createContentElement(TextBlock::factory());
        $text_block2 = $content_element2->content;

        $content_element2->pages()->detach();

        $content_element2->pages()->attach($page, [
            'sort_order' => 2,
            'unlisted' => false,
            'expandable' => 'expand',
            'guest' => false,
            'no_margin' => false,
            'randomize' => false,
            'version_id' => $page->getDraftVersion()->id,
        ]);

        $page->refresh();

        $this->assertEquals(2, $page->contentElements->count());

        //$page->publish();

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

        $content_element1['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element1->toArray();
        $text_block_content = TextBlock::factory()->raw();
        $input['content'] = $text_block_content;

        $this->json('POST', route('content-elements.update', ['id' => $content_element1->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element1->refresh();
        $content_element1_version_id = $content_element1->getPageVersion($page)->id;
        $page->refresh();

        $this->assertEquals($page->getDraftVersion()->id, $content_element1->getPageVersion($page)->id);

        $content_element2['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element2->toArray();
        $input['publish_at'] = now()->subMinutes(5);
        $text_block_content2 = TextBlock::factory()->raw();
        $input['content'] = $text_block_content2;

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element2->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element2->refresh();
        $this->assertNotNull($content_element2->publish_at);
        $this->assertTrue($content_element2->publish_at->isPast());
        $this->assertEquals($page->draft_version_id, $content_element2->getPageVersion($page)->id);

        // publish command
        // find pages where the page needs to be publish OR the content elements need to be published

        $page->refresh();
        $this->assertNull($page->publish_at);

        $user = auth()->user();
        $this->assertNotNull($user);
        $this->assertEquals($user->id, $content_element2->publisher->id);
        auth()->logout();

        Page::publishScheduledContent();

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

        $this->assertNotEquals($content_element1_version_id, $content_element1->getPageVersion($page)->id);
        $this->assertEquals($page->getDraftVersion()->id, $content_element1->getPageVersion($page)->id);
        $this->assertEquals($page->publishedVersion->id, $content_element2->getPageVersion($page)->id);
        $this->assertNotNull($page->publishedVersion->publisher);
        $this->assertEquals($user->id, $page->publishedVersion->publisher->id);
    }


    public function test_loading_a_pages_content_elements_includes_the_contentable_property()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $page = $content_element->pages->first();

        $this->assertInstanceOf(Page::class, $page);

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

        $this->json('GET', route('pages.load', ['page' => $page->full_slug]))
            ->assertSuccessful()
            ->assertJsonFragment([
                'contentable_id' => $page->id,
                'contentable_type' => get_class($page),
            ]);
    }

    public function test_a_page_doesnt_create_a_new_version_after_publishing()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory());
        $page = $content_element->pages->first();

        $this->assertInstanceOf(Page::class, $page);

        $this->assertNull($content_element->published_at);
        $uuid = $content_element->uuid;

        $this->assertEquals(1, ContentElement::where('uuid', $uuid)->count());
        $this->assertEquals(1, $page->contentElements()->count());

        $this->json('POST', route('pages.publish', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Page Published',
             ]);

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

        $this->assertEquals(1, $page->versions()->count());

        $version = $page->versions()->first();

        $this->assertInstanceOf(Version::class, $version);

        $this->assertEquals($version->id, $content_element->getPageVersion($page)->id);

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
             ->assertJsonFragment([
                'version_id' => $version->id,
             ]);

        $page->refresh();
        $this->assertEquals(1, ContentElement::where('uuid', $uuid)->count());
        $this->assertEquals(1, $page->contentElements()->count());
    }

    public function test_a_published_content_can_be_instanced_onto_another_page()
    {
        // create a content element
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        // publish it
        $page->publish();

        $content_element->refresh();
        $this->assertNotNull($content_element->getPageVersion($page)->published_at);
        // instance it onto another page

        $new_page = $this->getModel();
        $this->assertEquals(0, $new_page->contentElements()->count());

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

        $content_element['pivot'] = $this->getContentableArray($new_page);
        /*
            'contentable_id' => $new_page->id,
            'contentable_type' => get_class($new_page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $input = $content_element->toArray();
        $input['instance'] = 'true';

        $this->assertEquals($new_page->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

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

        // assert its a new content element
        $this->assertEquals(1, $new_page->contentElements()->count());
        $new_content_element = $new_page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $new_content_element->id);

        // assert that the origanl page has a draft that is the new ce
        $this->assertEquals(2, $page->contentElements()->count());
        $page_new_content_element = $page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $page_new_content_element->id);
        // assert that both ce's are the same id
        $this->assertEquals($new_content_element->id, $page_new_content_element->id);
        $this->assertEquals($new_content_element->uuid, $page_new_content_element->uuid);
        // make a text change

        $input['content'] = TextBlock::factory()->raw();

        // Update the content

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();
        $new_page->refresh();
        $new_content_element->refresh();

        // assert the change is on both pages
        $this->assertEquals(2, $page->contentElements()->count());
        $this->assertEquals(1, $new_page->contentElements()->count());
        $this->assertEquals(Arr::get($input, 'content.body'), $page->contentElements()->get()->last()->content->body);
        $this->assertEquals(Arr::get($input, 'content.body'), $new_page->contentElements()->get()->last()->content->body);

        //dump('PAGE ID: '.$page->id);
        //dump('PAGE CID: '.$new_content_element->id);

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

        // publish the content element on the new page
        $this->json('POST', route(Str::plural($this->getClassname()).'.publish', ['id' => $new_page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Published',
             ]);

        $page->refresh();
        $new_page->refresh();
        $new_content_element->refresh();

        // assert the instanced ce is published
        $this->assertNotNull($new_content_element->getPageVersion($new_page)->published_at);

        $this->assertEquals(2, $page->contentElements()->count());
        // assert the original page ce is also published
        $this->assertNotNull($new_content_element->getPageVersion($page)->published_at);
        $this->assertEquals($new_page->published_content_elements->last()->content->body, $page->published_content_elements->last()->content->body);
    }

    public function test_a_content_element_can_be_removed_from_one_page_but_stay_on_another()
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $page->publish();

        $content_element->refresh();
        $this->assertNotNull($content_element->getPageVersion($page)->published_at);

        $new_page = $this->getModel();
        $this->assertEquals(0, $new_page->contentElements()->count());

        $this->signInAdmin();

        $content_element['pivot'] = $this->getContentableArray($new_page);
        /*
            'contentable_id' => $new_page->id,
            'contentable_type' => get_class($new_page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $input = $content_element->load((new ContentElement())->loadWith())->toArray();
        $input['instance'] = 'true';

        $this->assertEquals($new_page->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

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

        $this->assertEquals(1, $new_page->contentElements()->count());
        $new_content_element = $new_page->contentElements()->first();
        $this->assertNotEquals($content_element->id, $new_content_element->id);

        $this->assertEquals(2, $page->contentElements()->count());
        $page_new_content_element = $page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $page_new_content_element->id);
        $this->assertEquals($new_content_element->id, $page_new_content_element->id);

        $new_page->publish();

        $new_content_element->refresh();
        $page_new_content_element->refresh();

        $this->assertNotNull($new_content_element->getPageVersion($new_page)->published_at);
        $this->assertNotNull($page_new_content_element->getPageVersion($page)->published_at);

        $input = [];
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->assertEquals(1, ContentElement::where('id', $page_new_content_element->id)->get()->count());

        $this->assertNotNull($page_new_content_element->getPreviousVersion($page));

        $this->withoutExceptionHandling();

        $this->json('POST', route('content-elements.remove', ['id' => $page_new_content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Version Removed',
                'uuid' => $page_new_content_element->uuid,
            ]);

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

        $this->assertEquals(1, $page->all_content_elements->count());
        $this->assertEquals(1, $new_page->all_content_elements->count());
    }

    public function test_the_home_page_can_be_edited()
    {
        $home = Page::find(1);

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

        $this->get('/')
            ->assertSuccessful();
    }

    public function test_a_protected_page_has_certain_fields_that_cant_be_changed()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $page = Page::factory()->create();
        $version = $page->versions()->first();
        $version->slug = 'foobar';
        $version->save();

        $page->protected = true;
        $page->save();
        $page->refresh();

        $name = $page->version->name;
        $slug = $page->version->slug;
        $title = $page->version->title;
        $full_slug = $page->full_slug;
        $parent_page_id = $page->version->parent_page_id;

        $input = $page->toArray();
        $input['version'] = Version::factory()->page()->raw();

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $name.' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

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

        $this->assertNotNull($page->version->name);
        $this->assertNotNull($page->version->slug);
        $this->assertNotNull($page->version->title);
        $this->assertNotNull($page->version->parent_page_id);
        $this->assertNotNull($page->full_slug);

        $this->assertEquals($name, $page->version->name);
        $this->assertEquals($slug, $page->version->slug);
        //$this->assertEquals($title, $page->version->title);
        $this->assertEquals($parent_page_id, $page->version->parent_page_id);
        $this->assertEquals($full_slug, $page->full_slug);

        $page->publish();

        $input = $page->toArray();
        $input['version'] = Version::factory()->page()->raw();

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => $name.' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        $this->assertNotNull($page->version->name);
        $this->assertNotNull($page->version->slug);
        $this->assertNotNull($page->version->title);
        $this->assertNotNull($page->version->parent_page_id);
        $this->assertNotNull($page->full_slug);

        $this->assertEquals($name, $page->version->name);
        $this->assertEquals($slug, $page->version->slug);
        //$this->assertEquals($title, $page->version->title);
        $this->assertEquals($parent_page_id, $page->version->parent_page_id);
        $this->assertEquals($full_slug, $page->full_slug);
    }

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

        $role = Role::factory()->create();
        $page->createPermission('view', $role);

        $page->refresh();

        $user = User::factory()->create();

        $this->assertFalse($user->can('view', $page));

        $this->get($page->full_slug)
             ->assertRedirect('/login');

        $this->signIn($user);

        $this->get($page->full_slug)
             ->assertRedirect('/');

        $user->addRole($role);

        $user->refresh();

        $this->get($page->full_slug)
             ->assertSuccessful();

        // check for inheritence from parents

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

        $this->assertEquals($page->id, $child->version->parent_page_id);

        auth()->logout();

        $this->assertFalse(auth()->check());

        $this->assertTrue($child->hasViewPermissions());

        $this->get($child->full_slug)
             ->assertRedirect('/login');

        $this->signIn($user);

        $this->get($page->full_slug)
             ->assertSuccessful();
    }

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

        $url = 'http://www.google.com';
        $input['version'] = Version::factory()->page()->raw();
        $input['version']['redirect'] = $url;

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

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        $this->assertNotNull($page->version->redirect);
        $this->assertEquals($url, $page->version->redirect);

        $page->publish();

        $this->disableEditing();

        $this->get($page->full_slug)
            ->assertRedirect($url);
    }

    public function test_renaming_a_page_clears_the_cache_for_full_slug()
    {
        $page = Page::factory()->create();
        $input['version'] = Version::factory()->page()->raw([
            'name' => 'Untitled Page',
        ]);

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

        $this->withoutExceptionHandling();
        $this->postJson(route('pages.store'), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => Page::all()->last()->full_slug,
            ]);

        $page = Page::all()->last();

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
            ->assertJsonFragment([
                'id' => $page->id,
            ]);

        $this->postJson(route('pages.store'), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => Page::all()->last()->full_slug,
            ]);

        $page2 = Page::all()->last();

        $this->assertNotEquals($page->id, $page2->id);

        $this->json('GET', $page2->full_slug)
             ->assertSuccessful()
            ->assertJsonFragment([
                'id' => $page2->id,
            ]);
    }

    /*
    public function requesting_a_preview_prompts_for_sign_in()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $page = Page::factory()->create();
        $this->assertNotNull($page->full_slug);
        $url = url($page->full_slug.'?preview=true');
        $this->disableEditing();
        auth()->logout();

        $this->get($url)
             ->assertRedirect('/login')
             ->assertSessionHas('url.intended', $url);
    }
     */

    /*
    public function loading_a_preview_includes_menu_items()
    {
        $page = Page::factory()->create();
        $version = $page->versions()->first();
        $version->parent_page_id = 1;
        $version->slug = Str::random(8);
        $version->unlisted = 0;
        $version->save();
        $page->refresh();

        $this->signInAdmin();
        $this->enableEditing();
        $this->assertNotNull($page->full_slug);
        $this->assertNotNull($page->preview_url);

        $this->assertNotNull($version->slug);
        $this->get($page->preview_url)
             ->assertSuccessful()
             ->assertSeeText($version->name);
    }
     */

    /*
    public function requesting_an_ajax_page_from_the_tree_after_timeout_redirects_to_login()
    {
        $page = Page::factory()->create();
        $page->publish();
        $this->json('GET', $page->full_slug)
             ->assertStatus(403)
            ->assertJsonFragment([
                'redirect' => 'login?error=Please%20login%20to%20edit',
            ]);
    }
     */

    public function test_an_old_url_doesnt_mess_up_previewing_of_a_page()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $old_version = $page->version;

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

        $new_name = 'foobar'.Str::random();

        $input['version'] = $old_version->toArray();
        $input['version']['name'] = $new_name;

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

        $page->refresh();

        $new_version = $page->version;

        $this->assertNotEquals($old_version->id, $new_version->id);

        $ce_input = $content_element->load((new ContentElement())->loadWith())->toArray();
        $ce_input['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $paragraph = $this->faker->paragraph;
        $ce_input['content']['body'] = $paragraph;

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $ce_input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();
        $this->assertEquals(2, $page->contentElements()->count());

        $this->get($new_name.'?preview=true')
             ->assertSuccessful()
         ->assertSee($paragraph);
    }

    public function test_a_page_can_toggle_protected()
    {
        $page = Page::factory()->create();
        $input['version'] = Version::factory()->page()->raw();
        $input['protected'] = true;

        $user = User::factory()->create();
        $role = Role::factory()->create();
        $user->addRole($role);
        $page->createPermission('update', $role);

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

        $this->assertTrue($user->can('update', $page));

        $this->signIn($user);
        $this->enableEditing();

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

        $page->refresh();
        $this->assertFalse($page->protected);

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

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

        $page->refresh();
        $this->assertTrue($page->protected);

        $input['protected'] = false;

        $this->signIn($user);
        $this->enableEditing();

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

        $page->refresh();
        $this->assertTrue($page->protected);

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

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

        $page->refresh();
        $this->assertFalse($page->protected);
    }

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

        $child->refresh();
        $parent->refresh();

        $this->assertNull($child->parent_slug);

        $this->assertNull($child->full_slug);
    }


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

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

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

        $this->assertNotNull($page->getPages());
        $this->assertEquals(1, $page->getPages()->count());

        $this->json('POST', route('pages.sub-pages', ['id' => $page->id]))
            ->assertStatus(401);

        $user = User::factory()->create();
        $this->signIn($user);

        $this->json('POST', route('pages.sub-pages', ['id' => $page->id]))
            ->assertStatus(403);

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

        $page->refresh();

        $this->assertTrue($user->can('update', $page));

        $this->json('POST', route('pages.sub-pages', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'name' => $sub_page->version->name,
             ]);
    }

    public function test_you_should_not_be_able_to_register_unless_as_a_guest()
    {
        $this->get('/register')
            ->assertSuccessful();

        $user = User::factory()->create();
        $this->signIn($user);

        $this->get('/register')
             ->assertRedirect('/');

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

        $this->get('/register')
            ->assertSuccessful();
    }

    public function test_removing_a_page_removes_its_contentables()
    {
        // create a content element
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        // publish it

        $content_element->refresh();

        $new_page = $this->getModel();
        $this->assertEquals(0, $new_page->contentElements()->count());

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

        $content_element['pivot'] = $this->getContentableArray($new_page);
        $input = $content_element->toArray();
        $input['instance'] = 'true';

        $this->assertEquals($new_page->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

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

        $content_element->refresh();
        $this->assertEquals(2, $content_element->contentables()->count());

        $this->json('POST', route('pages.remove', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Page Removed',
             ]);

        $content_element->refresh();
        $this->assertEquals(1, $content_element->contentables()->count());

        $this->assertEquals($new_page->id, $content_element->contentables()->first()->pageable->id);
    }

    public function test_a_page_versioning_data_is_copied_when_creating_a_new_version()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $version = $page->version;

        $description = $this->faker->sentence;
        $theme = 'dark';

        $version->description = $description;
        $version->theme = $theme;
        $version->signed_url = 1;
        $version->randomize = 1;
        $version->save();

        $page->publish();

        $input = $content_element->toArray();
        $text_block_input = TextBlock::factory()->raw();
        $input['content']['header'] = Arr::get($text_block_input, 'header');
        $input['content']['body'] = Arr::get($text_block_input, 'body');
        $input['content']['style'] = Arr::get($text_block_input, 'style');
        $input['pivot'] = $this->getContentableArray($page);

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();

        $this->assertEquals(2, $page->versions()->count());
        $new_version = $page->versions()->get()->last();
        $this->assertNotEquals($version->id, $new_version->id);

        $this->assertEquals($version->parent_page_id, $new_version->parent_page_id);
        $this->assertEquals($version->name, $new_version->name);
        $this->assertEquals($version->slug, $new_version->slug);
        $this->assertEquals($version->description, $new_version->description);
        $this->assertEquals($version->theme, $new_version->theme);
        $this->assertEquals($version->sort_order, $new_version->sort_order);
        $this->assertEquals($version->unlisted, $new_version->unlisted);
        $this->assertEquals($version->show_sub_menu, $new_version->show_sub_menu);
        $this->assertEquals($version->footer_color, $new_version->footer_color);
        $this->assertEquals($version->signed_url, $new_version->signed_url);
        $this->assertEquals($version->sibling_nav, $new_version->sibling_nav);
        $this->assertEquals($version->randomize, $new_version->randomize);
    }

    public function test_the_content_is_generated_for_a_signed_preview()
    {
        $menu_page = Page::factory()->create();
        $menu_version = $menu_page->getDraftVersion();
        $this->assertInstanceOf(Version::class, $menu_version);
        $menu_version->unlisted = false;
        $menu_version->save();
        $menu_page->refresh();
        cache()->tags([cache_name($menu_page)])->flush();
        $this->assertFalse($menu_page->getDraftVersion()->unlisted);
        //$menu_page->publish();

        $page = Page::factory()->create();
        $version = $page->versions()->first();
        $version->signed_url = true;
        $version->unlisted = true;
        $version->save();

        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $page->refresh();

        $this->signInAdmin();
        $this->enableEditing();
        $preview_url = $page->preview_url;
        $this->assertNotNull($preview_url);
        $content_body = $content_element->content->body;
        $this->assertNotNull($content_body);

        $this->disableEditing();
        auth()->logout();
        session()->invalidate();

        cache()->tags([cache_name('nav-menu')])->flush();

        $this->get($preview_url)
             ->assertSuccessful()
             //->assertSee($menu_page->name)
             ->assertSee($content_body);
    }

    public function test_creating_a_page_grants_update_permissons_to_the_creator()
    {
        $user = User::factory()->create();
        $parent_page = Page::factory()->create();
        $this->assertEquals(1, $parent_page->getDraftVersion()->parent_page_id);

        $input = [
            'version' => Version::factory()->page()->raw([
                'parent_page_id' => $parent_page->id,
                'sort_order' => 1,
            ]),
        ];

        $this->json('POST', route('pages.store'), $input)
            ->assertStatus(401);

        $this->signIn($user);
        $this->enableEditing();
        $this->json('POST', route('pages.store'), $input)
            ->assertStatus(403);

        // grant access to the parent page, most users will have some sort of top level access
        $user->createPermission('update', $parent_page);
        $user->refresh();
        $this->assertTrue($user->can('update', $parent_page));

        // create the new sub page
        $this->json('POST', route('pages.store'), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors([
                'version.name',
                'version.parent_page_id',
                'version.sort_order',
            ]);

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

        $page = Page::all()->last();
        $version = $page->versions()->first();
        $this->assertNotNull($version->name);
        $this->assertEquals(Arr::get($input, 'version.name'), $version->name);
        $this->assertEquals($version->name, $page->name);

        $this->assertTrue($user->can('update', $page));

        $this->json('POST', route('pages.preview-url', ['id' => $page->id]))
             ->assertSuccessful();
    }

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

        $input = [
            'version' => [
                'name' => 'Foobar',
                'parent_page_id' => 1,
                'sort_order' => '2',
                'unlisted' => true,
            ]
        ];

        $this->signIn($user);
        $this->enableEditing();

        $this->json('POST', route('pages.store'), $input)
            ->assertStatus(403);

        $user->createPermission('update', $home_page);
        $user->refresh();

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

        $new_page = Page::all()->last();
        $user->refresh();
        $this->assertTrue($user->can('update', $new_page));

        $this->json('POST', route('pages.preview-url', ['id' => $new_page->id]))
             ->assertSuccessful();
    }

    public function test_a_user_can_directly_edit_a_page_if_they_have_access()
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $this->assertNotNull($content_element->content->header);

        $user = User::factory()->create();

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

        $this->signIn($user);

        $this->get($full_slug)
            ->assertStatus(404);

        $this->get($full_slug.'?editing=true')
             ->assertSuccessful()
             ->assertDontSee($content_element->content->header);

        $this->assertFalse(editing());

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

        $this->get($full_slug.'?editing=true')
             ->assertSuccessful();

        $this->assertTrue(editing());

        $this->get($full_slug.'?editing=false')
             ->assertSuccessful()
             ->assertDontSee($content_element->content->header);

        $this->assertFalse(editing());
    }

    public function test_a_page_can_be_loaded_by_its_id()
    {
        $page = Page::factory()->create();
        $this->json('GET', route('pages.load-by-id', ['id' => $page->id]))
            ->assertStatus(401);

        $user = User::factory()->create();

        $this->signIn($user);

        $this->json('GET', route('pages.load-by-id', ['id' => $page->id]))
            ->assertStatus(404);

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

        $this->json('GET', route('pages.load-by-id', ['id' => $page->id]))
            ->assertStatus(404);

        $this->enableEditing();

        $this->json('GET', route('pages.load-by-id', ['id' => $page->id]))
             ->assertSuccessful()
            ->assertJsonFragment([
                'name' => $page->getDraftVersion()->name,
            ]);
    }

    public function test_an_nxt_form_can_be_viewed()
    {
        $id = 'fde86bdd-f03a-40db-bb96-a7a6b27c636f';

        $this->get(route('nxt.form', ['id' => $id]))
             ->assertSuccessful()
            ->assertViewHas('id', $id);
    }

    public function test_a_page_can_still_be_found_if_there_is_a_header_link()
    {
        $page = Page::factory()->create();
        $this->createContentElement(TextBlock::factory(), $page);
        $content_element = ContentElement::all()->last();
        $text_block = $content_element->content;
        $this->assertInstanceOf(ContentElement::class, $content_element);
        $this->assertInstanceOf(TextBlock::class, $text_block);
        $this->assertNotNull($text_block->header);
        $page->publish();
        $page->refresh();

        $this->assertNotNull($page->full_slug);
        $slug = $page->full_slug.'/foobar';

        $this->get($slug)
             ->assertSuccessful()
            ->assertSee($text_block->header);
    }

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

        $parent = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'))->pages->first();
        $version = $parent->getDraftVersion();

        $page1 = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'))->pages->first();
        $version1 = $page1->getDraftVersion();
        $version1->parent_page_id = $parent->id;
        $version1->sort_order = 1;
        $version1->unlisted = false;
        $version1->save();
        $page1->refresh();

        $page2 = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'))->pages->first();
        $version2 = $page2->getDraftVersion();
        $version2->parent_page_id = $parent->id;
        $version2->sort_order = 2;
        $version2->unlisted = false;
        $version2->save();
        $page2->refresh();

        $page3 = $this->createContentElement(PhotoBlock::factory()->has(Photo::factory([
            'file_upload_id' => FileUpload::factory()->jpg(),
        ]), 'photos'))->pages->first();
        $version3 = $page3->getDraftVersion();
        $version3->parent_page_id = $parent->id;
        $version3->sort_order = 3;
        $version3->unlisted = false;
        $version3->save();
        $page3->refresh();

        $this->assertNotNull($page2->previous_page);
        $this->assertNotNull($page2->next_page);
        $this->assertNotNull($page2->parentPage);
        $this->assertEquals($parent->id, $page2->parentPage->id);

        $this->assertNotNull($page2->previous_page->photo);
        $this->assertNotNull($page2->next_page->photo);
        $this->assertNotNull($page2->parentPage->photo);

        $this->json('GET', route('pages.sibling-nav', ['id' => $page2->id]))
             ->assertSuccessful()
            ->assertJsonFragment([
                'full_slug' => $page2->previous_page->full_slug,
                'full_slug' => $page2->next_page->full_slug,
                'full_slug' => $page2->parentPage->full_slug,
                'large' => $page2->previous_page->photo->large,
            ]);
    }

    /* Cant actually do this as we have no way of pulling in the current page
    public function a_uuid_link_on_a_page_from_instanced_content_is_linked_on_that_page(): void
    {
        $this->signInAdmin();
        $this->enableEditing();

        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $text_block = $content_element->content;

        $content_element2 = $this->createContentElement(TextBlock::factory(), $page);
        $text_block2 = $content_element2->content;

        $text_block->body = '<a class="button" href="'.$page->id.'#c-'.$content_element2->uuid.'">'.$page->getDraftVersion()->name.'</a>';
        $text_block->save();

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

        $content_element['pivot'] = $this->getContentableArray($page2);
        $input = $content_element->toArray();
        $input['instance'] = 'true';

        $this->assertEquals($page2->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element2['pivot'] = $this->getContentableArray($page2);
        $input = $content_element2->toArray();
        $input['instance'] = 'true';

        $this->assertEquals($page2->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element2->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $this->json('POST', route('pages.publish', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Page Published',
             ]);

        $this->json('POST', route('pages.publish', ['id' => $page2->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Page Published',
             ]);

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

        // assert its a new content element
        $this->assertEquals(2, $page2->contentElements()->count());

        // assert that the origanl page has a draft that is the new ce
        $this->assertEquals(2, $page->contentElements()->count());

        $this->assertEquals($page->content->first()->id, $page2->content->first()->id);
        $this->assertEquals($page->content->last()->id, $page2->content->last()->id);

        auth()->logout();
        $this->disableEditing();

        $this->assertNotNull($text_block2->header);
        $this->assertNotNull($page2->full_slug);
        $this->assertNotNull($page->name);

        $expected_text = '<a class="button" href="/'.$page2->full_slug.'#'.PageLink::convertAnchorText($text_block2->header).'">'.$page->name.'</a>';

        $this->assertEquals($expected_text, $page2->content->first()->content->body);
    }
     */
}
