<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;

use App\Models\Tag;
use App\Models\Blog;
use App\Models\Page;
use App\Models\PhotoBlock;
use App\Models\Photo;
use App\Models\FileUpload;
use App\Models\Livestream;
use App\Models\LivestreamList;
use App\Models\Announcement;
use App\Models\StaffProfile;
use App\Models\StaffProfileList;
use App\Models\PublicationList;
use App\Models\Inquiry;
use App\Models\InquiryForm;
use App\Models\Timetable;
use App\Models\TimetableContent;
use App\Models\Course;
use App\Models\CourseList;
use App\Models\Calendar;

class TagTest extends TestCase
{
    use WithFaker;

    public function test_a_tag_has_many_blogs()
    {
        $tag = Tag::factory()->create();
        $blog = Blog::factory()->create();

        $tag->blogs()->attach($blog);

        $tag->refresh();

        $this->assertEquals(1, $tag->blogs()->count());
        $this->assertInstanceOf(Blog::class, $tag->blogs()->first());
        $this->assertEquals($blog->id, $tag->blogs()->first()->id);
    }

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

        $tag->pages()->attach($page);

        $tag->refresh();

        $this->assertEquals(1, $tag->pages()->count());
        $this->assertInstanceOf(Page::class, $tag->pages()->first());
        $this->assertEquals($page->id, $tag->pages()->first()->id);
    }

    public function test_a_tag_can_be_found_or_created()
    {
        $name = $this->faker->firstName.$this->faker->randomNumber(3);

        $tag = (new Tag())->findOrCreateTag($name);

        $this->assertInstanceOf(Tag::class, $tag);

        $this->assertEquals($name, $tag->name);

        $this->assertEquals($tag->id, (new Tag())->findOrCreateTag($name)->id);

        $this->assertEquals($tag->id, (new Tag())->findOrCreateTag($tag->id)->id);
    }

    public function test_tag_can_be_found_or_created_from_an_array()
    {
        $tag_data = Tag::factory()->create()->toArray();

        $tag = (new Tag())->findOrCreateTag($tag_data);

        $this->assertInstanceOf(Tag::class, $tag);
        $this->assertEquals(Arr::get($tag_data, 'name'), $tag->name);
    }

    public function test_a_tag_is_always_returned_with_title_case_unless_all_uppercase()
    {
        $name = $this->faker->firstName.' '.$this->faker->lastName;

        $tag = (new Tag())->findOrCreateTag(strtolower($name));

        $this->assertEquals(Str::title($name), $tag->name);

        $tag2 = (new Tag())->findOrCreateTag($name);

        $this->assertEquals(Str::title($name), $tag2->name);

        $name = strtoupper($this->faker->firstName.' '.$this->faker->lastName);

        $tag3 = (new Tag())->findOrCreateTag($name);

        $this->assertEquals($name, $tag3->name);
    }

    public function test_a_tag_has_a_heiarchy()
    {
        $parent_tag = Tag::factory()->create([
            'parent_tag_id' => null,
        ]);

        $tag = Tag::factory()->create([
            'parent_tag_id' => $parent_tag->id,
        ]);

        $parent_tag->refresh();

        $this->assertInstanceOf(Tag::class, $tag->parentTag);
        $this->assertEquals(1, $parent_tag->tags->count());
        $this->assertTrue($parent_tag->tags->contains('id', $tag->id));
    }

    public function test_a_tag_has_a_label_attribute()
    {
        $parent_tag = Tag::factory()->create();
        $tag = Tag::factory()->create([
            'parent_tag_id' => $parent_tag->id,
        ]);

        $this->assertNotNull($tag->label);
        $this->assertTrue(Str::contains($tag->label, $parent_tag->name));

        $data = Tag::find($tag->id)->toArray();

        $this->assertNotNull(Arr::get($data, 'search_label'));

        $this->assertTrue(Str::contains(Arr::get($data, 'search_label'), $parent_tag->name));
    }

    public function test_a_tag_has_many_taggables(): void
    {
        $tag = Tag::factory()->create();

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

        $livestream = Livestream::factory()->create();
        $blog = Blog::factory()->create();
        $announcement = Announcement::factory()->create();
        $staff_profile = StaffProfile::factory()->create();
        $publication_list = PublicationList::factory()->create();
        $staff_profile_list = StaffProfileList::factory()->create();
        $inquiry = Inquiry::factory()->create();
        $timetable = Timetable::factory()->create();
        $inquiry_form = InquiryForm::factory()->create();
        $course = Course::factory()->create();
        $calendar = Calendar::factory()->create();
        $livestream_list = LivestreamList::factory()->create();
        $course_list = CourseList::factory()->create();
        $timetable_content = TimetableContent::factory()->create();

        $content_element->addTag($tag);
        $photo->addTag($tag);
        $page->addTag($tag);
        $blog->addTag($tag);
        $announcement->addTag($tag);
        $livestream->addTag($tag);
        $staff_profile->addTag($tag);
        $publication_list->addTag($tag);
        $staff_profile_list->addTag($tag);
        $inquiry->addTag($tag);
        $inquiry_form->addTag($tag);
        $timetable->addTag($tag);
        $course->addTag($tag);
        $calendar->addTag($tag);
        $livestream_list->addTag($tag);
        $course_list->addTag($tag);
        $timetable_content->addTag($tag);

        $tag->refresh();

        $this->assertEquals(1, $tag->contentElements->count());

        $this->assertNotNull($tag->items);
        $this->assertEquals(17, $tag->items->count());

        $taggable = $tag->items->first()->taggable;
        $this->assertNotNull($taggable);
        $this->assertTrue($tag->items->contains(function ($item) use ($content_element) {
            return get_class($item->taggable) === get_class($content_element) && $item->taggable->id === $content_element->id;
        }));
    }
}
