<?php

use App\Livewire\PaymentMethod;
use App\Models\Customer;
use App\Models\User;
use App\Services\Stripe\FakeStripeClient;
use App\Services\Stripe\StripeClient;
use App\Support\StripePublishableKey;
use Livewire\Livewire;

/**
 * Bis hierher konnte ein Kunde seine Karte NICHT tauschen — es gab im ganzen
 * Portal keine Zahlungsmittel-Verwaltung. Eine abgelaufene Karte war damit
 * eine Sackgasse: die Abbuchung scheitert, der Vertrag geht auf `past_due`,
 * und der Kunde hat keine Stelle, an der er die Ursache beheben könnte.
 *
 * Deshalb ist das die ERSTE Lieferung und der Mahnlauf die zweite. Ein
 * Mahnlauf über einem Portal ohne Kartenwechsel mahnt jemanden für etwas, das
 * er nicht abstellen kann.
 */
function portalUserWithStripe(): User
{
    $user = User::factory()->create(['email' => 'karte@portal.test', 'is_admin' => false]);
    Customer::factory()->create(['email' => 'karte@portal.test', 'stripe_customer_id' => 'cus_42']);

    return $user;
}

beforeEach(function () {
    app()->instance(StripeClient::class, new FakeStripeClient);
    StripePublishableKey::set('pk_test_sichtbar');
});

it('hands the browser a client secret and the publishable key', function () {
    // Am übergebenen Wert geprüft, nicht am HTML: der Schlüssel steht in einem
    // `@script`-Block, und den legt Livewire in die Effects-Nutzlast statt in
    // das gerenderte Markup. Ein assertSee darauf prüfte die Testfassung von
    // Livewire, nicht diese Komponente.
    Livewire::actingAs(portalUserWithStripe())
        ->test(PaymentMethod::class)
        ->assertSet('clientSecret', 'seti_fake_secret_cus_42')
        ->assertViewHas('publishableKey', 'pk_test_sichtbar');
});

it('stores the confirmed payment method as the default', function () {
    // Der Kern: eine Karte zu hinterlegen genügt nicht. Ohne diesen Schritt
    // bucht Stripe weiter mit der kaputten ab, und der Kunde steht im nächsten
    // Monat wieder in derselben Mahnung.
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserWithStripe())
        ->test(PaymentMethod::class)
        ->call('confirmed', 'pm_neu123')
        ->assertHasNoErrors();

    expect($fake->defaultPaymentMethods['cus_42'])->toBe('pm_neu123');
});

it('shows which card is on file once one is', function () {
    $fake = new FakeStripeClient;
    $fake->defaultPaymentMethods['cus_42'] = 'pm_vorhanden';
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserWithStripe())
        ->test(PaymentMethod::class)
        ->assertSee('4242');
});

it('says so instead of drawing a form that never loads', function () {
    // Ohne Publishable Key lädt Elements nicht. Ein gezeichnetes, aber totes
    // Kartenfeld ist die schlimmste Variante: der Kunde tippt seine Nummer
    // ein, nichts passiert, und er hält seine Karte für abgelehnt.
    StripePublishableKey::set('');
    config()->set('services.stripe.key', '');
    config()->set('services.stripe.key_test', '');

    Livewire::actingAs(portalUserWithStripe())
        ->test(PaymentMethod::class)
        ->assertSet('clientSecret', null)
        ->assertSee(__('billing.card_unavailable'));
});

it('refuses a payment method id that is not one', function () {
    // `confirmed()` wird aus dem Browser gerufen. Ohne diese Prüfung wäre das
    // ein Weg, einen fremden Bezeichner an das eigene Stripe-Konto zu hängen.
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserWithStripe())
        ->test(PaymentMethod::class)
        ->call('confirmed', 'nicht-eine-pm-id')
        ->assertHasErrors();

    expect($fake->defaultPaymentMethods)->toBe([]);
});

it('is not reachable for a visitor without a customer record', function () {
    $fremder = User::factory()->create(['email' => 'ohne@portal.test', 'is_admin' => false]);

    Livewire::actingAs($fremder)
        ->test(PaymentMethod::class)
        ->assertStatus(404);
});
