<?php
/**
 * Recipe costing and nutrition calculations.
 *
 * @package RestaurantCosting
 */

declare(strict_types=1);

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

/**
 * Matches spreadsheet logic: line costs per portion + labour, nutrition per portion.
 */
final class RC_Recipe_Calculator {

	/**
	 * @param object               $recipe  Recipe row (portions, scale_factor, menu_price, labor_rate).
	 * @param array<int, object>   $lines   Line rows with ingredient joined or ingredient_id.
	 * @param array<int, object>   $labor   Labour rows.
	 * @return array<string, mixed>
	 */
	public static function calculate( object $recipe, array $lines, array $labor = array() ): array {
		$portions     = max( 0.0001, (float) $recipe->portions );
		$labor_rate   = (float) ( $recipe->labor_rate ?? 25.0 );
		$menu_price   = isset( $recipe->menu_price ) ? (float) $recipe->menu_price : 0.0;

		$line_costs    = 0.0;
		$nutrition     = self::empty_nutrition();
		$line_details  = array();
		$portion_weight = 0.0;

		foreach ( $lines as $line ) {
			$qty    = (float) $line->quantity;
			$unit   = (string) $line->unit;
			$detail = array(
				'line_id'          => (int) ( $line->id ?? 0 ),
				'ingredient_name'  => (string) $line->ingredient_name,
				'quantity'         => $qty,
				'unit'             => $unit,
				'density_g_per_ml' => isset( $line->density_g_per_ml ) ? (float) $line->density_g_per_ml : null,
				'line_cost'        => 0.0,
				'imperial'         => '',
			);

			$density = isset( $line->density_g_per_ml ) && (float) $line->density_g_per_ml > 0
				? (float) $line->density_g_per_ml
				: null;

			$detail['imperial'] = RC_Unit_Converter::to_imperial( $qty, $unit, $density );

			if ( ! empty( $line->ingredient_id ) ) {
				$ingredient = RC_Ingredient_Repository::get( (int) $line->ingredient_id );
				if ( $ingredient ) {
					$detail['line_cost'] = RC_Ingredient_Repository::line_cost(
						(int) $line->ingredient_id,
						$qty,
						$portions,
						$unit
					);
					$line_costs += $detail['line_cost'];
					self::add_nutrition( $nutrition, $ingredient, $qty, $portions, $unit );

					// Portion weight
					$ref = (float) $ingredient->weight_grams;
					if ( $ref > 0 ) {
						if ( 'ea' === $unit ) {
							$portion_weight += ( $qty * $ref ) / $portions;
						} elseif ( 'ml' === $unit && $density > 0 ) {
							$portion_weight += ( $qty * $density ) / $portions;
						} else {
							$portion_weight += $qty / $portions;
						}
					}
				}
			}

			$line_details[] = $detail;
		}

		$labor_total = 0.0;
		foreach ( $labor as $lab ) {
			$minutes = (float) $lab->minutes;
			$labor_total += ( $minutes / 60.0 ) * $labor_rate;
		}
		$labor_per_portion = $labor_total / $portions;

		$cost_per_portion = round( $line_costs + $labor_per_portion, 2 );

		$food_cost_pct    = 0.0;
		$gross_profit     = 0.0;
		$gross_margin_pct = 0.0;

		if ( $menu_price > 0 ) {
			$food_cost_pct    = round( ( $cost_per_portion / $menu_price ) * 100, 2 );
			$gross_profit     = round( $menu_price - $cost_per_portion, 2 );
			$gross_margin_pct = round( ( $gross_profit / $menu_price ) * 100, 2 );
		}

		foreach ( $nutrition as $key => $val ) {
			$nutrition[ $key ] = round( $val, 2 );
		}

		return array(
			'line_details'       => $line_details,
			'ingredient_cost'    => round( $line_costs, 2 ),
			'labor_total'        => round( $labor_total, 2 ),
			'labor_per_portion'  => round( $labor_per_portion, 2 ),
			'cost_per_portion'   => $cost_per_portion,
			'menu_price'         => $menu_price,
			'food_cost_pct'      => $food_cost_pct,
			'gross_profit'       => $gross_profit,
			'gross_margin_pct'   => $gross_margin_pct,
			'nutrition'          => $nutrition,
			'portions'           => $portions,
			'portion_weight'     => round( $portion_weight, 2 ),
		);
	}

	/**
	 * @return array<string, float>
	 */
	private static function empty_nutrition(): array {
		return array(
			'calories'           => 0.0,
			'fat_grams'          => 0.0,
			'saturated_fat_grams' => 0.0,
			'trans_fat_grams'    => 0.0,
			'carb_grams'         => 0.0,
			'fiber_grams'        => 0.0,
			'sugar_grams'        => 0.0,
			'cholesterol_mg'     => 0.0,
			'protein_grams'      => 0.0,
			'sodium_mg'          => 0.0,
		);
	}

	/**
	 * @param array<string, float> $totals   Running totals.
	 * @param object               $ingredient Ingredient row.
	 */
	private static function add_nutrition( array &$totals, object $ingredient, float $qty, float $portions, string $unit = 'g' ): void {
		$ref = (float) $ingredient->weight_grams;
		if ( $ref <= 0 ) {
			return;
		}

		// For ea, each unit represents the full reference amount
		$factor = ( 'ea' === $unit ) ? ( $qty / $portions ) : ( $qty / $ref / $portions );
		$map    = array(
			'calories'           => 'calories',
			'fat_grams'          => 'fat_grams',
			'saturated_fat_grams' => 'saturated_fat_grams',
			'trans_fat_grams'    => 'trans_fat_grams',
			'carb_grams'         => 'carb_grams',
			'fiber_grams'        => 'fiber_grams',
			'sugar_grams'        => 'sugar_grams',
			'cholesterol_mg'     => 'cholesterol_mg',
			'protein_grams'      => 'protein_grams',
			'sodium_mg'          => 'sodium_mg',
		);

		foreach ( $map as $total_key => $ing_key ) {
			if ( null !== $ingredient->$ing_key && '' !== $ingredient->$ing_key ) {
				$totals[ $total_key ] += (float) $ingredient->$ing_key * $factor;
			}
		}
	}
}
