<?php
/**
 * Metric to US imperial display for recipe lines.
 *
 * Density is provided per recipe line (not stored on ingredients).
 *
 * @package RestaurantCosting
 */

declare(strict_types=1);

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

/**
 * Converts g/ml on a recipe line to kitchen-friendly imperial strings.
 */
final class RC_Unit_Converter {

	private const G_PER_OZ   = 28.349523125;
	private const ML_PER_OZ  = 29.5735295625;
	private const ML_PER_TSP = 4.92892159375;
	private const ML_PER_TBSP = 14.7867647813;
	private const ML_PER_CUP = 236.5882365;

	/**
	 * Format imperial equivalent for display/print.
	 *
	 * @param float       $quantity        Metric quantity.
	 * @param string      $unit            g, ml, or ea.
	 * @param float|null  $density_g_per_ml Optional line density (g/ml) for cross-unit context.
	 */
	public static function to_imperial( float $quantity, string $unit, ?float $density_g_per_ml = null ): string {
		$unit = strtolower( trim( $unit ) );

		if ( $quantity <= 0 ) {
			return '';
		}

		if ( 'ea' === $unit ) {
			return 1.0 === $quantity ? '1 ea' : self::format_number( $quantity ) . ' ea';
		}

		if ( 'g' === $unit ) {
			return self::format_weight_imperial( $quantity, $density_g_per_ml );
		}

		if ( 'ml' === $unit ) {
			return self::format_volume_imperial( $quantity, $density_g_per_ml );
		}

		return '';
	}

	/**
	 * Grams to oz (and lb when large). Optional density note for liquids by weight.
	 */
	private static function format_weight_imperial( float $grams, ?float $density ): string {
		$oz = $grams / self::G_PER_OZ;

		if ( $grams >= 454 ) {
			$lb     = floor( $grams / 454.0 );
			$remain = $grams - ( $lb * 454.0 );
			$oz_rem = $remain / self::G_PER_OZ;
			$parts  = array( self::format_number( $lb ) . ' lb' );
			if ( $oz_rem >= 0.05 ) {
				$parts[] = self::format_number( $oz_rem ) . ' oz';
			}
			$out = implode( ' ', $parts );
		} else {
			$out = self::format_number( $oz ) . ' oz';
		}

		if ( null !== $density && $density > 0 ) {
			$ml = $grams / $density;
			$out .= ' (' . self::format_volume_imperial( $ml, null ) . ')';
		}

		return $out;
	}

	/**
	 * ml to tsp / tbsp / cup / fl oz (largest sensible unit).
	 */
	private static function format_volume_imperial( float $ml, ?float $density ): string {
		if ( $ml >= self::ML_PER_CUP ) {
			$cups = $ml / self::ML_PER_CUP;
			$out  = self::format_number( $cups ) . ' cup';
		} elseif ( $ml >= self::ML_PER_TBSP ) {
			$tbsp = $ml / self::ML_PER_TBSP;
			$out  = self::format_number( $tbsp ) . ' tbsp';
		} elseif ( $ml >= self::ML_PER_TSP ) {
			$tsp = $ml / self::ML_PER_TSP;
			$out = self::format_number( $tsp ) . ' tsp';
		} else {
			$floz = $ml / self::ML_PER_OZ;
			$out  = self::format_number( $floz ) . ' oz';
		}

		if ( null !== $density && $density > 0 ) {
			$grams = $ml * $density;
			$out  .= ' (' . self::format_number( $grams / self::G_PER_OZ ) . ' oz wt)';
		}

		return $out;
	}

	/**
	 * Trim trailing zeros for display.
	 */
	private static function format_number( float $value ): string {
		$formatted = number_format( $value, 2, '.', '' );
		return rtrim( rtrim( $formatted, '0' ), '.' );
	}
}
