<?php

use App\Livewire\OpenInvoices;
use App\Models\Customer;
use App\Models\User;
use App\Services\Stripe\FakeStripeClient;
use App\Services\Stripe\StripeClient;
use Livewire\Livewire;

function portalUserOwing(FakeStripeClient $fake): User
{
    $fake->openInvoiceRows = [[
        'id' => 'in_1', 'number' => 'R-1', 'amount_due_cents' => 21480,
        'currency' => 'EUR', 'created_at' => '1750000000',
    ]];

    $user = User::factory()->create(['email' => 'schuldner@portal.test', 'is_admin' => false]);
    Customer::factory()->create(['email' => 'schuldner@portal.test', 'stripe_customer_id' => 'cus_42']);

    return $user;
}

it('shows what is still owed', function () {
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserOwing($fake))
        ->test(OpenInvoices::class)
        ->assertSee('R-1')
        ->assertSee('214,80');
});

it('settles an invoice when the customer asks', function () {
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserOwing($fake))
        ->test(OpenInvoices::class)
        ->call('pay', 'in_1')
        ->assertHasNoErrors();

    expect($fake->paidInvoices)->toBe(['in_1']);
});

it('shows a refusal instead of pretending it worked', function () {
    // Der Fall, der wirklich eintritt. Eine grüne Meldung über einer
    // abgelehnten Karte ist die teuerste Anzeige, die diese Seite haben kann:
    // der Kunde geht davon aus, es sei erledigt, und die nächste Nachricht,
    // die er bekommt, ist eine Mahnung.
    $fake = new FakeStripeClient;
    $fake->payDeclines = true;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserOwing($fake))
        ->test(OpenInvoices::class)
        ->call('pay', 'in_1')
        ->assertSee('declined');

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

it('refuses to pay an invoice that is not this customer\'s', function () {
    // Die ID kommt aus dem Browser. Ohne diese Prüfung könnte jemand eine
    // fremde Rechnungs-ID einreichen — und Stripe zöge sie beim fremden Kunden
    // ein, auf dessen Karte.
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    Livewire::actingAs(portalUserOwing($fake))
        ->test(OpenInvoices::class)
        ->call('pay', 'in_fremd')
        ->assertForbidden();

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

it('stops paying the rest after the first refusal', function () {
    // Alle weiteren scheitern an derselben Karte, und drei Ablehnungen
    // hintereinander lassen Stripes Betrugserkennung anschlagen — danach
    // lehnt sie auch die Karte ab, die in Ordnung wäre.
    $fake = new FakeStripeClient;
    $fake->payDeclines = true;
    app()->instance(StripeClient::class, $fake);

    $user = portalUserOwing($fake);
    $fake->openInvoiceRows[] = [
        'id' => 'in_2', 'number' => 'R-2', 'amount_due_cents' => 1000,
        'currency' => 'EUR', 'created_at' => '1750000000',
    ];

    Livewire::actingAs($user)->test(OpenInvoices::class)->call('payAll');

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

it('says so plainly when nothing is open', function () {
    $fake = new FakeStripeClient;
    app()->instance(StripeClient::class, $fake);

    $user = User::factory()->create(['email' => 'schuldenfrei@portal.test', 'is_admin' => false]);
    Customer::factory()->create(['email' => 'schuldenfrei@portal.test', 'stripe_customer_id' => 'cus_9']);

    Livewire::actingAs($user)
        ->test(OpenInvoices::class)
        ->assertSee(__('billing.invoices_none'));
});
