<?php

namespace Tests\Feature\Livewire;

use App\Livewire\PlaylistOverview;
use App\Models\Episode;
use App\Models\Gallery;
use App\Models\Hentai;
use App\Models\Playlist;
use App\Models\PlaylistEpisode;
use App\Models\Studios;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\RefreshDatabaseState;
use Livewire\Livewire;
use Tests\TestCase;

class PlaylistOverviewTest extends TestCase
{
    use RefreshDatabase;

    /**
     * The repo's post-launch data-fix migrations (e.g. 2026_01_08_213625)
     * assume a populated production schema and fail on a fresh database, so
     * the "testing" database is provisioned from a production schema dump
     * instead of running migrate:fresh.
     */
    protected function migrateDatabases(): void
    {
        RefreshDatabaseState::$migrated = true;
    }

    private function makePlaylist(int $episodeCount = 1): array
    {
        $user = User::factory()->create();
        $playlist = Playlist::factory()->create(['user_id' => $user->id]);
        $hentai = Hentai::factory()->create();
        $studio = Studios::factory()->create();

        for ($i = 1; $i <= $episodeCount; $i++) {
            $episode = Episode::factory()->create([
                'hentai_id' => $hentai->id,
                'studios_id' => $studio->id,
                'title' => sprintf('Episode %02d', $i),
            ]);

            Gallery::factory()->create([
                'hentai_id' => $hentai->id,
                'episode_id' => $episode->id,
            ]);

            PlaylistEpisode::factory()->create([
                'playlist_id' => $playlist->id,
                'episode_id' => $episode->id,
                'position' => $i,
            ]);
        }

        return [$user, $playlist];
    }

    public function test_episodes_are_paginated(): void
    {
        [, $playlist] = $this->makePlaylist(30);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->assertViewHas('episodes', fn ($episodes) => $episodes->total() === 30
                && $episodes->count() === 25
                && $episodes->hasPages())
            ->assertSee('Episode 01')
            ->assertSee('Episode 25')
            ->assertDontSee('Episode 26');
    }

    public function test_per_page_selector_changes_page_size(): void
    {
        [, $playlist] = $this->makePlaylist(30);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->set('perPage', 100)
            ->assertViewHas('episodes', fn ($episodes) => $episodes->total() === 30
                && $episodes->count() === 30
                && ! $episodes->hasPages())
            ->assertSee('Episode 30');
    }

    public function test_per_page_is_restricted_to_allowed_values(): void
    {
        [, $playlist] = $this->makePlaylist(30);

        Livewire::withQueryParams(['perPage' => 999])
            ->test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->assertSet('perPage', 25)
            ->assertViewHas('episodes', fn ($episodes) => $episodes->perPage() === 25);
    }

    public function test_search_filters_episodes_and_resets_page(): void
    {
        [, $playlist] = $this->makePlaylist(30);

        $hentai = Hentai::factory()->create();
        $studio = Studios::factory()->create();
        $uniqueEpisode = Episode::factory()->create([
            'hentai_id' => $hentai->id,
            'studios_id' => $studio->id,
            'title' => 'ZebraUnicornSearch',
        ]);

        Gallery::factory()->create([
            'hentai_id' => $hentai->id,
            'episode_id' => $uniqueEpisode->id,
        ]);

        PlaylistEpisode::factory()->create([
            'playlist_id' => $playlist->id,
            'episode_id' => $uniqueEpisode->id,
            'position' => 31,
        ]);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('gotoPage', 2)
            ->assertSet('paginators.page', 2)
            ->set('search', 'ZebraUnicornSearch')
            ->assertSet('paginators.page', 1)
            ->assertSee('ZebraUnicornSearch')
            ->assertDontSee('Episode 02');
    }

    public function test_guest_sees_no_owner_controls(): void
    {
        [, $playlist] = $this->makePlaylist(3);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->assertDontSee('wire:click="moveUp')
            ->assertDontSee('wire:click="moveDown')
            ->assertDontSee('wire:click="remove');
    }

