<?php

namespace App\Actions;

use App\Console\Commands\AdvanceDunning;
use App\Models\DunningCase;
use App\Services\Billing\DunningMailer;
use App\Services\Billing\DunningSchedule;
use App\Services\Stripe\StripeClient;
use Illuminate\Support\Carbon;

/**
 * Einen Mahnfall schliessen — aber erst, wenn WIRKLICH alles bezahlt ist.
 *
 * Die Bedingung des Betreibers lautete „bis der Betrag + Mahngebühren beglichen
 * ist". Wer den Rückstand zahlt und die Mahnspesen offen lässt, ist damit nicht
 * fertig, und seine Cloud bleibt aus.
 *
 * Gefragt wird STRIPE, nicht der eigene Zustand: die offenen Rechnungen des
 * Kunden sind die Wahrheit über das, was noch aussteht. Ein eigenes Feld
 * „bezahlt" je Gebührenrechnung wäre eine zweite Buchhaltung, die mit der
 * ersten auseinanderläuft — und zwar genau dann, wenn eine Zahlung ausserhalb
 * unseres Ablaufs eingeht.
 */
class SettleDunningCase
{
    public function __invoke(DunningCase $case): bool
    {
        if (! $case->isOpen()) {
            return false;
        }

        $customerId = $case->subscription?->customer?->stripe_customer_id;

        if (blank($customerId)) {
            return false;
        }

        $offen = array_column(app(StripeClient::class)->openInvoices((string) $customerId), 'id');

        // Platzhalter zählen nicht mit: sie sind keine Stripe-ID, und wogegen
        // man sie prüfen sollte, weiss niemand. Sie stehen im Fall und auf der
        // Seite — dort kann ein Mensch nachsehen. Sie hier als „offen" zu
        // werten hiesse, den Fall für immer festzuhalten.
        $unsere = array_values(array_filter(
            array_merge([$case->stripe_invoice_id], array_values($case->fee_invoice_ids ?? [])),
            fn (string $id) => $id !== AdvanceDunning::PENDING,
        ));

        // Steht noch eine davon offen, ist der Fall nicht zu.
        if (array_intersect($unsere, $offen) !== []) {
            return false;
        }

        $case->update(['settled_at' => Carbon::now()]);

        $stand = $case->level;

        // Und die Cloud kommt zurück — NUR die dieses Vertrags.
        //
        // Vorher holte eine Zahlung auf Vertrag A jede gesperrte Cloud des
        // Kunden zurück, auch die, für die noch geschuldet wird (Codex P1).
        // Ein Kunde mit zwei Verträgen hätte durch das Begleichen des einen
        // den anderen freibekommen.
        $instance = $case->instance();

        if ($instance !== null && $instance->suspended_at !== null) {
            app(ResumeInstance::class)($instance);
        }

        // Nur wer wirklich stand, bekommt die Entwarnung. Wer auf Stufe 1
        // bezahlt hat, hat nie eine Sperre gesehen — ihm zu schreiben, seine
        // Cloud laufe wieder, wäre eine Nachricht über etwas, das nie war.
        if ($stand >= DunningSchedule::SUSPENDED) {
            app(DunningMailer::class)->resumed($case);
        }

        return true;
    }
}
