<?php

namespace Tests\Feature;

use App\Models\Label;
use App\Models\Service;
use App\Models\ServiceAgendaItem;
use App\Models\ServiceSong;
use App\Models\Setting;
use App\Models\Slide;
use App\Models\Song;
use App\Services\PlaylistExportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use ProPresenter\Parser\ProPlaylistReader;
use Tests\Support\InspectsSlideFillMedia;
use Tests\TestCase;

final class FullPlaylistExportTest extends TestCase
{
    use InspectsSlideFillMedia;
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        Storage::fake('public');
    }

    public function test_full_service_playlist_includes_all_features_in_correct_order(): void
    {
        Setting::set('namenseinblender_macro_name', 'Namenseinblender');
        Setting::set('namenseinblender_macro_uuid', 'macro-uuid-1');
        Setting::set('namenseinblender_macro_collection_name', '--MAIN--');
        Setting::set('namenseinblender_macro_collection_uuid', 'collection-uuid-1');

        Storage::disk('public')->put('slides/kv.jpg', 'keyvisual-image');
        Storage::disk('public')->put('slides/bg.jpg', 'background-image');
        Storage::disk('public')->put('slides/sermon1.jpg', 'sermon-image-1');
        Storage::disk('public')->put('slides/sermon2.jpg', 'sermon-image-2');

        $service = Service::factory()->create([
            'title' => 'Voller Gottesdienst',
            'date' => now(),
            'key_visual_filename' => 'slides/kv.jpg',
            'background_filename' => 'slides/bg.jpg',
            'moderator_name' => 'Moderator Max',
            'preacher_name' => 'Pastor Paul',
            'preacher_name_override' => null,
            'has_agenda' => true,
        ]);

        $song = $this->createSongWithContent('Großer Gott');
        $serviceSong = ServiceSong::create([
            'service_id' => $service->id,
            'song_id' => $song->id,
            'cts_song_name' => 'Großer Gott',
            'order' => 1,
        ]);
        ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Großer Gott',
            'service_song_id' => $serviceSong->id,
            'sort_order' => 1,
            'is_before_event' => false,
        ]);

        $sermonItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Predigt',
            'service_song_id' => null,
            'sort_order' => 2,
            'is_before_event' => false,
        ]);
        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $sermonItem->id,
            'type' => 'sermon',
            'original_filename' => 'sermon1.jpg',
            'stored_filename' => 'slides/sermon1.jpg',
            'sort_order' => 0,
        ]);
        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $sermonItem->id,
            'type' => 'sermon',
            'original_filename' => 'sermon2.jpg',
            'stored_filename' => 'slides/sermon2.jpg',
            'sort_order' => 1,
        ]);

        $slideCountBefore = Slide::count();

        $result = app(PlaylistExportService::class)->generatePlaylist($service);
        $playlist = ProPlaylistReader::read($result['path']);
        $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());

        $moderatorIndex = array_search('Moderator - Moderator Max', $names, true);
        $songIndex = array_search('Großer Gott', $names, true);
        $introIndex = array_search('Prediger - Pastor Paul', $names, true);
        $sermonIndex = array_search('Predigt', $names, true);

        $this->assertNotFalse($moderatorIndex, 'Moderator nametag missing');
        $this->assertNotFalse($songIndex, 'Song presentation missing');
        $this->assertNotFalse($introIndex, 'Prediger intro (keyvisual + nametag) missing');
        $this->assertNotFalse($sermonIndex, 'Predigt (sermon slides) missing');

        $this->assertSame(0, $moderatorIndex, 'Moderator nametag must be first');
        $this->assertLessThan($songIndex, $moderatorIndex);
        $this->assertLessThan($introIndex, $songIndex);
        $this->assertLessThan($sermonIndex, $introIndex, 'Prediger intro must come before sermon slides');

        $songParser = $playlist->getEmbeddedSong('Großer Gott.pro');
        $this->assertNotNull($songParser, 'Embedded song .pro missing');
        $songSlides = $this->allParserSlides($songParser);
        $this->assertNotEmpty($songSlides);
        foreach ($songSlides as $slide) {
            // Service background stays a BACKGROUND media action behind the song text.
            $this->assertTrue($slide->hasBackgroundMedia(), 'Song slide must have background action');
            $this->assertSame('BACKGROUND.jpg', $slide->getBackgroundMediaUrl());
            $this->assertTrue($this->elementHasTextAt($slide, 0));
            $this->assertSame([], $this->fillMediaUrls($slide));
            $this->assertFalse($this->hasForegroundImageMediaAction($slide));
        }

        $embeddedMedia = $playlist->getEmbeddedMediaFiles();
        $this->assertArrayHasKey('BACKGROUND.jpg', $embeddedMedia, 'Background must be embedded under fixed name');
        $this->assertSame('background-image', $embeddedMedia['BACKGROUND.jpg']);
        $this->assertArrayHasKey('KEY_VISUAL.jpg', $embeddedMedia, 'Key-visual must be embedded under fixed name');
        $this->assertSame('keyvisual-image', $embeddedMedia['KEY_VISUAL.jpg']);

        $sermonParser = $playlist->getEmbeddedSong('Predigt.pro');
        $this->assertNotNull($sermonParser, 'Embedded sermon .pro missing');
        $sermonSlides = $this->allParserSlides($sermonParser);
        $this->assertCount(2, $sermonSlides);
        foreach ($sermonSlides as $slide) {
            // Uploaded sermon image is a content element with the service background
            // behind it as a BACKGROUND media action (background is NOT a content element).
            $this->assertTrue($slide->hasBackgroundMedia(), 'Sermon slide must have background action');
            $this->assertSame('BACKGROUND.jpg', $slide->getBackgroundMediaUrl());
            $this->assertNotEmpty($this->fillMediaUrls($slide));
            $this->assertNotContains('BACKGROUND.jpg', $this->fillMediaUrls($slide));
            $this->assertFalse($this->hasForegroundImageMediaAction($slide));
        }

        $this->assertSame($slideCountBefore, Slide::count());

        $this->cleanupTempDir($result['temp_dir']);
    }

    public function test_without_macro_no_nametags_and_sermon_keeps_keyvisual_and_slides(): void
    {
        Setting::set('namenseinblender_macro_name', '');

        Storage::disk('public')->put('slides/kv.jpg', 'keyvisual-image');
        Storage::disk('public')->put('slides/sermon1.jpg', 'sermon-image-1');
        Storage::disk('public')->put('slides/sermon2.jpg', 'sermon-image-2');

        $service = Service::factory()->create([
            'title' => 'Ohne Macro',
            'date' => now(),
            'key_visual_filename' => 'slides/kv.jpg',
            'background_filename' => null,
            'moderator_name' => 'Moderator Max',
            'preacher_name' => 'Pastor Paul',
            'preacher_name_override' => null,
            'has_agenda' => true,
        ]);

        $sermonItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Predigt',
            'service_song_id' => null,
            'sort_order' => 1,
            'is_before_event' => false,
        ]);
        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $sermonItem->id,
            'type' => 'sermon',
            'original_filename' => 'sermon1.jpg',
            'stored_filename' => 'slides/sermon1.jpg',
            'sort_order' => 0,
        ]);
        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $sermonItem->id,
            'type' => 'sermon',
            'original_filename' => 'sermon2.jpg',
            'stored_filename' => 'slides/sermon2.jpg',
            'sort_order' => 1,
        ]);

        $slideCountBefore = Slide::count();

        $result = app(PlaylistExportService::class)->generatePlaylist($service);
        $playlist = ProPlaylistReader::read($result['path']);
        $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());

        $this->assertNotContains('Moderator', $names);
        $this->assertNotContains('Predigername', $names);

        $kvIndex = array_search('Keyvisual-Predigt', $names, true);
        $sermonIndex = array_search('Predigt', $names, true);
        $this->assertNotFalse($kvIndex, 'Keyvisual-Predigt entry missing');
        $this->assertNotFalse($sermonIndex, 'Predigt entry missing');
        $this->assertLessThan($sermonIndex, $kvIndex, 'Keyvisual must come before sermon slides');

        $sermonParser = $playlist->getEmbeddedSong('Predigt.pro');
        $this->assertNotNull($sermonParser);
        $this->assertCount(2, $this->allParserSlides($sermonParser));

        $this->assertSame($slideCountBefore, Slide::count());

        $this->cleanupTempDir($result['temp_dir']);
    }

    public function test_service_without_agenda_falls_back_to_legacy_export_without_crash(): void
    {
        Setting::set('namenseinblender_macro_name', 'Namenseinblender');

        $service = Service::factory()->create([
            'title' => 'Ohne Agenda',
            'date' => now(),
            'key_visual_filename' => null,
            'background_filename' => null,
            'has_agenda' => false,
        ]);

        $song = $this->createSongWithContent('Legacy Lied');
        ServiceSong::create([
            'service_id' => $service->id,
            'song_id' => $song->id,
            'cts_song_name' => 'Legacy Lied',
            'order' => 1,
        ]);

        $slideCountBefore = Slide::count();

        $result = app(PlaylistExportService::class)->generatePlaylist($service);

        $this->assertArrayHasKey('path', $result);
        $this->assertArrayHasKey('filename', $result);
        $this->assertFileExists($result['path']);

        $playlist = ProPlaylistReader::read($result['path']);
        $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());

        $this->assertNotContains('Moderator', $names);
        $this->assertNotContains('Predigername', $names);
        $this->assertNotContains('Keyvisual-Predigt', $names);

        $this->assertNotNull($playlist->getEmbeddedSong('Legacy Lied.pro'));

        $this->assertSame($slideCountBefore, Slide::count());

        $this->cleanupTempDir($result['temp_dir']);
    }

    private function createSongWithContent(string $title): Song
    {
        $song = Song::create([
            'title' => $title,
            'ccli_id' => fake()->unique()->numerify('#####'),
            'author' => 'Test Author',
            'copyright_text' => 'Test Publisher',
        ]);

        $label = Label::firstOrCreate(
            ['name' => 'Verse 1 - '.$title],
            ['color' => '#2196F3'],
        );
        $section = $song->sections()->create(['label_id' => $label->id, 'order' => 0]);
        $section->slides()->create(['order' => 0, 'text_content' => 'Erste Zeile']);

        $arrangement = $song->arrangements()->create(['name' => 'normal', 'is_default' => true]);
        $arrangement->arrangementSections()->create(['song_section_id' => $section->id, 'order' => 0]);

        return $song;
    }

    private function allParserSlides(\ProPresenter\Parser\Song $parserSong): array
    {
        $slides = [];

        foreach ($parserSong->getGroups() as $group) {
            foreach ($parserSong->getSlidesForGroup($group) as $slide) {
                $slides[] = $slide;
            }
        }

        return $slides;
    }

    private function cleanupTempDir(string $dir): void
    {
        if (! is_dir($dir)) {
            return;
        }

        $items = scandir($dir);
        if ($items === false) {
            return;
        }

        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $dir.'/'.$item;
            is_dir($path) ? $this->cleanupTempDir($path) : unlink($path);
        }

        rmdir($dir);
    }
}
