<?php

declare(strict_types=1);

use App\Models\Artwork;
use App\Models\ArtworkFeature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Inertia\Testing\AssertableInertia;
use Klevze\ControlPanel\Models\Admin\AdminVerification;
use Klevze\ControlPanel\Core\Structs\MenuRootItem;
use Klevze\ControlPanel\Framework\Core\Menu as ControlPanelMenu;

uses(RefreshDatabase::class);

function createControlPanelAdmin(): User
{
    $admin = User::factory()->create(['role' => 'admin']);
    $admin->forceFill([
        'isAdmin' => true,
        'activated' => true,
    ])->save();

    AdminVerification::createForUser($admin->fresh());

    return $admin->fresh();
}

function adminArtwork(array $attributes = []): Artwork
{
    return Artwork::factory()->create(array_merge([
        'title' => 'Featured Artwork ' . fake()->unique()->words(2, true),
        'slug' => 'featured-artwork-' . fake()->unique()->numberBetween(1000, 9999),
        'is_public' => true,
        'is_approved' => true,
        'published_at' => now()->subHour(),
        'has_missing_thumbnails' => false,
    ], $attributes));
}

function featureRow(Artwork $artwork, array $attributes = []): ArtworkFeature
{
    return ArtworkFeature::query()->create(array_merge([
        'artwork_id' => $artwork->id,
        'priority' => 100,
        'featured_at' => now()->subHour(),
        'expires_at' => null,
        'is_active' => true,
    ], $attributes));
}

function medalScore(Artwork $artwork, int $score30d): void
{
    DB::table('artwork_medal_stats')->insert([
        'artwork_id' => $artwork->id,
        'gold_count' => 0,
        'silver_count' => 0,
        'bronze_count' => 0,
        'score_total' => $score30d,
        'score_7d' => $score30d,
        'score_30d' => $score30d,
        'last_medaled_at' => now(),
        'created_at' => now(),
        'updated_at' => now(),
    ]);
}

it('blocks non staff users from the featured artworks admin area', function (): void {
    $user = User::factory()->create(['role' => 'user']);
    $user->forceFill([
        'isAdmin' => false,
        'activated' => true,
    ])->save();

    $this->actingAs($user)->actingAs($user, 'controlpanel')
        ->get(route('admin.cp.artworks.featured.main'))
        ->assertRedirect(route('cp.login'));
});

it('registers the featured artworks entry in the cpad menu', function (): void {
    $sidebarMenu = collect(app(ControlPanelMenu::class)->getSidebarMenu());

    $editorialRoot = $sidebarMenu
        ->first(fn ($item): bool => $item instanceof MenuRootItem && $item->getName() === 'Artworks');

    expect($editorialRoot)->toBeInstanceOf(MenuRootItem::class);

    $featuredItem = collect($editorialRoot->getItems())
        ->first(fn ($item): bool => ($item->name ?? null) === 'Featured Artworks');

    expect($featuredItem)->not->toBeNull()
        ->and($featuredItem->mainRoute)->toBe('admin.cp.artworks.featured.main')
        ->and($featuredItem->icon)->toBe('fas fa-star');
});

it('renders the featured artworks admin index with the current winner summary', function (): void {
    $admin = createControlPanelAdmin();
    $owner = User::factory()->create(['username' => 'winnermaker']);
    $higherMedal = adminArtwork(['user_id' => $owner->id, 'title' => 'Higher Medal Winner']);
    $runnerUp = adminArtwork(['user_id' => $owner->id, 'title' => 'Runner Up']);

    featureRow($higherMedal, ['priority' => 100, 'featured_at' => now()->subHour()]);
    featureRow($runnerUp, ['priority' => 100, 'featured_at' => now()->subHour()]);
    medalScore($higherMedal, 12);
    medalScore($runnerUp, 3);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->get(route('admin.cp.artworks.featured.main'))
        ->assertOk()
        ->assertInertia(fn (AssertableInertia $page) => $page
            ->component('Collection/FeaturedArtworksAdmin')
            ->where('winner.artwork.id', $higherMedal->id)
            ->where('winner.medals.score_30d', 12)
            ->where('winner.selection_reason', 'Tied on priority, won on higher 30-day medal score.')
            ->where('entries.0.is_winner', true)
            ->where('entries.0.artwork.id', $higherMedal->id)
            ->where('endpoints.store', route('admin.cp.artworks.featured.store')));
});

