<?php
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.DirectDatabaseQuery.SchemaChange
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Manages sequential document numbering per document type and year.
 * Supports prefix, suffix, padding, and yearly/monthly reset.
 */
class PBUAIV_Sequence {

    private static ?PBUAIV_Sequence $instance = null;

    public static function instance() : self {
        if ( null === self::$instance ) self::$instance = new self();
        return self::$instance;
    }

    private function __construct() {}

    /**
     * Generate and reserve the next document number.
     *
     * @param  string $doc_type  e.g. 'invoice', 'receipt', 'credit_note', 'proforma'
     * @return string            Formatted document number, e.g. "INV-2026-0042"
     */
    public function next( string $doc_type ) : string {
        global $wpdb;
        $table = $wpdb->prefix . 'pbuaiv_sequences';
        $year  = (int) wp_date( 'Y' );

        // Atomic increment using DB-level transaction
        $wpdb->query( 'START TRANSACTION' );

        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, next_number FROM {$table} WHERE doc_type = %s AND year = %d FOR UPDATE",
            $doc_type, $year
        ) );

        if ( $row ) {
            $number = (int) $row->next_number;
            $wpdb->update(
                $table,
                [ 'next_number' => $number + 1 ],
                [ 'id' => $row->id ],
                [ '%d' ], [ '%d' ]
            );
        } else {
            $number = 1;
            $wpdb->insert( $table, [
                'doc_type'    => $doc_type,
                'year'        => $year,
                'next_number' => 2,
            ], [ '%s', '%d', '%d' ] );
        }

        $wpdb->query( 'COMMIT' );

        return $this->format( $doc_type, $number, $year );
    }

    /**
     * Peek at the next number without incrementing.
     */
    public function peek( string $doc_type ) : string {
        global $wpdb;
        $table = $wpdb->prefix . 'pbuaiv_sequences';
        $year  = (int) wp_date( 'Y' );
        $row   = $wpdb->get_row( $wpdb->prepare(
            "SELECT next_number FROM {$table} WHERE doc_type = %s AND year = %d",
            $doc_type, $year
        ) );
        $number = $row ? (int) $row->next_number : 1;
        return $this->format( $doc_type, $number, $year );
    }

    /**
     * Return supported document types and their canonical option keys.
     *
     * Keeping these keys in one explicit map prevents settings from being
     * saved under one option name and read later from another.
     *
     * @return array<string,array<string,string>>
     */
    public static function option_key_map() : array {
        return [
            'invoice'       => [ 'prefix' => 'pbuaiv_invoice_prefix',       'suffix' => 'pbuaiv_invoice_suffix',       'padding' => 'pbuaiv_invoice_padding',       'reset' => 'pbuaiv_invoice_reset' ],
            'receipt'       => [ 'prefix' => 'pbuaiv_receipt_prefix',       'suffix' => 'pbuaiv_receipt_suffix',       'padding' => 'pbuaiv_receipt_padding',       'reset' => 'pbuaiv_receipt_reset' ],
            'credit_note'   => [ 'prefix' => 'pbuaiv_credit_note_prefix',   'suffix' => 'pbuaiv_credit_note_suffix',   'padding' => 'pbuaiv_credit_note_padding',   'reset' => 'pbuaiv_credit_note_reset' ],
            'proforma'      => [ 'prefix' => 'pbuaiv_proforma_prefix',      'suffix' => 'pbuaiv_proforma_suffix',      'padding' => 'pbuaiv_proforma_padding',      'reset' => 'pbuaiv_proforma_reset' ],
            'packing_slip'  => [ 'prefix' => 'pbuaiv_packing_slip_prefix',  'suffix' => 'pbuaiv_packing_slip_suffix',  'padding' => 'pbuaiv_packing_slip_padding',  'reset' => 'pbuaiv_packing_slip_reset' ],
            'delivery_note' => [ 'prefix' => 'pbuaiv_delivery_note_prefix', 'suffix' => 'pbuaiv_delivery_note_suffix', 'padding' => 'pbuaiv_delivery_note_padding', 'reset' => 'pbuaiv_delivery_note_reset' ],
        ];
    }

    /**
     * Return one canonical option key for a supported document type/setting.
     */
    public static function option_key( string $doc_type, string $setting ) : string {
        $map = self::option_key_map();
        return $map[ $doc_type ][ $setting ] ?? '';
    }

    /**
     * Format a document number using plugin settings.
     */
    private function format( string $doc_type, int $number, int $year ) : string {
        $prefix_key  = self::option_key( $doc_type, 'prefix' );
        $suffix_key  = self::option_key( $doc_type, 'suffix' );
        $padding_key = self::option_key( $doc_type, 'padding' );
        $reset_key   = self::option_key( $doc_type, 'reset' );

        $prefix  = $prefix_key ? get_option( $prefix_key, 'DOC-' ) : 'DOC-';
        $suffix  = $suffix_key ? get_option( $suffix_key, '' ) : '';
        $padding = $padding_key ? (int) get_option( $padding_key, 4 ) : 4;
        $reset   = $reset_key ? get_option( $reset_key, 'yearly' ) : 'yearly';

        $padding = max( 1, min( 10, $padding ) );
        $padded  = str_pad( (string) $number, $padding, '0', STR_PAD_LEFT );

        if ( $reset === 'yearly' ) {
            return $prefix . $year . '-' . $padded . $suffix;
        }

        return $prefix . $padded . $suffix;
    }

    /**
     * Reset a sequence (set next number to 1).
     */
    public function reset( string $doc_type, int $year = 0 ) : void {
        global $wpdb;
        if ( ! $year ) $year = (int) wp_date( 'Y' );
        $wpdb->update(
            $wpdb->prefix . 'pbuaiv_sequences',
            [ 'next_number' => 1 ],
            [ 'doc_type' => $doc_type, 'year' => $year ],
            [ '%d' ], [ '%s', '%d' ]
        );
    }

    /**
     * Manually set the next number.
     */
    public function set_next( string $doc_type, int $next, int $year = 0 ) : void {
        global $wpdb;
        if ( ! $year ) $year = (int) wp_date( 'Y' );
        $table = $wpdb->prefix . 'pbuaiv_sequences';
        $row   = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$table} WHERE doc_type = %s AND year = %d", $doc_type, $year
        ) );
        if ( $row ) {
            $wpdb->update( $table, [ 'next_number' => $next ], [ 'doc_type' => $doc_type, 'year' => $year ], [ '%d' ], [ '%s', '%d' ] );
        } else {
            $wpdb->insert( $table, [ 'doc_type' => $doc_type, 'year' => $year, 'next_number' => $next ], [ '%s', '%d', '%d' ] );
        }
    }
}
