<?php

namespace Tests\Unit;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;

use App\Models\ContentElement;
use App\Models\TextBlock;
use App\Models\Page;
use App\Models\User;
use App\Models\Role;
use App\Models\PhotoBlock;
use App\Models\Photo;
use App\Models\FileUpload;

trait ContentElementsTestTrait
{
    abstract protected function getModel();
    abstract protected function getClassname();

    public function test_a_page_can_have_many_content_elements()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();

        $page->refresh();

        $this->assertNotNull($page->contentElements);
        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
    }

    public function test_a_page_has_a_full_type_attribute()
    {
        $page = $this->getModel();
        $this->assertNotNull($page->full_type);
        $this->assertEquals(get_class($page), $page->full_type);
    }

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

        $page->saveContentElements($input);
        $page->refresh();

        $this->assertEquals(1, $page->contentElements->count());
        $content_element = $page->contentElements->first();
        $this->assertInstanceOf(ContentElement::class, $content_element);
        $this->assertEquals(Arr::get($input, 'content.body'), $content_element->body);
    }

    public function test_a_page_can_get_its_all_content_elements()
    {
        // this checks for the proper grouping of content elements by UUID
        // we also use all_content_elements in the content attribute logic for a page
        $page = get_class($this->getModel())::factory()->create();
        $page->publish();

        $published_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $published_content_element->{Str::plural($this->getClassname())}()->detach();
        $published_content_element->{Str::plural($this->getClassname())}()->attach($page, ['sort_order' => 1, 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

        $unpublished_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $unpublished_content_element->{Str::plural($this->getClassname())}()->detach();
        $unpublished_content_element->{Str::plural($this->getClassname())}()->attach($page, ['sort_order' => 1, 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

        $page->refresh();
        $this->assertNotNull($page->all_content_elements);
        $this->assertInstanceOf(Collection::class, $page->all_content_elements);
        $this->assertTrue($page->all_content_elements->contains('id', $unpublished_content_element->id));
        $this->assertTrue($page->all_content_elements->contains('id', $published_content_element->id));
    }

    public function test_a_page_can_get_its_published_content_elements()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $this->assertEquals(1, $content_element->{Str::plural($this->getClassname())}()->count());
        $page = $content_element->{Str::plural($this->getClassname())}()->first();
        $content_element->save();

        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
        $this->assertTrue($page->all_content_elements->contains('id', $content_element->id));
        $this->assertEquals($page->getDraftVersion()->id, $content_element->contentables->first()->version->id);

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

        $content_element->refresh();
        $this->assertNotNull($content_element->contentables->first()->version->published_at);
        $this->assertTrue($page->published_content_elements->contains('id', $content_element->id));

        $unlisted_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $page->contentElements()->attach($unlisted_content_element, ['sort_order' => 1, 'unlisted' => true, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->published_version_id]);

        $unpublished_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $page->contentElements()->attach($unpublished_content_element, ['sort_order' => 2, 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

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

        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
        $this->assertTrue($page->contentElements->contains('id', $unpublished_content_element->id));
        $this->assertTrue($page->contentElements->contains('id', $unlisted_content_element->id));

        $this->assertFalse($page->published_content_elements->contains('id', $unlisted_content_element->id));
        $this->assertFalse($page->published_content_elements->contains('id', $unpublished_content_element->id));
    }

    public function test_a_page_can_get_its_preview_content_elements()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $this->assertEquals(1, $content_element->{Str::plural($this->getClassname())}()->count());
        $page = $content_element->{Str::plural($this->getClassname())}()->first();
        $content_element->save();

        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
        $this->assertTrue($page->all_content_elements->contains('id', $content_element->id));
        $this->assertEquals($page->getDraftVersion()->id, $content_element->contentables->first()->version->id);
        $this->assertEquals(0, $page->pivot->unlisted);

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

        $this->assertNotNull($content_element->contentables->first()->version->published_at);
        $this->assertEquals(1, $page->contentElements->count());
        $this->assertTrue(editing());
        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
        request()->merge(['preview' => true]);
        $this->assertTrue($page->preview_content_elements->contains('id', $content_element->id));

        $unlisted_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $page->contentElements()->attach($unlisted_content_element, ['sort_order' => 1, 'unlisted' => true, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->published_version_id]);

        $unpublished_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $page->contentElements()->attach($unpublished_content_element, ['sort_order' => 2, 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

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

        $this->assertTrue($page->contentElements->contains('id', $content_element->id));
        $this->assertTrue($page->contentElements->contains('id', $unpublished_content_element->id));
        $this->assertTrue($page->contentElements->contains('id', $unlisted_content_element->id));

        $this->assertFalse($page->preview_content_elements->contains('id', $unlisted_content_element->id));
        $this->assertTrue($page->preview_content_elements->contains('id', $content_element->id));
        $this->assertTrue($page->preview_content_elements->contains('id', $unpublished_content_element->id));
    }

    public function test_a_page_has_a_type_attribute()
    {
        $page = $this->getModel();

        $this->assertNotNull($page->type);
        $this->assertEquals(Str::kebab(class_basename($this->getModel())), $page->type);
    }

    public function test_a_page_has_a_resource_attribute()
    {
        $page = $this->getModel();

        $this->assertNotNull($page->resource);
        $this->assertEquals(Str::kebab(Str::plural(class_basename($this->getModel()))), $page->resource);
    }

    public function test_a_page_can_get_its_photos()
    {
        $page = $this->getModel();

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

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

        $this->assertNotNull($page->photos);
        $this->assertTrue($page->photos->contains('id', $photo->id));
        $this->assertNotNull($page->photo);
        $this->assertInstanceOf(Photo::class, $page->photo);
        $this->assertEquals($photo->id, $page->photo->id);
    }

    public function test_a_page_can_get_its_teaser()
    {
        $page = $this->getModel();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $text_block = $content_element->content;

        $this->assertInstanceOf(TextBlock::class, $text_block);
        $page->publish();

        $this->assertNotNull($page->teaser);
        $this->assertEquals($text_block->body, $page->teaser);
    }
}
