<?php

namespace App\Services;

use App\Models\Service;
use App\Models\ServiceAgendaItem;
use App\Models\Setting;
use App\Models\Slide;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use ProPresenter\Parser\ProFileGenerator;
use ProPresenter\Parser\ProPlaylistGenerator;

class PlaylistExportService
{
    /** @return array{path: string, filename: string, skipped: int} */
    public function generatePlaylist(Service $service): array
    {
        $agendaItems = ServiceAgendaItem::where('service_id', $service->id)
            ->where('is_before_event', false)
            ->orderBy('sort_order')
            ->with([
                'slides' => fn ($q) => $q->whereNull('deleted_at')->orderBy('sort_order'),
                'serviceSong.song.arrangements.arrangementLabels.label.songSlides',
                'serviceSong.arrangement.arrangementLabels.label',
            ])
            ->get();

        if ($agendaItems->isEmpty()) {
            return $this->generatePlaylistLegacy($service);
        }

        return $this->generatePlaylistFromAgenda($service, $agendaItems);
    }

    /**
     * @param  Collection<int, ServiceAgendaItem>  $agendaItems
     * @return array{path: string, filename: string, skipped: int, temp_dir: string}
     */
    private function generatePlaylistFromAgenda(Service $service, Collection $agendaItems): array
    {
        $informationSlides = Slide::where('type', 'information')
            ->where(fn ($q) => $q->whereNull('expire_date')->orWhereDate('expire_date', '>=', $service->date))
            ->where(fn ($q) => $q->whereNull('service_id')->orWhere('service_id', $service->id))
            ->whereNull('deleted_at');

        if ($service->date) {
            $informationSlides->whereDate('uploaded_at', '<=', $service->date);
        }

        $informationSlides = $informationSlides->orderBy('sort_order')->orderByDesc('uploaded_at')->get();

        $announcementPatterns = Setting::get('agenda_announcement_position');
        $announcementInserted = false;

        $exportService = app(ProExportService::class);
        $tempDir = sys_get_temp_dir().'/playlist-export-'.uniqid();
        mkdir($tempDir, 0755, true);

        $playlistItems = [];
        $embeddedFiles = [];
        $skippedUnmatched = 0;

        foreach ($agendaItems as $item) {
            if (! $announcementInserted && $announcementPatterns && $informationSlides->isNotEmpty()) {
                $patterns = array_map('trim', explode(',', $announcementPatterns));
                $matcher = app(AgendaMatcherService::class);
                if ($matcher->matchesAny($item->title, $patterns)) {
                    $this->addSlidesFromCollection(
                        $informationSlides,
                        'information',
                        'Informationen',
                        $tempDir,
                        $playlistItems,
                        $embeddedFiles,
                    );
                    $announcementInserted = true;
                }
            }

            if ($item->serviceSong) {
                $serviceSong = $item->serviceSong;

                if ($serviceSong->song_id && $serviceSong->song) {
                    $song = $serviceSong->song;

                    if ($this->countSongLabels($song) === 0) {
                        $skippedUnmatched++;

                        continue;
                    }

                    $proPath = $exportService->generateProFile($song, $service);
                    $proFilename = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $song->title).'.pro';
                    $destPath = $tempDir.'/'.$proFilename;
                    rename($proPath, $destPath);

                    $embeddedFiles[$proFilename] = file_get_contents($destPath);

                    $playlistItems[] = [
                        'type' => 'presentation',
                        'name' => $song->title,
                        'path' => $proFilename,
                    ];
                } else {
                    $skippedUnmatched++;
                }

                continue;
            }

            if ($item->slides->isNotEmpty()) {
                $label = $item->title ?: 'Folien';
                $this->addSlidesFromCollection(
                    $item->slides,
                    'agenda_'.$item->id,
                    $label,
                    $tempDir,
                    $playlistItems,
                    $embeddedFiles,
                );
            }
        }

        if (! $announcementInserted && $informationSlides->isNotEmpty()) {
            $prependItems = [];
            $prependFiles = [];
            $this->addSlidesFromCollection(
                $informationSlides,
                'information',
                'Informationen',
                $tempDir,
                $prependItems,
                $prependFiles,
            );
            $playlistItems = array_merge($prependItems, $playlistItems);
            $embeddedFiles = array_merge($prependFiles, $embeddedFiles);
        }

        if (empty($playlistItems)) {
            $this->deleteDirectory($tempDir);
            throw new \RuntimeException('Keine Songs mit Inhalt zum Exportieren gefunden.');
        }

