<?php

namespace Tests\Unit;

use App\EventPaymentModules\EventPaymentModuleRegistry;
use App\EventPaymentModules\Modules\AccountTransferPaymentModule;
use App\EventPaymentModules\Modules\UndefinedPaymentModule;
use App\EventPaymentModules\ProvidesGiroCode;
use App\Models\PaymentMethod;
use PHPUnit\Framework\TestCase;

class EventPaymentModuleRegistryTest extends TestCase
{
    public function test_for_slug_resolves_known_modules(): void
    {
        $this->assertInstanceOf(
            AccountTransferPaymentModule::class,
            EventPaymentModuleRegistry::forSlug(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION)
        );
        $this->assertInstanceOf(
            UndefinedPaymentModule::class,
            EventPaymentModuleRegistry::forSlug(PaymentMethod::PAYMENT_NOT_DEFINED)
        );
    }

    public function test_for_slug_returns_null_for_unknown(): void
    {
        $this->assertNull(EventPaymentModuleRegistry::forSlug('DOES_NOT_EXIST'));
    }

    public function test_slugs_lists_all_registered_modules(): void
    {
        $slugs = EventPaymentModuleRegistry::slugs();

        $this->assertContains(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION, $slugs);
        $this->assertContains(PaymentMethod::PAYMENT_NOT_DEFINED, $slugs);
    }

    public function test_module_option_helpers(): void
    {
        $module = new AccountTransferPaymentModule();

        $this->assertSame(['account_owner', 'iban'], $module->requiredOptionKeys());
        // Rückgabe folgt der Schema-Reihenfolge, unbekannte Keys werden verworfen.
        $this->assertSame(
            ['account_owner' => 'Kasse', 'iban' => 'DE123'],
            $module->sanitizeConfiguration(['iban' => 'DE123', 'account_owner' => 'Kasse', 'evil' => 'x'])
        );
        $this->assertFalse($module->isConfigurationComplete(['iban' => 'DE123']));
        $this->assertTrue($module->isConfigurationComplete(['iban' => 'DE123', 'account_owner' => 'Kasse']));
    }

    public function test_participant_options_per_module(): void
    {
        // Überweisung: keine payer-seitigen Eingaben.
        $this->assertSame([], (new AccountTransferPaymentModule())->getParticipantOptions());

        // Sonstiges: exemplarische Teilnehmer-Eingaben (Betrag + Passend-Checkbox).
        $undefined = (new UndefinedPaymentModule())->getParticipantOptions();
        $this->assertSame(['amount_brought', 'has_exact_amount'], array_column($undefined, 'name'));
        $this->assertSame('checkbox', $undefined[1]['type']);
    }

    public function test_participant_option_helpers(): void
    {
        // Anonymes Modul mit einer Pflicht-Teilnehmereingabe.
        $module = new class extends \App\EventPaymentModules\AbstractEventPaymentModule {
            public static function slug(): string
            {
                return 'TEST';
            }

            public function defaultName(): string
            {
                return 'Test';
            }

            public function getOptions(): array
            {
                return [];
            }

            public function getParticipantOptions(): array
            {
                return [['name' => 'iban', 'label' => 'IBAN', 'type' => 'string', 'required' => true]];
            }

            protected function invoiceClosingStatement(\App\EventPaymentModules\DTO\CreateInvoiceRequest $request): string
            {
                return '';
            }
        };

        // sanitize verwirft unbekannte Keys, complete verlangt Pflichtfeld non-empty.
        $this->assertSame(['iban' => 'DE1'], $module->sanitizeParticipantOptions(['iban' => 'DE1', 'evil' => 'x']));
        $this->assertFalse($module->participantOptionsComplete([]));
        $this->assertFalse($module->participantOptionsComplete(['iban' => '']));
        $this->assertTrue($module->participantOptionsComplete(['iban' => 'DE1']));
    }

    public function test_only_account_transfer_provides_giro_code(): void
    {
        $this->assertInstanceOf(ProvidesGiroCode::class, new AccountTransferPaymentModule());
        $this->assertNotInstanceOf(ProvidesGiroCode::class, new UndefinedPaymentModule());
    }

    public function test_defaults_are_built_from_modules(): void
    {
        $defaults = PaymentMethod::defaults();

        $this->assertSame('Überweisung auf Veranstaltungskonto', $defaults[PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]['name']);
        $this->assertArrayHasKey(PaymentMethod::PAYMENT_NOT_DEFINED, $defaults);
    }
}
