<?php

namespace Tests\Feature;

use App\Models\Artwork;
use App\Models\Category;
use App\Models\ContentType;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class BrowseApiTest extends TestCase
{
    use RefreshDatabase;

    public function test_web_browse_renders_canonical_and_rel_prev_next_for_paginated_pages(): void
    {
        $user = User::factory()->create(['name' => 'Seo Author']);
        $contentType = ContentType::create([
            'name' => 'Skins',
            'slug' => 'skins',
            'description' => 'Skins content type',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Classic',
            'slug' => 'classic',
            'description' => 'Classic skins',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        for ($i = 1; $i <= 25; $i++) {
            $artwork = Artwork::factory()
                ->for($user)
                ->create([
                    'title' => 'Seo Item ' . $i,
                    'slug' => 'seo-item-' . $i,
                    'published_at' => now()->subMinutes($i),
                ]);
            $artwork->categories()->attach($category->id);
        }

        $response = $this->get('/explore?limit=12&grid=v2');
        $response->assertOk();

        $html = $response->getContent();
        $this->assertNotFalse($html);
        $this->assertStringContainsString('name="robots" content="index,follow"', $html);
        $this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?limit=12"\s*\/>/i', $html);
        preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $html, $canonicalMatches);
        $this->assertArrayHasKey(1, $canonicalMatches);
        $canonicalUrl = html_entity_decode((string) $canonicalMatches[1], ENT_QUOTES);
        $this->assertStringNotContainsString('grid=v2', $canonicalUrl);

        $secondPage = $this->get('/explore?limit=12&page=2&grid=v2');
        $secondPage->assertOk();
        $secondHtml = $secondPage->getContent();
        $this->assertNotFalse($secondHtml);
        $this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?[^"]*page=2/i', $secondHtml);
        preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $secondHtml, $secondCanonicalMatches);
        $this->assertArrayHasKey(1, $secondCanonicalMatches);
        $secondCanonicalUrl = html_entity_decode((string) $secondCanonicalMatches[1], ENT_QUOTES);
        $this->assertStringNotContainsString('grid=v2', $secondCanonicalUrl);
        $this->assertStringContainsString('page=2', $secondCanonicalUrl);

        $pageOne = $this->get('/explore?limit=12&page=1&grid=v2');
        $pageOne->assertOk();
        $pageOneHtml = $pageOne->getContent();
        $this->assertNotFalse($pageOneHtml);
        $this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?limit=12"\s*\/>/i', $pageOneHtml);
        preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $pageOneHtml, $pageOneCanonicalMatches);
        $this->assertArrayHasKey(1, $pageOneCanonicalMatches);
        $pageOneCanonicalUrl = html_entity_decode((string) $pageOneCanonicalMatches[1], ENT_QUOTES);
        $this->assertStringNotContainsString('page=1', $pageOneCanonicalUrl);
    }

    public function test_api_browse_supports_limit_and_cursor_pagination(): void
    {
        $user = User::factory()->create(['name' => 'Cursor Author']);

        $contentType = ContentType::create([
            'name' => 'Skins',
            'slug' => 'skins',
            'description' => 'Skins content type',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Winamp',
            'slug' => 'winamp',
            'description' => 'Winamp skins',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        for ($i = 1; $i <= 6; $i++) {
            $artwork = Artwork::factory()
                ->for($user)
                ->create([
                    'title' => 'Cursor Item ' . $i,
                    'slug' => 'cursor-item-' . $i,
                    'published_at' => now()->subMinutes($i),
                ]);

            $artwork->categories()->attach($category->id);
        }

        $first = $this->getJson('/api/v1/browse?limit=2');
        $first->assertOk();
        $first->assertJsonCount(2, 'data');

        $nextCursor = (string) data_get($first->json(), 'links.next', '');
        $this->assertNotEmpty($nextCursor);
        $this->assertStringContainsString('cursor=', $nextCursor);

        $second = $this->getJson($nextCursor);
        $second->assertOk();
        $second->assertJsonCount(2, 'data');

        $firstFirstSlug = data_get($first->json(), 'data.0.slug');
        $secondFirstSlug = data_get($second->json(), 'data.0.slug');
        $this->assertNotSame($firstFirstSlug, $secondFirstSlug);
    }

    public function test_api_browse_returns_public_artworks(): void
    {
        $user = User::factory()->create(['name' => 'Author One']);
        $contentType = ContentType::create([
            'name' => 'Wallpapers',
            'slug' => 'wallpapers',
            'description' => 'Wallpapers content type',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Abstract',
            'slug' => 'abstract',
            'description' => 'Abstract wallpapers',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        $artwork = Artwork::factory()
            ->for($user)
            ->create([
                'slug' => 'neon-city',
                'published_at' => now()->subDay(),
            ]);

        $artwork->categories()->attach($category->id);

        $response = $this->getJson('/api/v1/browse');

        $response->assertOk()
            ->assertJsonPath('data.0.slug', 'neon-city')
            ->assertJsonPath('data.0.category.slug', 'abstract')
            ->assertJsonPath('data.0.author.name', 'Author One');
    }

    public function test_web_browse_shows_artworks(): void
    {
        $user = User::factory()->create(['name' => 'Author Two']);
        $contentType = ContentType::create([
            'name' => 'Photography',
            'slug' => 'photography',
            'description' => 'Photos',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Nature',
            'slug' => 'nature',
            'description' => 'Nature photos',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        $artwork = Artwork::factory()
            ->for($user)
            ->create([
                'title' => 'Forest Light',
                'slug' => 'forest-light',
                'published_at' => now()->subDay(),
            ]);

        $artwork->categories()->attach($category->id);

        $response = $this->get('/explore');

        $response->assertOk();
        $response->assertSee('Forest Light');
        $response->assertSee('Author Two');

        $html = $response->getContent();
        $this->assertNotFalse($html);
        $this->assertStringContainsString('data-react-masonry-gallery', $html);
        $this->assertStringContainsString('data-gallery-type="browse"', $html);
        $this->assertStringContainsString('Forest Light', $html);
    }

    public function test_web_explore_does_not_lazy_load_presentation_relations_per_artwork(): void
    {
        DB::flushQueryLog();
        DB::enableQueryLog();

        $user = User::factory()->create(['name' => 'Explore Author']);
        $contentType = ContentType::create([
            'name' => 'Skins',
            'slug' => 'skins',
            'description' => 'Skins content type',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Minimal',
            'slug' => 'minimal',
            'description' => 'Minimal skins',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        for ($i = 1; $i <= 4; $i++) {
            $artwork = Artwork::factory()
                ->for($user)
                ->create([
                    'title' => 'Explore Item ' . $i,
                    'slug' => 'explore-item-' . $i,
                    'published_at' => now()->subMinutes($i),
                ]);

            $artwork->categories()->attach($category->id);
        }

        $response = $this->get('/explore?sort=latest');

        $response->assertOk()->assertSee('Explore Item 1');

        $queries = collect(DB::getQueryLog())->pluck('query')->all();

        $this->assertFalse(
            collect($queries)->contains(fn (string $query): bool => str_contains($query, 'from "users" where "users"."id" = ? limit 1')),
            'Explore should not lazy-load the artwork user relation per tile.'
        );

        $this->assertFalse(
            collect($queries)->contains(fn (string $query): bool => str_contains($query, 'from "profiles" where "profiles"."user_id" = ? limit 1')),
            'Explore should not lazy-load the artwork profile relation per tile.'
        );

        $this->assertFalse(
            collect($queries)->contains(fn (string $query): bool => str_contains($query, 'from "artwork_category" where "artwork_category"."artwork_id" = ?')),
            'Explore should not lazy-load artwork categories one tile at a time.'
        );

        DB::disableQueryLog();
    }

    public function test_web_browse_filters_out_artworks_that_are_no_longer_publicly_browsable(): void
    {
        $user = User::factory()->create(['name' => 'Visibility Author']);
        $contentType = ContentType::create([
            'name' => 'Skins',
            'slug' => 'skins',
            'description' => 'Skins content type',
        ]);

        $category = Category::create([
            'content_type_id' => $contentType->id,
            'name' => 'Minimal',
            'slug' => 'minimal',
            'description' => 'Minimal skins',
            'is_active' => true,
            'sort_order' => 1,
        ]);

        $visible = Artwork::factory()->for($user)->create([
            'title' => 'Visible Explore Item',
            'slug' => 'visible-explore-item',
            'published_at' => now()->subHour(),
            'is_public' => true,
            'is_approved' => true,
        ]);
        $visible->categories()->attach($category->id);

        $hidden = Artwork::factory()->for($user)->create([
            'title' => 'Hidden Explore Item',
            'slug' => 'hidden-explore-item',
            'published_at' => now()->subHour(),
            'is_public' => false,
            'is_approved' => false,
        ]);
        $hidden->categories()->attach($category->id);

        Cache::forever('explore.cache.version', 1);
        Cache::put(
            'explore.all.v1.trending.1',
            new LengthAwarePaginator(collect([$hidden, $visible]), 2, 24, 1),
            300,
        );

        $response = $this->get('/explore');

        $response->assertOk()
            ->assertSee('Visible Explore Item')
            ->assertDontSee('Hidden Explore Item');
    }
}
