<?php

declare(strict_types=1);

namespace ProPresenter\Parser\Tests;

use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use ProPresenter\Parser\ProFileGenerator;
use ProPresenter\Parser\ProFileReader;
use ProPresenter\Parser\ProFileWriter;

class ProFileGeneratorTest extends TestCase
{
    private string $tmpDir;

    protected function setUp(): void
    {
        $this->tmpDir = sys_get_temp_dir() . '/propresenter-generator-test-' . uniqid();
        mkdir($this->tmpDir, 0777, true);
    }

    protected function tearDown(): void
    {
        if (!is_dir($this->tmpDir)) {
            return;
        }

        foreach (scandir($this->tmpDir) ?: [] as $entry) {
            if ($entry === '.' || $entry === '..') {
                continue;
            }

            @unlink($this->tmpDir . '/' . $entry);
        }

        @rmdir($this->tmpDir);
    }

    #[Test]
    public function testGenerateCreatesValidSong(): void
    {
        $song = ProFileGenerator::generate(
            'Simple Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        ['text' => 'Hello World'],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $this->assertSame('Simple Song', $song->getName());
        $this->assertCount(1, $song->getGroups());
        $this->assertCount(1, $song->getSlides());
        $this->assertSame('Hello World', $song->getSlides()[0]->getPlainText());

        $arrangement = $song->getArrangementByName('normal');
        $this->assertNotNull($arrangement);
        $groups = $song->getGroupsForArrangement($arrangement);
        $this->assertCount(1, $groups);
        $this->assertSame('Verse 1', $groups[0]->getName());
    }

    #[Test]
    public function testGenerateWithMultipleGroupsAndArrangements(): void
    {
        $song = ProFileGenerator::generate(
            'Multi Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        ['text' => 'V1.1'],
                        ['text' => 'V1.2'],
                    ],
                ],
                [
                    'name' => 'Chorus',
                    'color' => [0.4, 0.5, 0.6, 1.0],
                    'slides' => [
                        ['text' => 'C1'],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1', 'Chorus']],
                ['name' => 'short', 'groupNames' => ['Chorus']],
            ],
        );

        $this->assertSame(['Verse 1', 'Chorus'], array_map(fn ($group) => $group->getName(), $song->getGroups()));

        $verse1 = $song->getGroupByName('Verse 1');
        $this->assertNotNull($verse1);
        $verseSlides = $song->getSlidesForGroup($verse1);
        $this->assertSame(['V1.1', 'V1.2'], array_map(fn ($slide) => $slide->getPlainText(), $verseSlides));

        $chorus = $song->getGroupByName('Chorus');
        $this->assertNotNull($chorus);
        $chorusSlides = $song->getSlidesForGroup($chorus);
        $this->assertSame(['C1'], array_map(fn ($slide) => $slide->getPlainText(), $chorusSlides));

        $normal = $song->getArrangementByName('normal');
        $this->assertNotNull($normal);
        $this->assertSame(
            ['Verse 1', 'Chorus'],
            array_map(fn ($group) => $group->getName(), $song->getGroupsForArrangement($normal)),
        );

        $short = $song->getArrangementByName('short');
        $this->assertNotNull($short);
        $this->assertSame(
            ['Chorus'],
            array_map(fn ($group) => $group->getName(), $song->getGroupsForArrangement($short)),
        );
    }

