<?php

namespace Tests\Feature;

use App\Enumerations\EatingHabit;
use App\Enumerations\EfzStatus;
use App\Enumerations\FirstAidPermission;
use App\Enumerations\ParticipationType;
use App\Enumerations\SwimmingPermission;
use App\EventPaymentModules\DTO\RegistrationRenderContext;
use App\EventPaymentModules\ProvidesGiroCode;
use App\Models\AvailablePaymentMethod;
use App\Models\Event;
use App\Models\EventParticipant;
use App\Models\PaymentMethod;
use App\Models\Tenant;
use App\RelationModels\EventPaymentMethods;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class EventParticipantPaymentSummaryTest extends TestCase
{
    use RefreshDatabase;

    private Tenant $tenant;

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

        $this->tenant = Tenant::create([
            'slug' => 'tv', 'name' => 'Test', 'email' => 't@example.com', 'email_finance' => 'f@example.com',
            'url' => 'test.local', 'account_name' => 'Test e.V.', 'account_iban' => 'DE00', 'account_bic' => 'XY',
            'city' => 'Stadt', 'postcode' => '00000', 'is_active_local_group' => true, 'has_active_instance' => true,
        ]);
        app()->instance('tenant', $this->tenant);

        PaymentMethod::create(['slug' => PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);
        PaymentMethod::create(['slug' => PaymentMethod::PAYMENT_NOT_DEFINED]);

        DB::table('participation_fee_types')->insert(['slug' => 'FIXED', 'name' => 'Fixed', 'created_at' => now(), 'updated_at' => now()]);
        ParticipationType::create(['slug' => ParticipationType::PARTICIPATION_TYPE_PARTICIPANT, 'name' => 'Teilnehmende']);
        EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_NOT_CHECKED, 'name' => 'Nicht geprüft']);
        SwimmingPermission::create(['slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED, 'name' => 'Darf baden', 'short' => 'Erteilt']);
        FirstAidPermission::create(['slug' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED, 'name' => 'Zugestimmt', 'description' => '']);
        EatingHabit::create(['slug' => EatingHabit::EATING_HABIT_OMNIVOR, 'name' => 'Omnivor']);
    }

    private function createEventWithTransfer(array $config): Event
    {
        $event = Event::create([
            'tenant' => $this->tenant->slug, 'name' => 'Event', 'identifier' => 'evt-1', 'location' => 'Ort',
            'postal_code' => '00000', 'email' => 'e@example.com', 'start_date' => '2026-08-01', 'end_date' => '2026-08-05',
            'early_bird_end' => '2026-07-01', 'registration_final_end' => '2026-07-20', 'early_bird_end_amount_increase' => 0,
            'account_owner' => 'Owner', 'account_iban' => 'DE00', 'participation_fee_type' => 'FIXED',
            'pay_per_day' => false, 'pay_direct' => false,
        ]);

        AvailablePaymentMethod::create([
            'tenant' => $this->tenant->slug, 'slug' => PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION,
            'name' => 'Überweisung', 'active' => true, 'configuration' => $config,
        ]);

        EventPaymentMethods::create([
            'event_id' => $event->id,
            'slug' => PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION,
            'configuration' => $config,
        ]);

        return $event;
    }

    private function createParticipant(Event $event, float $amount, string $paymentMethod = PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION): EventParticipant
    {
        return EventParticipant::create([
            'tenant' => $this->tenant->slug, 'event_id' => $event->id, 'identifier' => 'p-1',
            'firstname' => 'Hans', 'lastname' => 'Muster', 'participation_type' => ParticipationType::PARTICIPATION_TYPE_PARTICIPANT,
            'birthday' => '2000-01-01', 'address_1' => 'Weg 1', 'postcode' => '00000', 'city' => 'Stadt',
            'email_1' => 'h@example.com', 'phone_1' => '123', 'arrival_date' => '2026-08-01', 'departure_date' => '2026-08-05',
            'arrival_eating' => 0, 'departure_eating' => 0, 'amount' => $amount, 'payment_purpose' => 'Event - Beitrag Hans Muster',
            'efz_status' => EfzStatus::EFZ_STATUS_NOT_CHECKED, 'payment_method' => $paymentMethod,
            'swimming_permission' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED,
            'first_aid_permission' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED,
            'eating_habit' => EatingHabit::EATING_HABIT_OMNIVOR,
        ]);
    }

    public function test_transfer_module_produces_giro_code(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0);

        $module = $participant->paymentModule();
        $this->assertInstanceOf(ProvidesGiroCode::class, $module);

        $giro = $module->giroCode($participant, $participant->paymentConfiguration());
        $this->assertNotNull($giro);
        $this->assertNotSame('', $giro);
    }

    public function test_cash_module_provides_no_giro_code(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0, PaymentMethod::PAYMENT_NOT_DEFINED);

        $this->assertNotInstanceOf(ProvidesGiroCode::class, $participant->paymentModule());
    }

    public function test_payment_configuration_resolves_event_pivot_config(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0);

        $config = $participant->paymentConfiguration();

        $this->assertSame('DE-EVENT', $config['iban']);
        $this->assertSame('Kasse', $config['account_owner']);
    }

    public function test_payment_summary_renders_html_from_resolved_config(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0);

        // Web-Kontext: SPA-Markup (form-table), IBAN aus der Config + GiroCode-Bild über den Endpoint.
        $web = $participant->paymentSummary(RegistrationRenderContext::Web);
        $this->assertTrue($web->hasPaymentInformation);
        $this->assertStringContainsString('DE-EVENT', $web->html);
        $this->assertStringContainsString('/print-girocode/', $web->html);
        $this->assertStringContainsString('class="form-table"', $web->html);

        // Mail-Kontext: E-Mail-Markup (Inline-Styles), GiroCode als inline-CID.
        $mail = $participant->paymentSummary(RegistrationRenderContext::Mail);
        $this->assertStringContainsString('cid:girocode.png', $mail->html);
        $this->assertStringContainsString('style="', $mail->html);
        $this->assertStringNotContainsString('class="form-table"', $mail->html);
    }

    public function test_cash_summary_renders_configured_text(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);

        $cashConfig = ['payment_information' => '<p>Bitte bar vor Ort zahlen.</p>'];
        AvailablePaymentMethod::create([
            'tenant' => $this->tenant->slug, 'slug' => PaymentMethod::PAYMENT_NOT_DEFINED,
            'name' => 'Barzahlung', 'active' => true, 'configuration' => $cashConfig,
        ]);
        EventPaymentMethods::create([
            'event_id' => $event->id, 'slug' => PaymentMethod::PAYMENT_NOT_DEFINED, 'configuration' => $cashConfig,
        ]);

        $participant = $this->createParticipant($event, 50.0, PaymentMethod::PAYMENT_NOT_DEFINED);

        $summary = $participant->paymentSummary(RegistrationRenderContext::Web);
        $this->assertTrue($summary->hasPaymentInformation);
        $this->assertStringContainsString('Bitte bar vor Ort zahlen.', $summary->html);
        $this->assertStringContainsString('class="payment-information"', $summary->html);
    }

    public function test_paid_participant_has_no_payment_information(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0);
        $participant->amount_paid = 50.0;
        $participant->save();

        $this->assertFalse($participant->paymentSummary()->hasPaymentInformation);
    }

    public function test_resource_exposes_payment_summary(): void
    {
        $event = $this->createEventWithTransfer(['account_owner' => 'Kasse', 'iban' => 'DE-EVENT']);
        $participant = $this->createParticipant($event, 50.0);

        $array = $participant->toResource()->toArray(request());

        $this->assertTrue($array['paymentSummary']['hasPaymentInformation']);
        $this->assertStringContainsString('DE-EVENT', $array['paymentSummary']['html']);
    }
}
