<?php

namespace Tests\Feature;

use App\Models\Label;
use App\Models\Service;
use App\Models\ServiceAgendaItem;
use App\Models\ServiceSong;
use App\Models\Slide;
use App\Models\Song;
use App\Models\SongArrangement;
use App\Models\SongArrangementLabel;
use App\Models\SongSlide;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Tests\TestCase;
use ZipArchive;

final class AgendaItemDownloadTest extends TestCase
{
    use RefreshDatabase;

    public function test_agenda_item_mit_slides_liefert_probundle(): void
    {
        Storage::fake('public');

        $user = User::factory()->create();
        $service = Service::factory()->create();

        $agendaItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Predigt',
            'sort_order' => 1,
            'service_song_id' => null,
            'is_before_event' => false,
        ]);

        Storage::disk('public')->put('slides/predigt-1.jpg', 'fake-image-1');
        Storage::disk('public')->put('slides/predigt-2.jpg', 'fake-image-2');

        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $agendaItem->id,
            'type' => 'sermon',
            'stored_filename' => 'slides/predigt-1.jpg',
            'original_filename' => 'Predigt Folie 1.jpg',
            'sort_order' => 0,
        ]);

        Slide::factory()->create([
            'service_id' => $service->id,
            'service_agenda_item_id' => $agendaItem->id,
            'type' => 'sermon',
            'stored_filename' => 'slides/predigt-2.jpg',
            'original_filename' => 'Predigt Folie 2.jpg',
            'sort_order' => 1,
        ]);

        $response = $this->actingAs($user)->get(
            route('services.agenda-item.download', [
                'service' => $service,
                'agendaItem' => $agendaItem,
            ])
        );

        $response->assertOk();
        $response->assertHeader('content-type', 'application/zip');

        $baseResponse = $response->baseResponse;
        $this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);

        $copiedPath = sys_get_temp_dir().'/agenda-test-'.uniqid().'.probundle';
        copy($baseResponse->getFile()->getPathname(), $copiedPath);

        $zip = new ZipArchive;
        $this->assertTrue($zip->open($copiedPath) === true);
        $this->assertSame(3, $zip->numFiles);

        $names = [];
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $name = $zip->getNameIndex($i);
            if ($name !== false) {
                $names[] = $name;
            }
        }

        $proEntry = array_values(array_filter($names, fn (string $n) => str_ends_with($n, '.pro')));
        $this->assertCount(1, $proEntry, '.probundle muss genau eine .pro Datei enthalten');
        $this->assertContains('predigt-1.jpg', $names);
        $this->assertContains('predigt-2.jpg', $names);

        $this->assertSame('fake-image-1', $zip->getFromName('predigt-1.jpg'));
        $this->assertSame('fake-image-2', $zip->getFromName('predigt-2.jpg'));

        $zip->close();
        @unlink($copiedPath);
    }

    public function test_song_agenda_item_liefert_probundle_mit_pro_daten(): void
    {
        $user = User::factory()->create();
        $service = Service::factory()->create();

        $song = Song::factory()->create(['title' => 'Amazing Grace']);
        $label = Label::factory()->create(['name' => 'Verse 1']);
        SongSlide::factory()->create(['label_id' => $label->id, 'text_content' => 'Amazing grace how sweet the sound', 'order' => 1]);

        $arrangement = SongArrangement::factory()->create([
            'song_id' => $song->id,
            'is_default' => true,
        ]);
        SongArrangementLabel::factory()->create([
            'song_arrangement_id' => $arrangement->id,
            'label_id' => $label->id,
            'order' => 1,
        ]);

        $serviceSong = ServiceSong::create([
            'service_id' => $service->id,
            'song_id' => $song->id,
            'song_arrangement_id' => null,
            'use_translation' => false,
            'order' => 1,
            'cts_song_name' => 'Amazing Grace',
            'cts_ccli_id' => '12345',
        ]);

        $agendaItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Amazing Grace',
            'sort_order' => 1,
            'service_song_id' => $serviceSong->id,
            'is_before_event' => false,
        ]);

        $response = $this->actingAs($user)->get(
            route('services.agenda-item.download', [
                'service' => $service,
                'agendaItem' => $agendaItem,
            ])
        );

        $response->assertOk();
        $response->assertHeader('content-type', 'application/zip');

        $baseResponse = $response->baseResponse;
        $this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);

        $copiedPath = sys_get_temp_dir().'/agenda-song-test-'.uniqid().'.probundle';
        copy($baseResponse->getFile()->getPathname(), $copiedPath);

        $zip = new ZipArchive;
        $this->assertTrue($zip->open($copiedPath) === true);
        $this->assertSame(1, $zip->numFiles);
        $this->assertTrue(str_ends_with($zip->getNameIndex(0), '.pro'), '.probundle muss eine .pro Datei enthalten');

        $proContent = $zip->getFromName($zip->getNameIndex(0));
        $this->assertNotFalse($proContent);
        $this->assertGreaterThan(0, strlen($proContent));

        $zip->close();
        @unlink($copiedPath);
    }

    public function test_agenda_item_von_anderem_service_liefert_404(): void
    {
        $user = User::factory()->create();
        $service = Service::factory()->create();
        $otherService = Service::factory()->create();

        $agendaItem = ServiceAgendaItem::factory()->create([
            'service_id' => $otherService->id,
            'title' => 'Fremd',
            'sort_order' => 1,
            'is_before_event' => false,
        ]);

        $response = $this->actingAs($user)->get(
            route('services.agenda-item.download', [
                'service' => $service,
                'agendaItem' => $agendaItem,
            ])
        );

        $response->assertNotFound();
    }

    public function test_agenda_item_ohne_slides_und_ohne_song_liefert_leeres_probundle(): void
    {
        $user = User::factory()->create();
        $service = Service::factory()->create();

        $agendaItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'title' => 'Leer',
            'sort_order' => 1,
            'service_song_id' => null,
            'is_before_event' => false,
        ]);

        $response = $this->actingAs($user)->get(
            route('services.agenda-item.download', [
                'service' => $service,
                'agendaItem' => $agendaItem,
            ])
        );

        $response->assertOk();

        $baseResponse = $response->baseResponse;
        $this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);

        $copiedPath = sys_get_temp_dir().'/agenda-empty-test-'.uniqid().'.probundle';
        copy($baseResponse->getFile()->getPathname(), $copiedPath);

        $zip = new ZipArchive;
        $this->assertTrue($zip->open($copiedPath) === true);
        $this->assertSame(1, $zip->numFiles);
        $this->assertTrue(str_ends_with($zip->getNameIndex(0), '.pro'), 'Einziger Eintrag muss .pro Datei sein');

        $zip->close();
        @unlink($copiedPath);
    }

    public function test_erfordert_authentifizierung(): void
    {
        $service = Service::factory()->create();
        $agendaItem = ServiceAgendaItem::factory()->create([
            'service_id' => $service->id,
            'is_before_event' => false,
        ]);

        $response = $this->get(
            route('services.agenda-item.download', [
                'service' => $service,
                'agendaItem' => $agendaItem,
            ])
        );

        $response->assertRedirect(route('login'));
    }
}