    #[Test]
    public function testGenerateWithTranslation(): void
    {
        $song = ProFileGenerator::generate(
            'Translation Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        ['text' => 'Original', 'translation' => 'Translated'],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $slide = $song->getSlides()[0];
        $this->assertTrue($slide->hasTranslation());
        $this->assertSame('Translated', $slide->getTranslation()?->getPlainText());
    }

    #[Test]
    public function testGenerateWithCcliMetadata(): void
    {
        $song = ProFileGenerator::generate(
            'CCLI Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        ['text' => 'Line'],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
            [
                'author' => 'Author Name',
                'song_title' => 'Song Title',
                'publisher' => 'Publisher Name',
                'copyright_year' => 2024,
                'song_number' => 12345,
                'display' => true,
                'artist_credits' => 'Artist Credits',
                'album' => 'Album Name',
            ],
        );

        $this->assertSame('Author Name', $song->getCcliAuthor());
        $this->assertSame('Song Title', $song->getCcliSongTitle());
        $this->assertSame('Publisher Name', $song->getCcliPublisher());
        $this->assertSame(2024, $song->getCcliCopyrightYear());
        $this->assertSame(12345, $song->getCcliSongNumber());
        $this->assertTrue($song->getCcliDisplay());
        $this->assertSame('Artist Credits', $song->getCcliArtistCredits());
        $this->assertSame('Album Name', $song->getCcliAlbum());
    }

    #[Test]
    public function testRoundTripFromTestPro(): void
    {
        $original = ProFileReader::read('/Users/thorsten/AI/propresenter/ref/Test.pro');

        $groups = [];
        foreach ($original->getGroups() as $group) {
            $color = $group->getColor();
            $slides = [];
            foreach ($original->getSlidesForGroup($group) as $slide) {
                $slides[] = [
                    'text' => $slide->getPlainText(),
                    'translation' => $slide->hasTranslation() ? $slide->getTranslation()?->getPlainText() : null,
                ];
            }

            $groups[] = [
                'name' => $group->getName(),
                'color' => [
                    $color['r'] ?? 0.0,
                    $color['g'] ?? 0.0,
                    $color['b'] ?? 0.0,
                    $color['a'] ?? 1.0,
                ],
                'slides' => $slides,
            ];
        }

        $arrangements = [];
        foreach ($original->getArrangements() as $arrangement) {
            $arrangements[] = [
                'name' => $arrangement->getName(),
                'groupNames' => array_map(
                    fn ($group) => $group->getName(),
                    $original->getGroupsForArrangement($arrangement),
                ),
            ];
        }

        $ccli = [
            'author' => $original->getCcliAuthor(),
            'song_title' => $original->getCcliSongTitle(),
            'publisher' => $original->getCcliPublisher(),
            'copyright_year' => $original->getCcliCopyrightYear(),
            'song_number' => $original->getCcliSongNumber(),
            'display' => $original->getCcliDisplay(),
            'artist_credits' => $original->getCcliArtistCredits(),
            'album' => $original->getCcliAlbum(),
        ];

        $generated = ProFileGenerator::generate($original->getName(), $groups, $arrangements, $ccli);
        $filePath = $this->tmpDir . '/test-roundtrip.pro';
        ProFileWriter::write($generated, $filePath);
        $roundTrip = ProFileReader::read($filePath);

        $this->assertSame($original->getName(), $roundTrip->getName());

        $this->assertSame(
            array_map(fn ($group) => $group->getName(), $original->getGroups()),
            array_map(fn ($group) => $group->getName(), $roundTrip->getGroups()),
        );

        foreach ($original->getGroups() as $group) {
            $actualGroup = $roundTrip->getGroupByName($group->getName());
            $this->assertNotNull($actualGroup);

            $expectedSlides = $original->getSlidesForGroup($group);
            $actualSlides = $roundTrip->getSlidesForGroup($actualGroup);
            $this->assertCount(count($expectedSlides), $actualSlides);

            foreach ($expectedSlides as $index => $expectedSlide) {
                $actualSlide = $actualSlides[$index];
                $this->assertSame($expectedSlide->getPlainText(), $actualSlide->getPlainText());
                $this->assertSame($expectedSlide->hasTranslation(), $actualSlide->hasTranslation());
                if ($expectedSlide->hasTranslation()) {
                    $this->assertSame(
                        $expectedSlide->getTranslation()?->getPlainText(),
                        $actualSlide->getTranslation()?->getPlainText(),
                    );
                }
            }
        }

        $this->assertSame(
            array_map(fn ($arrangement) => $arrangement->getName(), $original->getArrangements()),
            array_map(fn ($arrangement) => $arrangement->getName(), $roundTrip->getArrangements()),
        );

        foreach ($original->getArrangements() as $arrangement) {
            $roundTripArrangement = $roundTrip->getArrangementByName($arrangement->getName());
            $this->assertNotNull($roundTripArrangement);

            $expectedNames = array_map(
                fn ($group) => $group->getName(),
                $original->getGroupsForArrangement($arrangement),
            );
            $actualNames = array_map(
                fn ($group) => $group->getName(),
                $roundTrip->getGroupsForArrangement($roundTripArrangement),
            );

            $this->assertSame($expectedNames, $actualNames);
        }
    }

    #[Test]
    public function testGenerateAndWriteCreatesFile(): void
    {
        $filePath = $this->tmpDir . '/generated.pro';

        ProFileGenerator::generateAndWrite(
            $filePath,
            'Write Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        ['text' => 'Line 1'],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $this->assertFileExists($filePath);

        $song = ProFileReader::read($filePath);
        $this->assertSame('Write Song', $song->getName());
    }

    #[Test]
    public function testGenerateWithMacro(): void
    {
        $song = ProFileGenerator::generate(
            'Macro Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        [
                            'text' => 'Line 1',
                            'macro' => [
                                'name' => 'Macro Name',
                                'uuid' => '11111111-2222-3333-4444-555555555555',
                                'collectionName' => '--MAIN--',
                                'collectionUuid' => '8D02FC57-83F8-4042-9B90-81C229728426',
                            ],
                        ],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $slide = $song->getSlides()[0];
        $this->assertTrue($slide->hasMacro());
        $this->assertSame('Macro Name', $slide->getMacroName());
        $this->assertSame('11111111-2222-3333-4444-555555555555', $slide->getMacroUuid());
    }

    #[Test]
    public function testGenerateMediaSlide(): void
    {
        $song = ProFileGenerator::generate(
            'Media Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        [
                            'media' => 'file:///tmp/test-image.jpg',
                            'format' => 'JPG',
                        ],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $slide = $song->getSlides()[0];
        $this->assertTrue($slide->hasMedia());
        $this->assertSame('file:///tmp/test-image.jpg', $slide->getMediaUrl());
        $this->assertSame('JPG', $slide->getMediaFormat());
        $this->assertSame('', $slide->getPlainText());
    }

    #[Test]
    public function testGenerateMediaSlideWithLabelAndMacro(): void
    {
        $song = ProFileGenerator::generate(
            'Media Macro Song',
            [
                [
                    'name' => 'Verse 1',
                    'color' => [0.1, 0.2, 0.3, 1.0],
                    'slides' => [
                        [
                            'media' => 'file:///tmp/test-image-2.jpg',
                            'format' => 'JPG',
                            'label' => 'Image Slide Label',
                            'macro' => [
                                'name' => 'Image Macro',
                                'uuid' => 'AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE',
                            ],
                        ],
                    ],
                ],
            ],
            [
                ['name' => 'normal', 'groupNames' => ['Verse 1']],
            ],
        );

        $slide = $song->getSlides()[0];
        $this->assertTrue($slide->hasMedia());
        $this->assertSame('Image Slide Label', $slide->getLabel());
        $this->assertTrue($slide->hasMacro());
        $this->assertSame('Image Macro', $slide->getMacroName());
        $this->assertSame('AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE', $slide->getMacroUuid());
    }
}
