<?php

namespace Tests\Unit;

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

use Illuminate\Support\Arr;

use App\Models\ContentElement;
use App\Models\TextBlock;
use App\Models\Page;
use App\Models\Version;
use App\Models\Blog;
use App\Models\User;
use App\Models\Role;
use App\Models\PhotoBlock;

use Tests\Unit\TagsTrait;
use Tests\Unit\PermissionsTestTrait;

class ContentElementTest extends TestCase
{
    use WithFaker;
    use TagsTrait;
    use PermissionsTestTrait;

    protected function getClassname()
    {
        return 'content-element';
    }

    protected function getModel()
    {
        return $this->createContentElement(TextBlock::factory());
    }

    public function test_a_content_element_can_be_created_from_the_saveContentElement_function()
    {
        $page = Page::factory()->create();
        $text_block = TextBlock::factory()->raw();
        $input = [
            'id' => 0,
            'type' => 'text-block',
            'content' => $text_block,
            'pivot' => $this->getContentableArray($page, []),
            /*
                'contentable_id' => $page->id,
                'contentable_type' => get_class($page),
                'sort_order' => 1,
                'unlisted' => false,
                'expandable' => null,
                'guest' => false,
            ],
             */
        ];

        $content_element = (new ContentElement())->saveContentElement($input, null);

        $this->assertInstanceOf(ContentElement::class, $content_element);

        $page->refresh();
        $this->assertInstanceOf(ContentElement::class, $page->contentElements()->first());
        $this->assertEquals($content_element->id, $page->contentElements()->first()->id);

        $this->assertEquals($page->id, $content_element->pages->first()->id);
        $this->assertEquals(1, $content_element->pages->first()->pivot->sort_order);
        $this->assertEquals(0, $content_element->pages->first()->pivot->unlisted);
        $this->assertNull($content_element->pages->first()->pivot->expandable);
        $this->assertEquals(0, $content_element->pages->first()->pivot->guest);
        $this->assertEquals(0, $content_element->pages->first()->pivot->no_margin);
        $this->assertEquals(0, $content_element->pages->first()->pivot->randomize);
        $this->assertNotNull($content_element->content_id);
        $this->assertEquals(Arr::get($text_block, 'header'), $content_element->content->header);
        $this->assertEquals(Arr::get($text_block, 'body'), $content_element->content->body);
    }