it('allows admins to create featured rows', function (): void {
    $admin = createControlPanelAdmin();
    $artwork = adminArtwork();

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->postJson(route('admin.cp.artworks.featured.store'), [
            'artwork_id' => $artwork->id,
            'priority' => 220,
            'featured_at' => now()->toISOString(),
            'expires_at' => now()->addDay()->toISOString(),
            'is_active' => true,
        ])
        ->assertOk()
        ->assertJsonPath('winner.artwork.id', $artwork->id)
        ->assertJsonPath('stats.total', 1);

    $feature = ArtworkFeature::query()->firstOrFail();
    expect((int) $feature->artwork_id)->toBe($artwork->id)
        ->and((int) $feature->priority)->toBe(220)
        ->and((int) $feature->created_by)->toBe($admin->id)
        ->and((bool) $feature->is_active)->toBeTrue();
});

it('allows admins to update featured rows', function (): void {
    $admin = createControlPanelAdmin();
    $feature = featureRow(adminArtwork(), [
        'priority' => 50,
        'featured_at' => now()->subDays(2),
        'is_active' => true,
    ]);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.update', ['feature' => $feature->id]), [
            'priority' => 180,
            'featured_at' => now()->subHour()->toISOString(),
            'expires_at' => now()->addHours(6)->toISOString(),
            'is_active' => false,
        ])
        ->assertOk()
        ->assertJsonPath('entries.0.priority', 180)
        ->assertJsonPath('entries.0.is_active', false);

    $fresh = $feature->fresh();
    expect((int) $fresh->priority)->toBe(180)
        ->and((bool) $fresh->is_active)->toBeFalse()
        ->and($fresh->expires_at)->not->toBeNull();
});

it('allows admins to activate and deactivate featured rows', function (): void {
    $admin = createControlPanelAdmin();
    $feature = featureRow(adminArtwork(), ['is_active' => true]);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.toggle', ['feature' => $feature->id]))
        ->assertOk()
        ->assertJsonPath('entries.0.is_active', false);

    expect($feature->fresh()->is_active)->toBeFalse();
});

it('allows admins to force and unforce the homepage hero from the featured pool', function (): void {
    $admin = createControlPanelAdmin();
    $owner = User::factory()->create(['username' => 'forcehero']);
    $naturalWinner = adminArtwork(['user_id' => $owner->id, 'title' => 'Natural Winner']);
    $forcedArtwork = adminArtwork(['user_id' => $owner->id, 'title' => 'Forced Winner']);

    $naturalFeature = featureRow($naturalWinner, ['priority' => 300, 'featured_at' => now()->subHour()]);
    $forcedFeature = featureRow($forcedArtwork, ['priority' => 100, 'featured_at' => now()->subDays(2)]);

    medalScore($naturalWinner, 50);
    medalScore($forcedArtwork, 1);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.force-hero', ['feature' => $forcedFeature->id]))
        ->assertOk()
        ->assertJsonPath('winner.artwork.id', $forcedArtwork->id)
        ->assertJsonPath('winner.is_force_hero', true)
        ->assertJsonPath('winner.selection_reason', 'Forced hero override is enabled for this featured artwork.');

    expect($forcedFeature->fresh()->force_hero)->toBeTrue()
        ->and($naturalFeature->fresh()->force_hero)->toBeFalse()
        ->and(ArtworkFeature::query()->where('force_hero', true)->count())->toBe(1);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.force-hero', ['feature' => $forcedFeature->id]))
        ->assertOk()
        ->assertJsonPath('winner.artwork.id', $naturalWinner->id)
        ->assertJsonPath('winner.is_force_hero', false)
        ->assertJsonPath('winner.selection_reason', 'Highest priority among active, eligible featured artworks.');

    expect($forcedFeature->fresh()->force_hero)->toBeFalse()
        ->and(ArtworkFeature::query()->where('force_hero', true)->count())->toBe(0);
});

