<?php

namespace App\Livewire;

use App\Models\Customer;
use App\Services\Stripe\StripeClient;
use App\Support\StripePublishableKey;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Livewire\Component;

/**
 * Die Karte des Kunden, getauscht ohne dass ein Kartendatum diesen Server
 * berührt: Stripe Elements schickt sie direkt an Stripe und bekommt eine ID
 * zurück, und nur die kommt hier an.
 *
 * Bis zum 31. Juli 2026 gab es das nicht — im ganzen Portal keine
 * Zahlungsmittel-Verwaltung. Eine abgelaufene Karte war damit eine Sackgasse,
 * und jeder Mahnlauf darüber hätte jemanden für etwas gemahnt, das er nicht
 * abstellen kann.
 *
 * Der SetupIntent entsteht beim Aufbau der Seite. Er ist an den Stripe-Kunden
 * gebunden und überlebt einen Neuaufbau, kostet also nichts, wenn niemand
 * etwas eingibt.
 */
class PaymentMethod extends Component
{
    /**
     * Das Geheimnis, mit dem der Browser den SetupIntent bestätigt.
     *
     * Null heißt: es fehlt der veröffentlichbare Schlüssel. Dann wird das
     * gesagt, statt ein Kartenfeld zu zeichnen, das nie lädt — der Kunde tippt
     * sonst seine Nummer ein, nichts passiert, und er hält seine Karte für
     * abgelehnt.
     */
    public ?string $clientSecret = null;

    /** @var array{id: string, brand: string, last4: string, exp_month: int, exp_year: int}|null */
    public ?array $card = null;

    public function mount(): void
    {
        $stripe = app(StripeClient::class);
        $customerId = $this->stripeCustomerId();

        $this->card = $stripe->defaultPaymentMethod($customerId);

        if (StripePublishableKey::current() === '') {
            return;
        }

        $this->clientSecret = $stripe->createSetupIntent($customerId)['client_secret'];
    }

    /**
     * Vom Browser gerufen, nachdem Elements den SetupIntent bestätigt hat.
     *
     * Die ID kommt damit aus dem Browser und wird geprüft, bevor sie an Stripe
     * weitergereicht wird — sonst wäre das ein Weg, einen beliebigen Bezeichner
     * an das eigene Stripe-Konto zu hängen.
     */
    public function confirmed(string $paymentMethodId): void
    {
        Validator::make(
            ['paymentMethodId' => $paymentMethodId],
            ['paymentMethodId' => ['required', 'string', 'max:255', 'regex:/^pm_[A-Za-z0-9]+$/']],
        )->validate();

        $stripe = app(StripeClient::class);
        $customerId = $this->stripeCustomerId();

        // Hinterlegen genügt nicht — ohne diesen Schritt bucht Stripe weiter
        // mit der alten Karte ab.
        $stripe->setDefaultPaymentMethod($customerId, $paymentMethodId);
        $this->card = $stripe->defaultPaymentMethod($customerId);

        $this->dispatch('notify', message: __('billing.card_saved'));
    }

    private function stripeCustomerId(): string
    {
        $customer = Customer::forUser(Auth::user());

        abort_if($customer === null || blank($customer->stripe_customer_id), 404);

        return (string) $customer->stripe_customer_id;
    }

    public function render()
    {
        return view('livewire.payment-method', [
            'publishableKey' => StripePublishableKey::current(),
        ]);
    }
}
