<?php

namespace Tests\Unit;

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

use App\Models\Tag;
use App\Models\User;

trait PublicationsTestTrait
{
    use WithFaker;
    abstract protected function getModel();
    abstract protected function getClassname();

    public function test_a_publication_has_a_full_slug_attribute()
    {
        $publication = $this->getModel();
        $version = $publication->versions->first();
        $version->name = 'Jimmy Page';
        $version->save();
        $publication->publish();

        $this->assertEquals(Str::plural($this->getClassname()).'/jimmy-page', $publication->full_slug);
    }

    public function test_a_publication_can_be_found_by_its_full_slug()
    {
        $publication = $this->getModel();
        $version = $publication->versions->first();
        $version->name = $this->faker->firstName;
        $version->save();
        $publication->publish();

        $publication_slug = $publication->full_slug;

        $classname = get_class($publication);
        $found_publication = (new $classname())->findByFullSlug($publication->full_slug);

        $this->assertInstanceOf($classname, $found_publication);
        $this->assertEquals($publication->id, $found_publication->id);
    }

    public function test_a_publication_has_a_slug_attribute()
    {
        $name = $this->faker->firstName;
        $publication = $this->getModel();
        $version = $publication->versions->first();
        $version->name = $name;
        $version->save();
        $publication->publish();

        $this->assertNotNull($publication->getSlug());
        $this->assertEquals(Str::kebab($name), $publication->getSlug());
    }

    public function test_publications_can_be_got()
    {
        $publication = $this->getModel();
        $publication->publish();

        $classname = get_class($publication);
        $publications = (new $classname())->getByTags([
            'limit' => 10,
            'descending' => true,
        ]);

        $this->assertTrue($publications->contains('id', $publication->id));
    }

    public function test_publications_get_can_be_filtered_by_tags()
    {
        $publication1 = $this->getModel();
        $publication2 = $this->getModel();

        $tag = Tag::factory()->create();

        $publication1->addTag($tag);

        $publication1->publish();
        $publication2->publish();

        $classname = get_class($publication1);
        $publications = (new $classname())->getByTags([
            'limit' => 10,
            'descending' => true,
        ]);
        $this->assertTrue($publications->contains('id', $publication1->id));
        $this->assertTrue($publications->contains('id', $publication2->id));

        $publications = (new $classname())->getByTags([
            'tags' => [$tag],
            'limit' => 10,
            'descending' => true,
        ]);
        $this->assertTrue($publications->contains('id', $publication1->id));
        $this->assertFalse($publications->contains('id', $publication2->id));
    }

    public function test_new_unpublished_publications_can_be_listed_by_publication_managers()
    {
        $publication = $this->getModel();

        $classname = get_class($publication);
        $publications = (new $classname())->getByTags([
            'limit' => 100,
            'descending' => true,
        ]);
        $this->assertFalse($publications->contains('id', $publication->id));

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

        $publications = (new $classname())->getByTags([
            'limit' => 100,
            'descending' => true,
        ]);
        $this->assertFalse($publications->contains('id', $publication->id));

        $user->addRole(Str::plural($this->getClassname()).'-manager');
        $user->refresh();

        $this->assertTrue($user->can('viewAny', $classname));

        $publications = (new $classname())->getByTags([
            'limit' => 100,
            'descending' => true,
        ]);
        $this->assertTrue($publications->contains('id', $publication->id));
    }

    public function test_a_publication_can_find_the_next_oldest_and_prev_oldest_publications()
    {
        $publication = $this->getModel();
        $prev_blog = $this->getModel();
        $next_blog = $this->getModel();

        $publication->publish();
        $prev_blog->publish();
        $next_blog->publish();

        $classname = $this->getClassname();

        $time = $this->faker->numberBetween(1000, 10000);

        $prev_blog->publishedVersion->published_at = now()->addSeconds($time);
        $prev_blog->publishedVersion->save();

        $publication->publishedVersion->published_at = now()->addSeconds($time + 1);
        $publication->publishedVersion->save();


        $next_blog->publishedVersion->published_at = now()->addSeconds($time + 2);
        $next_blog->publishedVersion->save();

        $publication->refresh();

        $this->assertTrue($next_blog->published_at > $publication->published_at);
        $this->assertTrue($next_blog->published_at > $prev_blog->published_at);
        $this->assertTrue($prev_blog->published_at < $publication->published_at);
        $this->assertTrue($prev_blog->published_at < $next_blog->published_at);

        $next_attribue = 'next_'.$classname;
        $previous_attribue = 'previous_'.$classname;
        $this->assertNotNull($publication->$next_attribue);
        $this->assertNotNull($publication->$previous_attribue);
        $this->assertEquals($next_blog->id, $publication->$next_attribue->id);
        $this->assertEquals($prev_blog->id, $publication->$previous_attribue->id);
    }

    public function test_a_publication_has_a_published_at_append()
    {
        $publication = $this->getModel();

        $data = $publication->toArray();
        $this->assertNull($publication->published_at);
        $this->assertNull(Arr::get($data, 'published_at'));

        $publication->publish();

        $data = $publication->toArray();
        $this->assertNotNull($publication->published_at);
        //$this->assertNotNull(Arr::get($data, 'published_at'));
    }

    public function test_a_publication_has_a_first_published_at_append()
    {
        $publication = $this->getModel();

        $data = $publication->toArray();
        $this->assertNull($publication->first_published_at);
        $this->assertNull(Arr::get($data, 'first_published_at'));

        $publication->publish();

        $data = $publication->toArray();
        $this->assertNotNull($publication->first_published_at);

        sleep(2);

        $draft_version = $publication->getDraftVersion();

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

        $this->assertNotNull($publication->published_at);
        $this->assertNotNull($publication->first_published_at);
        $this->assertTrue($publication->published_at->notEqualTo($publication->first_published_at));
    }

    public function test_a_publication_can_get_text_from_a_slug()
    {
        $classname = get_class($this->getModel());
        $slug = 'blogs/support-for-all';
        $result = (new $classname())->getTextFromSlug($slug);
        $this->assertEquals('Support For All', $result);
    }

    public function test_a_publication_has_a_type_attribute(): void
    {
        $publication = $this->getModel();
        $this->assertNotNull($publication->type);
        $publication_array = $this->getModel()->toArray();
        $this->assertNotNull(Arr::get($publication_array, 'type'));
    }
}