    public function test_only_owner_can_move_episodes(): void
    {
        [$user, $playlist] = $this->makePlaylist(3);
        $otherUser = User::factory()->create();

        $this->actingAs($otherUser);

        $second = PlaylistEpisode::where('playlist_id', $playlist->id)->where('position', 2)->first();

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('moveUp', $second->id);

        $this->assertSame(2, PlaylistEpisode::find($second->id)->position);
        $this->assertSame(
            [1, 2, 3],
            PlaylistEpisode::where('playlist_id', $playlist->id)->orderBy('position')->pluck('position')->all()
        );
    }

    public function test_owner_can_move_episode_up_and_down(): void
    {
        [$user, $playlist] = $this->makePlaylist(3);

        $this->actingAs($user);

        $second = PlaylistEpisode::where('playlist_id', $playlist->id)->where('position', 2)->first();

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('moveUp', $second->id)
            ->assertOk();

        $this->assertSame(1, PlaylistEpisode::find($second->id)->position);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('moveDown', $second->id)
            ->assertOk();

        $this->assertSame(2, PlaylistEpisode::find($second->id)->position);
        $this->assertSame(
            [1, 2, 3],
            PlaylistEpisode::where('playlist_id', $playlist->id)->orderBy('position')->pluck('position')->all()
        );
    }

    public function test_owner_can_remove_episode_and_positions_reorder(): void
    {
        [$user, $playlist] = $this->makePlaylist(5);

        $this->actingAs($user);

        $second = PlaylistEpisode::where('playlist_id', $playlist->id)->where('position', 2)->first();

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('remove', $second->id)
            ->assertOk();

        $this->assertDatabaseMissing('playlist_episodes', ['id' => $second->id]);
        $this->assertSame(
            [1, 2, 3, 4],
            PlaylistEpisode::where('playlist_id', $playlist->id)->orderBy('position')->pluck('position')->all()
        );
    }

    public function test_removing_last_item_on_last_page_clamps_page(): void
    {
        [$user, $playlist] = $this->makePlaylist(26);

        $this->actingAs($user);

        $last = PlaylistEpisode::where('playlist_id', $playlist->id)->where('position', 26)->first();

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('gotoPage', 2)
            ->assertSet('paginators.page', 2)
            ->call('remove', $last->id)
            ->assertSet('paginators.page', 1)
            ->assertViewHas('episodes', fn ($episodes) => $episodes->total() === 25 && ! $episodes->hasPages());
    }

    public function test_edit_name_and_toggle_visibility_still_work(): void
    {
        [$user, $playlist] = $this->makePlaylist(2);

        $this->actingAs($user);

        Livewire::test(PlaylistOverview::class, ['playlist_id' => $playlist->id])
            ->call('editName')
            ->set('editingPlaylistName', 'Renamed Playlist')
            ->call('updateName')
            ->call('toggleVisibility')
            ->assertOk();

        $this->assertDatabaseHas('playlists', [
            'id' => $playlist->id,
            'name' => 'Renamed Playlist',
            'is_private' => false,
        ]);
    }

    public function test_public_playlist_page_renders(): void
    {
        $user = User::factory()->create();
        $playlist = Playlist::factory()->create(['user_id' => $user->id, 'is_private' => false]);

        $this->get('/playlist/'.$playlist->id)->assertOk();

        $private = Playlist::factory()->create(['user_id' => $user->id, 'is_private' => true]);

        $this->get('/playlist/'.$private->id)->assertNotFound();
    }

    public function test_user_playlist_page_renders(): void
    {
        $user = User::factory()->create();
        $playlist = Playlist::factory()->create(['user_id' => $user->id, 'is_private' => true]);

        $this->actingAs($user)->get('/user/playlist/'.$playlist->id)->assertOk();

        $otherUser = User::factory()->create();

        $this->actingAs($otherUser)->get('/user/playlist/'.$playlist->id)->assertNotFound();
    }
}
