<?php

namespace App\Services\Billing;

use App\Support\Settings;

/**
 * Wann gemahnt wird und was es kostet — an einer Stelle, einstellbar.
 *
 * Fünf Stufen, gezählt in Tagen seit der ersten gescheiterten Abbuchung:
 *
 *   0  Hinweis        „Ihre Abbuchung ist gescheitert, hier ist der Knopf"
 *   1  erste Mahnung
 *   2  zweite Mahnung   ab hier Gebühr (Vorgabe)
 *   3  dritte Mahnung
 *   4  Cloud aus
 *
 * **Stufe 0 ist keine Mahnung.** Keine Gebühr, keine Frist im Rechtssinn — nur
 * die Nachricht, dass etwas schiefging. Eine abgelaufene Karte ist keine
 * Zahlungsverweigerung, und wer sie am selben Tag mahnt, verliert Kunden, die
 * zahlen wollten.
 */
final class DunningSchedule
{
    public const DAYS = 'dunning.days';

    public const FEE_FROM_LEVEL = 'dunning.fee_from_level';

    public const FEES = 'dunning.fee_cents';

    /** Die letzte Stufe: ab hier steht die Cloud. */
    public const SUSPENDED = 4;

    /** @var array<int, int> Stufe => Tage seit dem ersten Fehlschlag */
    public const DEFAULT_DAYS = [0, 3, 10, 17, 24];

    /** @var array<int, int> Stufe => Gebühr in Cent */
    public const DEFAULT_FEES = [2 => 500, 3 => 1000];

    public const DEFAULT_FEE_FROM_LEVEL = 2;

    /**
     * Die fünf Fristen, aufsteigend.
     *
     * Sortiert statt geglaubt: eine Reihenfolge, die rückwärts läuft, ist kein
     * Zeitplan — der Tageslauf spränge über Stufen, und die dritte Mahnung
     * stünde vor der ersten. Ein Formular kann das erzeugen, also wird es hier
     * abgefangen und nicht dort erhofft.
     *
     * @return array<int, int>
     */
    public static function days(): array
    {
        $stored = Settings::get(self::DAYS);

        $days = is_array($stored) && count($stored) === count(self::DEFAULT_DAYS)
            ? array_map('intval', array_values($stored))
            : self::DEFAULT_DAYS;

        sort($days);

        return $days;
    }

    public static function dayOfLevel(int $level): int
    {
        return self::days()[$level] ?? self::DEFAULT_DAYS[$level] ?? 0;
    }

    /** Ab welcher Stufe eine Gebühr anfällt. */
    public static function feeFromLevel(): int
    {
        $stored = Settings::get(self::FEE_FROM_LEVEL);

        // Nie vor Stufe 1: Stufe 0 ist der Hinweis, und eine Gebühr darauf
        // wäre eine Mahngebühr ohne Mahnung.
        return is_numeric($stored) ? max(1, (int) $stored) : self::DEFAULT_FEE_FROM_LEVEL;
    }

    /** Was diese Stufe kostet, in Cent. Null unterhalb der eingestellten Stufe. */
    public static function feeCents(int $level): int
    {
        if ($level < self::feeFromLevel()) {
            return 0;
        }

        $stored = Settings::get(self::FEES);
        $fees = is_array($stored) ? $stored : self::DEFAULT_FEES;

        return (int) ($fees[$level] ?? self::DEFAULT_FEES[$level] ?? 0);
    }
}
