<?php
declare(strict_types=1);

namespace Itdatex\Ausgangsrechnung;

if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Automatische Rechnungsnummern nach frei definierbarem Muster.
 *
 * Muster-Platzhalter:
 *   {YYYY}   4-stelliges Jahr
 *   {YY}     2-stelliges Jahr
 *   {MM}     2-stelliger Monat
 *   {DD}     2-stelliger Tag
 *   {####}   Counter, gepaddet auf 4 Stellen (auch 5, 6, 7… erlaubt)
 *
 * Der Counter wird erst inkrementiert, wenn eine ausgestellte Rechnung
 * tatsaechlich mit dem Vorschlag identisch war (`consumeIfMatched`).
 * Tippt der Nutzer im Formular eine eigene Nummer, bleibt der Counter
 * unangetastet.
 */
final class Numbering
{
    public const OPTION_PATTERN      = 'ausrg_numbering_pattern';
    public const OPTION_COUNTER      = 'ausrg_numbering_counter';
    public const OPTION_YEARLY_RESET = 'ausrg_numbering_yearly_reset';
    public const OPTION_LAST_YEAR    = 'ausrg_numbering_last_year';

    public const DEFAULT_PATTERN = '{YYYY}-{####}';

    public static function pattern(): string
    {
        $p = trim( (string) get_option( self::OPTION_PATTERN, self::DEFAULT_PATTERN ) );
        return $p !== '' ? $p : self::DEFAULT_PATTERN;
    }

    public static function counter(): int
    {
        return max( 1, (int) get_option( self::OPTION_COUNTER, 1 ) );
    }

    public static function yearlyReset(): bool
    {
        return (bool) get_option( self::OPTION_YEARLY_RESET, true );
    }

    /**
     * Berechnet die naechste vorgeschlagene Rechnungsnummer, OHNE den
     * Counter zu inkrementieren.
     */
    public static function nextSuggestion( ?\DateTimeImmutable $now = null ): string
    {
        $now     = $now ?? new \DateTimeImmutable( 'today' );
        $counter = self::effectiveCounter( $now );
        return self::render( self::pattern(), $counter, $now );
    }

    /**
     * Rendert ein Pattern mit gegebenem Counter und Datum. Public gemacht,
     * damit die Settings-Preview den Effekt einer Pattern-Aenderung zeigen
     * kann, ohne den Persistenz-Counter zu beruehren.
     */
    public static function render( string $pattern, int $counter, ?\DateTimeImmutable $now = null ): string
    {
        $now = $now ?? new \DateTimeImmutable( 'today' );

        $result = str_replace(
            [ '{YYYY}', '{YY}', '{MM}', '{DD}' ],
            [ $now->format( 'Y' ), $now->format( 'y' ), $now->format( 'm' ), $now->format( 'd' ) ],
            $pattern
        );

        // Counter-Platzhalter {####} bis {##########} — Padding == Anzahl # Zeichen.
        $result = preg_replace_callback(
            '/\{(#{2,10})\}/',
            static fn( array $m ): string => str_pad( (string) $counter, strlen( $m[1] ), '0', STR_PAD_LEFT ),
            $result
        );

        return (string) $result;
    }

    /**
     * Wenn die ausgestellte Nummer identisch mit dem aktuellen Vorschlag
     * war, Counter inkrementieren (mit ggf. Jahres-Reset).
     * Gibt true zurueck, wenn der Counter tatsaechlich veraendert wurde.
     */
    public static function consumeIfMatched( string $usedNumber, ?\DateTimeImmutable $now = null ): bool
    {
        $now = $now ?? new \DateTimeImmutable( 'today' );
        if ( $usedNumber !== self::nextSuggestion( $now ) ) {
            return false;
        }
        $newCounter = self::effectiveCounter( $now ) + 1;
        update_option( self::OPTION_COUNTER,   $newCounter );
        update_option( self::OPTION_LAST_YEAR, $now->format( 'Y' ) );
        return true;
    }

    /**
     * Der wirklich aktive Counter — beruecksichtigt Jahres-Reset.
     */
    private static function effectiveCounter( \DateTimeImmutable $now ): int
    {
        if ( ! self::yearlyReset() ) {
            return self::counter();
        }
        $lastYear = (string) get_option( self::OPTION_LAST_YEAR, '' );
        if ( $lastYear === '' || $lastYear === $now->format( 'Y' ) ) {
            return self::counter();
        }
        // Jahreswechsel — Counter startet virtuell wieder bei 1, wird erst
        // beim naechsten consumeIfMatched persistiert.
        return 1;
    }
}
