<?php

namespace Tests\Feature;

use App\Models\Label;
use App\Models\Service;
use App\Models\Song;
use App\Services\ProExportService;
use App\Services\SlideMediaElementBuilder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use ProPresenter\Parser\ProFileGenerator;
use ProPresenter\Parser\ProFileReader;
use ProPresenter\Parser\Slide as ParserSlide;
use Tests\Support\InspectsSlideFillMedia;
use Tests\TestCase;

/**
 * Verifies the COMBINED image rule enforced by SlideMediaElementBuilder:
 *
 *   1. Song slide             → text element + BACKGROUND media action, NO image element.
 *   2. Uploaded content slide → full-frame fill.media content element, NO foreground
 *                               action (a service background, if set, stays a
 *                               BACKGROUND media action behind the element).
 *   3. Key-visual slide       → BACKGROUND media action only, NO content element.
 *   4. Name-tag slide         → text element + BACKGROUND media action + macro.
 */
final class SlideMediaElementTest extends TestCase
{
    use InspectsSlideFillMedia;
    use RefreshDatabase;

    private function bgData(string $name = 'BACKGROUND.jpg'): array
    {
        return ['path' => $name, 'format' => 'JPG', 'width' => 1920, 'height' => 1080, 'bundleRelative' => true];
    }

    /** Generate a one-slide song and run the production builder over it. */
    private function build(array $slideData, string $name = 'Test'): ParserSlide
    {
        $groups = [['name' => 'Gruppe', 'color' => [0, 0, 0, 1], 'slides' => [$slideData]]];
        $arrangements = [['name' => 'normal', 'groupNames' => ['Gruppe']]];

        $song = ProFileGenerator::generate($name, $groups, $arrangements);
        app(SlideMediaElementBuilder::class)->replaceImageActionsWithElements($song);

        return $song->getSlides()[0];
    }

    public function test_song_slide_keeps_background_action_and_text_without_image_element(): void
    {
        $groups = [['name' => 'Gruppe', 'color' => [0, 0, 0, 1], 'slides' => [[
            'text' => 'Textzeile',
            'background' => $this->bgData(),
        ]]]];
        $arrangements = [['name' => 'normal', 'groupNames' => ['Gruppe']]];

        $song = ProFileGenerator::generate('Song', $groups, $arrangements);

        // Sanity: the generator emits a BACKGROUND media action before conversion.
        $this->assertTrue($song->getSlides()[0]->hasBackgroundMedia());

        app(SlideMediaElementBuilder::class)->replaceImageActionsWithElements($song);
        $slide = $song->getSlides()[0];

        // Background stays a BACKGROUND media action (NOT converted to an element).
        $this->assertTrue($slide->hasBackgroundMedia());
        $this->assertSame('BACKGROUND.jpg', $slide->getBackgroundMediaUrl());

        // Text element preserved; no content fill.media element exists.
        $this->assertTrue($this->elementHasTextAt($slide, 0));
        $this->assertSame([], $this->fillMediaUrls($slide));

        // No foreground image action.
        $this->assertFalse($this->hasForegroundImageMediaAction($slide));
        $this->assertFalse($slide->hasMedia());
    }

    public function test_uploaded_content_slide_becomes_full_frame_element_without_foreground_action(): void
    {
        // Production uploaded-slide data: media + format + label + bundleRelative (no text).
        $slide = $this->build([
            'media' => 'UPLOAD.jpg',
            'format' => 'JPG',
            'label' => 'upload.jpg',
            'bundleRelative' => true,
        ]);

        // A single full-frame content fill.media element, no text.
        $this->assertCount(1, $this->baseSlideElements($slide));
        $this->assertSame('UPLOAD.jpg', $this->fillMediaUrlAt($slide, 0));
        $this->assertTrue($this->isFullFrameAt($slide, 0));
        $this->assertFalse($this->elementHasTextAt($slide, 0));

        // No foreground media action remains; no background configured at all.
        $this->assertFalse($this->hasForegroundImageMediaAction($slide));
        $this->assertFalse($slide->hasBackgroundMedia());
        $this->assertFalse($this->hasImageMediaAction($slide));
    }

