<?php

namespace Tests\Feature;

use App\Domains\Admin\Actions\UpdateAvailablePaymentMethod\UpdateAvailablePaymentMethodAction;
use App\Domains\Admin\Actions\UpdateAvailablePaymentMethod\UpdateAvailablePaymentMethodRequest;
use App\Domains\Event\Actions\SetPaymentMethods\SetPaymentMethodsCommand;
use App\Domains\Event\Actions\SetPaymentMethods\SetPaymentMethodsRequest;
use App\Models\AvailablePaymentMethod;
use App\Models\Event;
use App\Models\PaymentMethod;
use App\Models\Tenant;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class PaymentMethodConfigurationTest 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' => 'finance@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(),
        ]);
    }

    private function availableMethod(string $slug, bool $active = false, array $config = []): AvailablePaymentMethod
    {
        return AvailablePaymentMethod::create([
            'tenant' => $this->tenant->slug,
            'slug' => $slug,
            'name' => 'Konto',
            'description' => null,
            'active' => $active,
            'configuration' => $config,
        ]);
    }

    public function test_activation_is_blocked_when_required_options_missing(): void
    {
        $method = $this->availableMethod(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION);

        $response = new UpdateAvailablePaymentMethodAction(new UpdateAvailablePaymentMethodRequest(
            availablePaymentMethod: $method,
            name: 'Konto',
            description: null,
            active: true,
            configuration: ['iban' => 'DE123'], // account_owner fehlt
        ))->execute();

        $this->assertFalse($response->success);
        $this->assertFalse($method->fresh()->active);
    }

    public function test_activation_succeeds_when_configuration_complete(): void
    {
        $method = $this->availableMethod(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION);

        $response = new UpdateAvailablePaymentMethodAction(new UpdateAvailablePaymentMethodRequest(
            availablePaymentMethod: $method,
            name: 'Konto',
            description: null,
            active: true,
            configuration: ['iban' => 'DE123', 'account_owner' => 'Kasse', 'bic' => 'XY', 'evil' => 'x'],
        ))->execute();

        $this->assertTrue($response->success);

        $fresh = $method->fresh();
        $this->assertTrue($fresh->active);
        $this->assertSame('DE123', $fresh->configuration['iban']);
        $this->assertArrayNotHasKey('evil', $fresh->configuration); // unbekannte Keys verworfen
    }

    public function test_event_assignment_copies_snapshot_and_preserves_overrides(): void
    {
        $this->availableMethod(
            PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION,
            active: true,
            config: ['iban' => 'DE-TENANT', 'account_owner' => 'Kasse'],
        );

        $event = $this->createEvent();

        // 1. Zuweisung -> Snapshot der Tenant-Config landet im Pivot.
        $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);

        $pivotConfig = $event->fresh()->paymentMethods()->first()->pivot->configuration;
        $this->assertSame('DE-TENANT', $pivotConfig['iban']);

        // 2. Pro-Event-Override setzen.
        $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION], [
            PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION => ['iban' => 'DE-EVENT', 'account_owner' => 'Kasse'],
        ]);
        $this->assertSame('DE-EVENT', $event->fresh()->paymentMethods()->first()->pivot->configuration['iban']);

        // 3. Erneutes Speichern OHNE Override -> Override bleibt erhalten (kein Reset-Wipe).
        $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);
        $this->assertSame('DE-EVENT', $event->fresh()->paymentMethods()->first()->pivot->configuration['iban']);
    }

    private function createEvent(): Event
    {
        return 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,
        ]);
    }

    /**
     * @param array<int, string> $slugs
     * @param array<string, array<string, mixed>> $configs
     */
    private function runUpdateEvent(Event $event, array $slugs, array $configs = []): void
    {
        $request = new SetPaymentMethodsRequest($event, $slugs, $configs);

        $response = new SetPaymentMethodsCommand($request)->execute();
        $this->assertTrue($response->success, $response->message);
    }
}
