<?php

namespace App\Actions;

use App\Models\DunningCase;
use App\Models\Subscription;
use App\Services\Billing\DunningMailer;
use App\Services\Billing\DunningSchedule;
use Illuminate\Support\Carbon;

/**
 * Der Fall, der entsteht, wenn eine Abbuchung zum ersten Mal scheitert.
 *
 * Hängt an ApplyStripeBillingEvent::invoicePaymentFailed(), das bis hierher
 * `past_due` setzte und schwieg. Der Kunde erfuhr davon erst, wenn seine Cloud
 * stand.
 *
 * JE RECHNUNG GENAU EINMAL. Stripe versucht dieselbe Rechnung mehrfach, und
 * jeder Versuch ist ein eigenes Ereignis — ohne diese Bedingung entstünden drei
 * Fälle für eine Schuld, mit drei Mahnläufen und dreifachen Gebühren. Die
 * Eindeutigkeit steht zusätzlich als Index in der Tabelle, damit zwei
 * gleichzeitig eintreffende Ereignisse sie nicht umgehen können.
 *
 * Eröffnet wird auf Stufe 0 — dem HINWEIS. Keine Mahnung, keine Gebühr: eine
 * abgelaufene Karte ist keine Zahlungsverweigerung.
 */
class OpenDunningCase
{
    public function __invoke(Subscription $subscription, string $invoiceId): ?DunningCase
    {
        if ($invoiceId === '') {
            return null;
        }

        $now = Carbon::now();

        $case = DunningCase::query()->firstOrCreate(
            ['stripe_invoice_id' => $invoiceId],
            [
                'subscription_id' => $subscription->id,
                'level' => 0,
                'opened_at' => $now,
                // Die erste Mahnung, nicht der Hinweis: der geht sofort hinaus.
                'next_step_at' => $now->copy()->addDays(DunningSchedule::dayOfLevel(1)),
                'fee_invoice_ids' => [],
                'notified_levels' => [],
            ],
        );

        // Der Hinweis geht SOFORT hinaus, nicht erst mit dem Tageslauf: der
        // Kunde soll von der gescheiterten Abbuchung erfahren, solange er noch
        // weiss, wovon die Rede ist. Nur beim ERSTEN Mal — wasRecentlyCreated
        // unterscheidet das Eröffnen vom Wiedersehen derselben Rechnung.
        if ($case->wasRecentlyCreated) {
            app(DunningMailer::class)->level($case->load('subscription.customer'), 0);

            // VERMERKEN, sonst hält der Nachhol-Lauf des nächsten Tages diese
            // Nachricht für verloren und schickt dem Kunden dieselbe Mail ein
            // zweites Mal. Erst nach dem Einreihen: andersherum stünde hier
            // eine Nachricht, die nie einging.
            $case->update(['notified_levels' => [0]]);
        }

        return $case;
    }
}
