<?php
/**
 * Atomic sequential numbering via AUTO_INCREMENT claim table.
 * Numbers are assigned at DOCUMENT GENERATION time (never at order creation),
 * so gaps stay legally explainable and concurrency cannot duplicate numbers.
 *
 * @package FEPW
 */

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

/**
 * Class FEPW_Numbering
 */
class FEPW_Numbering {

	/**
	 * Claim the next number for a sequence.
	 *
	 * @param string $seq_key  'invoice' or 'credit_note' (per-year sequences: key-YYYY).
	 * @param int    $order_id Order id (audit trail).
	 * @return int Sequence position (1-based within the key).
	 */
	public static function claim( $seq_key, $order_id ) {
		global $wpdb;
		$table = $wpdb->prefix . 'fepw_sequences';

		$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- the atomic claim INSERT is the whole point of this table.
			$table,
			array(
				'seq_key'    => $seq_key,
				'order_id'   => $order_id,
				'claimed_at' => current_time( 'mysql', true ),
			),
			array( '%s', '%d', '%s' )
		);
		$claim_id = (int) $wpdb->insert_id;

		// Position within this sequence = count of claims with id <= ours (gap-free per key).
		return (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- own table; the position COUNT must never be cached (legal gap-free numbering).
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$table} WHERE seq_key = %s AND id <= %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- own table.
				$seq_key,
				$claim_id
			)
		);
	}

	/**
	 * Format a document number.
	 *
	 * @param string $type 'invoice'|'credit_note'.
	 * @param int    $order_id Order id.
	 * @return string
	 */
	public static function next_number( $type, $order_id ) {
		$settings = get_option( 'fepw_settings', array() );
		$format   = 'credit_note' === $type
			? ( isset( $settings['credit_format'] ) ? $settings['credit_format'] : 'AV-{YYYY}-{SEQ:5}' )
			: ( isset( $settings['number_format'] ) ? $settings['number_format'] : 'FA-{YYYY}-{SEQ:5}' );

		$year = wp_date( 'Y' );
		$seq  = self::claim( $type . '-' . $year, $order_id );

		$number = str_replace( array( '{YYYY}', '{YY}' ), array( $year, wp_date( 'y' ) ), $format );
		$number = preg_replace_callback(
			'/\{SEQ(?::(\d+))?\}/',
			function ( $m ) use ( $seq ) {
				$pad = isset( $m[1] ) ? (int) $m[1] : 5;
				return str_pad( (string) $seq, $pad, '0', STR_PAD_LEFT );
			},
			$number
		);

		return $number;
	}
}
