<?php
/**
 * Sequential invoice numbering with locking.
 *
 * Supports two parallel sequences:
 * - "invoice": standard invoices (default prefix "F-")
 * - "credit_note": refund credit notes (default prefix "AV-")
 *
 * @package FacturX_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

class GOFACTOX_Numbering {

    private const LOCK_KEY     = 'gofactox_invoice_number_lock';
    private const LOCK_TIMEOUT = 10;

    public static function init(): void {
        // Nothing to hook: numbering is called on demand.
    }

    /**
     * Get the next document number and increment the counter.
     *
     * Uses a transient-based lock to prevent race conditions on concurrent orders.
     *
     * @param string $type Either "invoice" or "credit_note".
     * @return string Formatted document number (e.g., "F-2026-0001" or "AV-2026-0001").
     */
    public static function next_number( string $type = 'invoice' ): string {
        $options = self::get_options( $type );

        // Acquire lock (shared across both sequences to avoid double-increment under load).
        $lock_acquired = false;
        for ( $i = 0; $i < 50; $i++ ) {
            if ( false === get_transient( self::LOCK_KEY ) ) {
                set_transient( self::LOCK_KEY, 1, self::LOCK_TIMEOUT );
                $lock_acquired = true;
                break;
            }
            usleep( 100_000 ); // 100ms
        }

        if ( ! $lock_acquired ) {
            // Fallback: delete stale lock and proceed.
            delete_transient( self::LOCK_KEY );
        }

        try {
            $prefix    = get_option( $options['prefix_option'], $options['prefix_default'] );
            $digits    = (int) get_option( $options['digits_option'], $options['digits_default'] );
            $next      = (int) get_option( $options['next_option'], 1 );
            $year      = current_time( 'Y' );

            $formatted = self::format( $prefix, $year, $next, $digits );

            // Increment counter.
            update_option( $options['next_option'], $next + 1 );

            return $formatted;
        } finally {
            // Always release lock.
            delete_transient( self::LOCK_KEY );
        }
    }

    /**
     * Preview the next document number without incrementing.
     *
     * @param string $type Either "invoice" or "credit_note".
     */
    public static function preview_next( string $type = 'invoice' ): string {
        $options = self::get_options( $type );
        $prefix  = get_option( $options['prefix_option'], $options['prefix_default'] );
        $digits  = (int) get_option( $options['digits_option'], $options['digits_default'] );
        $next    = (int) get_option( $options['next_option'], 1 );
        $year    = current_time( 'Y' );

        return self::format( $prefix, $year, $next, $digits );
    }

    /**
     * Reset the counter (e.g., at the beginning of a new fiscal year).
     *
     * @param int    $start Starting number.
     * @param string $type  Either "invoice" or "credit_note".
     */
    public static function reset_counter( int $start = 1, string $type = 'invoice' ): void {
        $options = self::get_options( $type );
        update_option( $options['next_option'], $start );
    }

    /**
     * Format a document number from its parts.
     */
    private static function format( string $prefix, string $year, int $next, int $digits ): string {
        return sprintf(
            '%s%s-%s',
            $prefix,
            $year,
            str_pad( (string) $next, max( 1, $digits ), '0', STR_PAD_LEFT )
        );
    }

    /**
     * Map a document type to the option keys + defaults that drive its sequence.
     *
     * Invoices keep their historical option names ("gofactox_invoice_prefix" etc.)
     * for backward compatibility. Credit notes have their own parallel options.
     *
     * @return array{prefix_option:string, prefix_default:string, digits_option:string, digits_default:int, next_option:string}
     */
    private static function get_options( string $type ): array {
        if ( $type === 'credit_note' ) {
            return [
                'prefix_option'  => 'gofactox_credit_note_prefix',
                'prefix_default' => 'AV-',
                'digits_option'  => 'gofactox_credit_note_digits',
                'digits_default' => 4,
                'next_option'    => 'gofactox_next_credit_note_number',
            ];
        }

        return [
            'prefix_option'  => 'gofactox_invoice_prefix',
            'prefix_default' => 'F-',
            'digits_option'  => 'gofactox_invoice_digits',
            'digits_default' => 4,
            'next_option'    => 'gofactox_next_invoice_number',
        ];
    }
}
