<?php

declare( strict_types=1 );

namespace Brightfern\Support;

/**
 * Numeric helpers for EN 16931 amounts.
 *
 * EN 16931 / Peppol BIS rules we encode here:
 *  - Monetary amounts (BT-106..BT-115, BT-131, BT-99, …) are expressed with
 *    exactly 2 decimals.
 *  - Unit prices (BT-146) and quantities (BT-129) may carry more decimals.
 *  - Rounding is "round half away from zero" (commercial rounding) at each
 *    documented boundary; we deliberately round at line/total boundaries so the
 *    VAT breakdown reconciles to the cent (BR-CO-* rules in the schematron).
 *
 * We keep amounts as PHP floats inside the model and only convert to fixed-
 * decimal strings at serialization time. All comparisons in the validator use
 * cents to avoid float drift.
 */
final class Num {

	/** Round a monetary amount to 2 decimals (BT-level amounts). */
	public static function money( float $value ): float {
		return round( $value, 2, PHP_ROUND_HALF_UP );
	}

	/** Round a price/quantity to a higher precision (UBL allows up to 4–6). */
	public static function rate( float $value, int $precision = 4 ): float {
		return round( $value, $precision, PHP_ROUND_HALF_UP );
	}

	/**
	 * Format a monetary amount for UBL output: 2 decimals, dot separator, no
	 * thousands grouping. Always emits e.g. "10.00", never "10".
	 */
	public static function amount( float $value ): string {
		return number_format( self::money( $value ), 2, '.', '' );
	}

	/**
	 * Format a percentage (VAT rate BT-152 / BT-119) for UBL. Peppol expects up
	 * to 2 decimals and no trailing-zero padding requirement, but a stable
	 * 2-decimal form ("21.00") is the safest for schematron equality checks.
	 */
	public static function percent( float $value ): string {
		return number_format( self::money( $value ), 2, '.', '' );
	}

	/** Format a quantity/price value with variable precision. */
	public static function quantity( float $value, int $precision = 4 ): string {
		$formatted = number_format( self::rate( $value, $precision ), $precision, '.', '' );
		// Trim needless trailing zeros but keep at least one decimal place.
		$formatted = rtrim( $formatted, '0' );
		return str_ends_with( $formatted, '.' ) ? $formatted . '00' : $formatted;
	}

	/** Compare two monetary amounts at cent precision (avoids float equality bugs). */
	public static function moneyEquals( float $a, float $b ): bool {
		return (int) round( $a * 100 ) === (int) round( $b * 100 );
	}
}
