<?php

namespace Tests\Tenant;

use App\PageRepository;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Mtc\ContentManager\PageStatus;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\ResourceView;
use Tests\TenantTestCase;

class PageRepositoryTest extends TenantTestCase
{
    public function testListing()
    {
        $newest_foo = Page::factory(1)->create([
            'category' => 'foo',
            'published_at' => Carbon::now()->subDays(),
            'status' => PageStatus::PUBLISHED->value,
        ]);
        Page::factory(5)->create([
            'category' => 'foo',
            'published_at' => Carbon::now()->subDays(2),
            'status' => PageStatus::PUBLISHED->value,
        ]);
        $oldest_foo = Page::factory(1)->create(['category' => 'foo', 'published_at' => Carbon::now()->subDays(3)]);

        Page::factory(1)->create(['category' => 'baz', 'published_at' => Carbon::now()->subDays(4)]);
        Page::factory(10)->create(['category' => 'baz', 'published_at' => Carbon::now()->subDays(5)]);
        $oldest_baz = Page::factory(1)->create(['category' => 'baz', 'published_at' => Carbon::now()->subDays(5)]);

        $repository = (new PageRepository());
        $listing = $repository->listing('foo');
        $this->assertInstanceOf(Collection::class, $listing);
        $this->assertEquals(6, $listing->count());
        $this->assertTrue($newest_foo->first()->is($listing->first()));


        $listing = $repository->listing('foo', 10);
        $this->assertEquals(6, $listing->count());
    }

    public function testListingOrderByPopular()
    {
        $page1 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(5),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(4),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page3 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(3),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // Create view records - page2 should have most views
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 5,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page2->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 100,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page3->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 50,
        ]);

        $repository = new PageRepository();
        $listing = $repository->listing('blog', 10, 'popular');

        $this->assertInstanceOf(Collection::class, $listing);
        $this->assertEquals(3, $listing->count());

        // Most popular (page2) should be first
        $this->assertEquals($page2->id, $listing->first()->id);
        $this->assertEquals($page3->id, $listing->get(1)->id);
        $this->assertEquals($page1->id, $listing->last()->id);
    }

    public function testListingOrderByTrending()
    {
        $page1 = Page::factory()->create([
            'category' => 'news',
            'published_at' => Carbon::now()->subDays(5),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'news',
            'published_at' => Carbon::now()->subDays(4),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page3 = Page::factory()->create([
            'category' => 'news',
            'published_at' => Carbon::now()->subDays(3),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // Create old view records (outside 30 day window) - should not count for trending
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->subDays(60)->format('Y-m-d'),
            'hits' => 1000,
        ]);

        // Create recent view records (within 30 day window)
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 5,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page2->id,
            'date' => Carbon::now()->subDays(10)->format('Y-m-d'),
            'hits' => 80,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page3->id,
            'date' => Carbon::now()->subDays(5)->format('Y-m-d'),
            'hits' => 30,
        ]);

        $repository = new PageRepository();
        $listing = $repository->listing('news', 10, 'trending');

        $this->assertInstanceOf(Collection::class, $listing);
        $this->assertEquals(3, $listing->count());

        // Trending (page2 with 80 recent hits) should be first
        $this->assertEquals($page2->id, $listing->first()->id);
        $this->assertEquals($page3->id, $listing->get(1)->id);
        $this->assertEquals($page1->id, $listing->last()->id);
    }

    public function testTrending()
    {
        $page1 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(5),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(4),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page3 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(3),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // Old views outside the window — should not count
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->subDays(60)->format('Y-m-d'),
            'hits' => 1000,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->subDays(5)->format('Y-m-d'),
            'hits' => 5,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page2->id,
            'date' => Carbon::now()->subDays(10)->format('Y-m-d'),
            'hits' => 80,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page3->id,
            'date' => Carbon::now()->subDays(5)->format('Y-m-d'),
            'hits' => 30,
        ]);

        $repository = new PageRepository();

        // Default days (30), default page_count (5)
        $results = $repository->trending('blog');
        $this->assertInstanceOf(Collection::class, $results);
        $this->assertEquals(3, $results->count());
        $this->assertEquals($page2->id, $results->first()->id);
        $this->assertEquals($page3->id, $results->get(1)->id);
        $this->assertEquals($page1->id, $results->last()->id);
    }

    public function testTrendingRespectsPageCount()
    {
        Page::factory(5)->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $results = (new PageRepository())->trending('blog', 2);
        $this->assertEquals(2, $results->count());
    }

    public function testTrendingWithZeroDaysIncludesAllViews()
    {
        $page1 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(5),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(4),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // Old views — should count when days = 0
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->subDays(365)->format('Y-m-d'),
            'hits' => 500,
        ]);

        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page2->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 10,
        ]);

        $results = (new PageRepository())->trending('blog', 5, 0);
        $this->assertEquals($page1->id, $results->first()->id);
    }

    public function testTrendingFiltersByAuthorSlug()
    {
        // Source page whose slug is used to look up the author_id
        $sourcePage = Page::factory()->create([
            'category' => 'blog',
            'author_id' => 1,
            'slug' => 'author-source-page',
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page1 = Page::factory()->create([
            'category' => 'blog',
            'author_id' => 1,
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'blog',
            'author_id' => 2,
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $repository = new PageRepository();

        // Without filter, all three pages are returned
        $this->assertCount(3, $repository->trending('blog'));

        // Filtering by slug — only pages with author_id matching source page returned
        $results = $repository->trending('blog', 5, null, 'author-source-page', 'author');
        $this->assertTrue($results->every(fn($p) => $p->author_id === 1));
        $this->assertFalse($results->contains(fn($p) => $p->id === $page2->id));
    }

    public function testTrendingWithUnknownSlugReturnsAll()
    {
        Page::factory(2)->create([
            'category' => 'blog',
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $results = (new PageRepository())->trending('blog', 5, null, 'non-existent-slug', 'author');
        $this->assertCount(2, $results);
    }

    public function testTrendingWithNonAuthorFilterTypeIgnoresSlug()
    {
        $sourcePage = Page::factory()->create([
            'category' => 'blog',
            'author_id' => 1,
            'slug' => 'some-page-slug',
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        Page::factory()->create([
            'category' => 'blog',
            'author_id' => 2,
            'published_at' => Carbon::now()->subDays(1),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // With a non-author filter_type the author lookup is ignored but the slug is still excluded
        $results = (new PageRepository())->trending('blog', 5, null, 'some-page-slug', 'other');
        $this->assertCount(1, $results);
    }

    public function testListingWithNoViewsOrdersCorrectly()
    {
        $page1 = Page::factory()->create([
            'category' => 'articles',
            'published_at' => Carbon::now()->subDays(5),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        $page2 = Page::factory()->create([
            'category' => 'articles',
            'published_at' => Carbon::now()->subDays(4),
            'status' => PageStatus::PUBLISHED->value,
        ]);

        // Only page1 has views
        ResourceView::query()->create([
            'viewable_type' => 'page',
            'viewable_id' => $page1->id,
            'date' => Carbon::now()->format('Y-m-d'),
            'hits' => 10,
        ]);

        $repository = new PageRepository();
        $listing = $repository->listing('articles', 10, 'popular');

        $this->assertEquals(2, $listing->count());
        // page1 with views should be first
        $this->assertEquals($page1->id, $listing->first()->id);
        // page2 with no views should be second
        $this->assertEquals($page2->id, $listing->last()->id);
    }
}