        $dateFormatted = $service->date?->format('Y-m-d') ?? now()->format('Y-m-d');
        $playlistName = $service->title.' - '.$dateFormatted;
        $outputFilename = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $service->title).'_'.$dateFormatted.'.proplaylist';
        $outputPath = $tempDir.'/'.$outputFilename;

        $this->writePlaylistFile($outputPath, $playlistName, $playlistItems, $embeddedFiles);

        return [
            'path' => $outputPath,
            'filename' => $outputFilename,
            'skipped' => $skippedUnmatched,
            'temp_dir' => $tempDir,
        ];
    }

    /**
     * Legacy export for services without agenda items.
     * Hardcoded block order: info → songs → moderation → sermon.
     *
     * @return array{path: string, filename: string, skipped: int, temp_dir: string}
     */
    private function generatePlaylistLegacy(Service $service): array
    {
        $service->loadMissing('serviceSongs.song.arrangements.arrangementLabels.label.songSlides');

        $matchedSongs = $service->serviceSongs()
            ->whereNotNull('song_id')
            ->orderBy('order')
            ->with('song.arrangements.arrangementLabels.label.songSlides')
            ->get();

        $skippedUnmatched = $service->serviceSongs()->whereNull('song_id')->count();
        $skippedEmpty = 0;

        $exportService = app(ProExportService::class);
        $tempDir = sys_get_temp_dir().'/playlist-export-'.uniqid();
        mkdir($tempDir, 0755, true);

        $playlistItems = [];
        $embeddedFiles = [];

        $this->addSlidePresentation(
            'information',
            'Informationen',
            $service,
            $tempDir,
            $playlistItems,
            $embeddedFiles,
        );

        foreach ($matchedSongs as $serviceSong) {
            $song = $serviceSong->song;

            if (! $song || $this->countSongLabels($song) === 0) {
                $skippedEmpty++;

                continue;
            }

            $proPath = $exportService->generateProFile($song, $service);
            $proFilename = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $song->title).'.pro';
            $destPath = $tempDir.'/'.$proFilename;
            rename($proPath, $destPath);

            $embeddedFiles[$proFilename] = file_get_contents($destPath);

            $playlistItems[] = [
                'type' => 'presentation',
                'name' => $song->title,
                'path' => $proFilename,
            ];
        }

        $this->addSlidePresentation(
            'moderation',
            'Moderation',
            $service,
            $tempDir,
            $playlistItems,
            $embeddedFiles,
        );

        $this->addSlidePresentation(
            'sermon',
            'Predigt',
            $service,
            $tempDir,
            $playlistItems,
            $embeddedFiles,
        );

        if (empty($playlistItems)) {
            $this->deleteDirectory($tempDir);
            throw new \RuntimeException('Keine Songs mit Inhalt zum Exportieren gefunden.');
        }

        $dateFormatted = $service->date?->format('Y-m-d') ?? now()->format('Y-m-d');
        $playlistName = $service->title.' - '.$dateFormatted;
        $outputFilename = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $service->title).'_'.$dateFormatted.'.proplaylist';
        $outputPath = $tempDir.'/'.$outputFilename;

        $this->writePlaylistFile($outputPath, $playlistName, $playlistItems, $embeddedFiles);

        return [
            'path' => $outputPath,
            'filename' => $outputFilename,
            'skipped' => $skippedUnmatched + $skippedEmpty,
            'temp_dir' => $tempDir,
        ];
    }

    private function addSlidesFromCollection(
        Collection $slides,
        string $prefix,
        string $label,
        string $tempDir,
        array &$playlistItems,
        array &$embeddedFiles,
    ): void {
        $slideDataList = [];
        $imageFiles = [];

        foreach ($slides->values() as $index => $slide) {
            $storedPath = Storage::disk('public')->path($slide->stored_filename);

            if (! file_exists($storedPath)) {
                continue;
            }

            $imageFilename = $prefix.'_'.($index + 1).'_'.basename($slide->stored_filename);
            $destPath = $tempDir.'/'.$imageFilename;
            copy($storedPath, $destPath);

            $imageFiles[$imageFilename] = file_get_contents($destPath);

            $slideDataList[] = [
                'media' => $imageFilename,
                'format' => 'JPG',
                'label' => $slide->original_filename,
            ];
        }

        if (empty($slideDataList)) {
            return;
        }

        $groups = [
            [
                'name' => $label,
                'color' => [0.5, 0.5, 0.5, 1.0],
                'slides' => $slideDataList,
            ],
        ];

        $arrangements = [
            [
                'name' => 'Standard',
                'groupNames' => [$label],
            ],
        ];

        $safeLabel = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $label);
        $proFilename = $safeLabel.'.pro';
        $proPath = $tempDir.'/'.$proFilename;

        $this->writeProFile($proPath, $label, $groups, $arrangements);

        foreach ($imageFiles as $filename => $contents) {
            $embeddedFiles[$filename] = $contents;
        }

        $embeddedFiles[$proFilename] = file_get_contents($proPath);

        $playlistItems[] = [
            'type' => 'presentation',
            'name' => $label,
            'path' => $proFilename,
        ];
    }

    private function addSlidePresentation(
        string $type,
        string $label,
        Service $service,
        string $tempDir,
        array &$playlistItems,
        array &$embeddedFiles,
    ): void {
        $query = Slide::where('type', $type)
            ->whereNull('deleted_at');

        if ($type === 'information') {
            $query->where(function ($q) use ($service) {
                $q->whereNull('service_id')
                    ->orWhere('service_id', $service->id);
            });

            if ($service->date) {
                $query->where(function ($q) use ($service) {
                    $q->whereNull('expire_date')
                        ->orWhereDate('expire_date', '>=', $service->date);
                })->whereDate('uploaded_at', '<=', $service->date);
            }
        } else {
            $query->where('service_id', $service->id);
        }

        $slides = $query->orderBy('sort_order')->orderByDesc('uploaded_at')->get();

        if ($slides->isEmpty()) {
            return;
        }

        $this->addSlidesFromCollection(
            $slides,
            $type,
            $label,
            $tempDir,
            $playlistItems,
            $embeddedFiles,
        );
    }

    protected function writeProFile(string $path, string $name, array $groups, array $arrangements): void
    {
        ProFileGenerator::generateAndWrite($path, $name, $groups, $arrangements);
    }

    private function countSongLabels(\App\Models\Song $song): int
    {
        return $song->arrangements()
            ->withCount('arrangementLabels')
            ->get()
            ->sum('arrangement_labels_count');
    }

    protected function writePlaylistFile(string $path, string $name, array $items, array $embeddedFiles): void
    {
        ProPlaylistGenerator::generateAndWrite($path, $name, $items, $embeddedFiles);
    }

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

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

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