    public function test_uploaded_content_slide_with_service_background_keeps_background_action_behind_element(): void
    {
        $slide = $this->build([
            'media' => 'UPLOAD.jpg',
            'format' => 'JPG',
            'label' => 'upload.jpg',
            'bundleRelative' => true,
            'background' => $this->bgData(),
        ]);

        // Uploaded image is a full-frame content element on top.
        $this->assertSame('UPLOAD.jpg', $this->fillMediaUrlAt($slide, 0));
        $this->assertTrue($this->isFullFrameAt($slide, 0));
        $this->assertFalse($this->hasForegroundImageMediaAction($slide));

        // Service background stays a BACKGROUND media action behind the element.
        $this->assertTrue($slide->hasBackgroundMedia());
        $this->assertSame('BACKGROUND.jpg', $slide->getBackgroundMediaUrl());
    }

    public function test_key_visual_slide_is_background_action_only_without_content_element(): void
    {
        // Production key-visual slide data: imageOnly + background.
        $slide = $this->build([
            'imageOnly' => true,
            'background' => $this->bgData('KEY_VISUAL.jpg'),
        ]);

        // Key-visual is a BACKGROUND media action, not a content element.
        $this->assertTrue($slide->hasBackgroundMedia());
        $this->assertSame('KEY_VISUAL.jpg', $slide->getBackgroundMediaUrl());

        // No content fill.media element and no foreground action.
        $this->assertSame([], $this->fillMediaUrls($slide));
        $this->assertFalse($this->hasForegroundImageMediaAction($slide));
    }

    public function test_name_tag_slide_keeps_text_background_action_and_macro(): void
    {
        $slide = $this->build([
            'text' => 'Erika Predigt',
            'subtitle' => 'Predigt',
            'background' => $this->bgData('KEY_VISUAL.jpg'),
            'macro' => [
                'name' => 'Namenseinblender',
                'uuid' => '11111111-1111-4111-8111-111111111111',
                'collectionName' => '--MAIN--',
                'collectionUuid' => null,
            ],
        ]);

        // Text element preserved (name-tag text sits above the background).
        $this->assertTrue($this->elementHasTextAt($slide, 0));
        $this->assertSame([], $this->fillMediaUrls($slide));

        // Key-visual behind the name tag stays a BACKGROUND media action.
        $this->assertTrue($slide->hasBackgroundMedia());
        $this->assertSame('KEY_VISUAL.jpg', $slide->getBackgroundMediaUrl());

        // Macro action preserved.
        $this->assertTrue($slide->hasMacro());
        $this->assertSame('Namenseinblender', $slide->getMacroName());

        // No image content element, no foreground action.
        $this->assertFalse($this->hasForegroundImageMediaAction($slide));
    }

    public function test_exported_song_pro_roundtrip_keeps_background_action_and_text(): void
    {
        Storage::fake('public');
        Storage::disk('public')->put('slides/background.jpg', 'background-image');

        $service = Service::factory()->create(['background_filename' => 'slides/background.jpg']);
        $song = $this->createSongWithContent();

        $path = app(ProExportService::class)->generateProFile($song, $service);
        $parserSong = ProFileReader::read($path);
        @unlink($path);

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

        $this->assertNotEmpty($slides);

        foreach ($slides as $slide) {
            // Background survives the .pro write+read as a BACKGROUND media action.
            $this->assertTrue($slide->hasBackgroundMedia());
            $this->assertSame('BACKGROUND.jpg', $slide->getBackgroundMediaUrl());
            // Text preserved; no content image element; no foreground action.
            $this->assertTrue($this->elementHasTextAt($slide, 0));
            $this->assertNotSame('', $slide->getPlainText());
            $this->assertSame([], $this->fillMediaUrls($slide));
            $this->assertFalse($this->hasForegroundImageMediaAction($slide));
        }
    }

    private function createSongWithContent(): Song
    {
        $song = Song::create([
            'title' => 'Fill Media Song',
            'ccli_id' => '99887',
            'author' => 'Test Author',
            'copyright_text' => 'Test Publisher',
        ]);

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

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

        return $song;
    }
}