it('returns a forced hero as the admin winner even when standard artwork eligibility fails', function (): void {
    $admin = createControlPanelAdmin();
    $owner = User::factory()->create(['username' => 'forceheromissingpreview']);
    $naturalWinner = adminArtwork(['user_id' => $owner->id, 'title' => 'Natural Winner']);
    $forcedArtwork = adminArtwork(['user_id' => $owner->id, 'title' => 'Forced Missing Preview', 'has_missing_thumbnails' => true]);

    featureRow($naturalWinner, ['priority' => 300, 'featured_at' => now()->subHour()]);
    $forcedFeature = featureRow($forcedArtwork, ['priority' => 100, 'featured_at' => now()->subDays(2)]);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.force-hero', ['feature' => $forcedFeature->id]))
        ->assertOk()
        ->assertJsonPath('winner.artwork.id', $forcedArtwork->id)
        ->assertJsonPath('winner.is_force_hero', true)
        ->assertJsonPath('winner.selection_reason', 'Forced hero override is enabled for this featured artwork.');

    expect(ArtworkFeature::query()->where('force_hero', true)->count())->toBe(1);
});

it('allows admins to delete featured rows', function (): void {
    $admin = createControlPanelAdmin();
    $feature = featureRow(adminArtwork());

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->deleteJson(route('admin.cp.artworks.featured.delete', ['feature' => $feature->id]))
        ->assertOk()
        ->assertJsonPath('stats.total', 0);

    expect(ArtworkFeature::query()->count())->toBe(0)
        ->and(ArtworkFeature::withTrashed()->count())->toBe(1);
});

it('marks expired and ineligible rows on the index page', function (): void {
    $admin = createControlPanelAdmin();
    $privateArtwork = adminArtwork([
        'title' => 'Private Artwork',
        'is_public' => false,
        'visibility' => Artwork::VISIBILITY_PRIVATE,
    ]);
    $expiredArtwork = adminArtwork(['title' => 'Expired Artwork']);

    featureRow($privateArtwork, ['priority' => 300]);
    featureRow($expiredArtwork, ['priority' => 200, 'expires_at' => now()->subMinute()]);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->get(route('admin.cp.artworks.featured.main'))
        ->assertOk()
        ->assertInertia(fn (AssertableInertia $page) => $page
            ->component('Collection/FeaturedArtworksAdmin')
            ->where('entries.0.artwork.id', $privateArtwork->id)
            ->where('entries.0.eligibility.is_eligible', false)
            ->where('entries.0.eligibility.reasons.0', 'Private')
            ->where('entries.1.artwork.id', $expiredArtwork->id)
            ->where('entries.1.is_expired', true)
            ->where('stats.expired', 1)
            ->where('stats.ineligible', 2));
});

it('clears homepage hero cache after create update toggle and delete actions', function (): void {
    $admin = createControlPanelAdmin();
    $artwork = adminArtwork();

    Cache::put('homepage.hero', ['stale' => true], 600);

    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->postJson(route('admin.cp.artworks.featured.store'), [
            'artwork_id' => $artwork->id,
            'priority' => 100,
            'featured_at' => now()->toISOString(),
            'expires_at' => null,
            'is_active' => true,
        ])
        ->assertOk();

    expect(Cache::has('homepage.hero'))->toBeFalse();

    $feature = ArtworkFeature::query()->firstOrFail();

    Cache::put('homepage.hero', ['stale' => true], 600);
    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.update', ['feature' => $feature->id]), [
            'priority' => 110,
            'featured_at' => now()->addMinute()->toISOString(),
            'expires_at' => now()->addDay()->toISOString(),
            'is_active' => true,
        ])
        ->assertOk();
    expect(Cache::has('homepage.hero'))->toBeFalse();

    Cache::put('homepage.hero', ['stale' => true], 600);
    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->patchJson(route('admin.cp.artworks.featured.toggle', ['feature' => $feature->id]))
        ->assertOk();
    expect(Cache::has('homepage.hero'))->toBeFalse();

    Cache::put('homepage.hero', ['stale' => true], 600);
    $this->actingAs($admin)->actingAs($admin, 'controlpanel')
        ->deleteJson(route('admin.cp.artworks.featured.delete', ['feature' => $feature->id]))
        ->assertOk();
    expect(Cache::has('homepage.hero'))->toBeFalse();
});