    public function test_a_content_element_has_many_versions()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $this->assertEquals(1, $content_element->versions()->count());
        $this->assertInstanceOf(Version::class, $content_element->versions->first());
    }

    public function test_a_content_element_belongs_to_many_pages()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), Page::factory()->create());
        $this->assertInstanceOf(Page::class, $content_element->pages->first());
    }

    public function test_a_content_element_belongs_to_many_blogs()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), Blog::factory()->create());
        $this->assertInstanceOf(Blog::class, $content_element->blogs->first());
    }

    public function test_a_content_element_can_have_a_text_block()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $this->assertInstanceOf(TextBlock::class, $content_element->content);
    }

    public function test_a_content_element_has_a_content_type()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $this->assertEquals('text-block', $content_element->type);
    }

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

        $content = $content_element->content;

        $this->assertNotEquals($page->getDraftVersion()->id, $page->publishedVersion->id);

        $this->assertNotNull($content_element->contentables->first()->version->published_at);

        $input = ContentElement::factory()->for(TextBlock::factory(), 'content')->raw();
        $input['type'] = 'text-block';
        $input['content'] = TextBlock::factory()->raw();
        $input['content']['id'] = $content->id;
        $input['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,
        ];
         */

        $saved_content_element = (new ContentElement())->saveContentElement($input, $content_element->id);

        $this->assertNotEquals($content_element->id, $saved_content_element->id);
        $this->assertNotEquals($content->id, $saved_content_element->content->id);

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

        $page->refresh();
        $this->assertEquals($page->getDraftVersion()->id, $saved_content_element->contentables()->first()->version->id);
    }


    public function test_a_content_element_can_get_its_previous_version()
    {
        $page = Page::factory()->create();
        $content_element1 = $this->createContentElement(TextBlock::factory(), $page, $page->publishedVersion);
        $page->publish();
        $page->refresh();

        $this->assertNotNull($content_element1->contentables()->first()->version->published_at);

        $content_element2 = ContentElement::factory()->for(TextBlock::factory(), 'content')->create([
            'uuid' => $content_element1->uuid,
        ]);

        $content_element2->pages()->detach();
        $content_element2->pages()->attach($page, ['sort_order' => $this->faker->randomNumber(1), 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

        $this->assertInstanceOf(ContentElement::class, $content_element2->getPreviousVersion($page));
        $this->assertEquals($content_element1->id, $content_element2->getPreviousVersion($page)->id);
        $this->assertEquals($content_element1->uuid, $content_element2->uuid);
    }

    public function test_saving_publish_at_persits_the_correct_value()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $content_element = $this->createContentElement(TextBlock::factory());
        $content = $content_element->content;

        $page = Page::factory()->create();
        $page->publish();
        $page->refresh();
        $content_element->pages()->attach($page, ['sort_order' => $this->faker->randomNumber(1), 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->getDraftVersion()->id]);

        $input = ContentElement::factory()->for(TextBlock::factory(), 'content')->raw();
        $input['type'] = 'text-block';
        $input['content'] = TextBlock::factory()->raw();
        $input['content']['id'] = $content->id;
        $input['publish_at'] = '2020-08-23T12:00:00.000000Z';

        $input['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,
        ];
         */

        (new ContentElement())->saveContentElement($input, $content_element->id);

        $content_element->refresh();

        $content_element_array = $content_element->toArray();
        $this->assertEquals('2020-08-23T12:00:00.000000Z', Arr::get($content_element_array, 'publish_at'));
        $this->assertNotNull($content_element->publisher);
        $this->assertInstanceOf(User::class, $content_element->publisher);
        $this->assertEquals(auth()->user()->id, $content_element->publisher->id);
    }

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

        $version = $page->getDraftVersion();

        $this->assertInstanceOf(Version::class, $content_element->getPageVersion($page));
        $this->assertEquals($version->id, $content_element->getPageVersion($page)->id);
    }

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

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

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

        $user->addRole($role);

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

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

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

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

        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

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

        $this->assertTrue($user1->can('update', $content_element));

        $content_element->createPermission('update', $user2);
        $user2->refresh();
        $user1->refresh();
        $content_element->refresh();

        $this->assertFalse($user1->can('update', $content_element));
        $this->assertTrue($user2->can('update', $content_element));
    }

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

        $new_content_element = $content_element->createNewVersionOnPage($page);

        $this->assertEquals($page->id, $new_content_element->pages->first()->id);
        $this->assertEquals(get_class($page), get_class($new_content_element->pages->first()));

        $this->assertEquals(1, $new_content_element->contentables()->count());
        $contentable = $new_content_element->contentables()->first();

        $this->assertEquals(1, $content_element->pages()->count());
        $this->assertEquals($content_element->pages()->first()->pivot->sort_order, $contentable->sort_order);
        $this->assertEquals($content_element->pages()->first()->pivot->unlisted, $contentable->unlisted);
        $this->assertEquals($content_element->pages()->first()->pivot->expandable, $contentable->expandable);
        $this->assertEquals($content_element->pages()->first()->pivot->guest, $contentable->guest);
        $this->assertEquals($content_element->pages()->first()->pivot->no_margin, $contentable->no_margin);
        $this->assertEquals($content_element->pages()->first()->pivot->randomize, $contentable->randomize);
    }

    public function test_a_content_element_has_append_and_load_with_functions()
    {
        $load_with = (new ContentElement())->loadWith();
        $append_with = (new ContentElement())->appendWith();
        $this->assertNotNull($load_with);
        $this->assertNotNull($append_with);

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

        $content_element->load($load_with);
        $content_element->append($append_with);
    }

    public function test_a_content_element_can_find_the_next_one_of_its_type()
    {
        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $contentable = $content_element->contentables()->first();
        $contentable->sort_order = 1;
        $contentable->save();

        $content_element2 = $this->createContentElement(PhotoBlock::factory(), $page);
        $contentable2 = $content_element2->contentables()->first();
        $contentable2->sort_order = 2;
        $contentable2->save();

        $content_element3 = $this->createContentElement(TextBlock::factory(), $page);
        $contentable3 = $content_element3->contentables()->first();
        $contentable3->sort_order = 3;
        $contentable3->save();

        $page->publish();

        $next_id = $content_element->findNextIdByType($page->content, $page);

        $this->assertEquals($content_element3->id, $next_id);
    }

    public function test_a_content_element_has_an_anchor_attribute()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $this->assertNotNull($content_element->anchor);

        $content_element2 = $this->createContentElement(TextBlock::factory());
        $this->assertNotNull(Arr::get($content_element2->append((new ContentElement())->appendWith())->toArray(), 'anchor'));
        $this->assertEquals($content_element2->anchor, Arr::get($content_element2->append((new ContentElement())->appendWith())->toArray(), 'anchor'));
    }

    public function test_a_content_element_can_find_its_published_version()
    {
        // find the latest published version of a content element

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

        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $uuid = $content_element->uuid;
        $version = $page->getDraftVersion();
        $this->assertEquals($version->id, $content_element->contentables()->first()->version->id);
        $page->publish();
        $version->refresh();
        $this->assertNotNull($version->published_at);


        sleep(2);

        $version2 = $page->getDraftVersion();
        $content_element2 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element2->uuid = $uuid;
        $content_element2->save();
        $content_element2->refresh();

        $page->publish();
        $version2->refresh();
        $this->assertNotNull($version2->published_at);

        $this->assertTrue($version->published_at->isBefore($version2->published_at));

        $version3 = $page->getDraftVersion();
        $content_element3 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element3->uuid = $uuid;
        $content_element3->save();
        $content_element3->refresh();

        $this->assertEquals(3, ContentElement::where('uuid', $uuid)->get()->count());

        $this->assertEquals($content_element2->id, $content_element->getPublishedVersion()->id);
    }
}
