<?php
/**
 * DRBU_Pricing_Engine — centralizovana matematika popusta.
 *
 * Podržava dva modela popusta:
 *  - 'percentage' — popust je procenat cene dodatnog komada;
 *  - 'fixed'      — popust je fiksni iznos koji se oduzima od cene dodatnog komada.
 *
 * @package discount-rules-box-upsell
 */

defined( 'ABSPATH' ) || exit;

class DRBU_Pricing_Engine {

	/**
	 * Singleton instanca.
	 *
	 * @var DRBU_Pricing_Engine|null
	 */
	private static $instance = null;

	/**
	 * Vraća jedinu instancu.
	 *
	 * @return DRBU_Pricing_Engine
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Cena jednog komada sa primenjenim popustom.
	 *
	 * @param float  $base          Osnovna cena komada.
	 * @param float  $discount      Vrednost popusta (procenat ili iznos).
	 * @param string $discount_type 'percentage' ili 'fixed'.
	 * @return float
	 */
	public function item_price( $base, $discount, $discount_type ) {
		$base = max( 0, (float) $base );

		if ( 'fixed' === $discount_type ) {
			return max( 0, $base - (float) $discount );
		}

		$percent = max( 0, min( 100, (float) $discount ) );
		return $base * ( 1 - ( $percent / 100 ) );
	}

	/**
	 * Ukupan iznos paketa za zadatu količinu (1–3 komada po strukturi nivoa).
	 *
	 * Za količine veće od 3 primenjuje se struktura trećeg nivoa.
	 *
	 * @param float  $base          Osnovna cena komada.
	 * @param int    $qty           Količina.
	 * @param array  $tiers         Niz od 3 nivoa, svaki sa ključem 'discount'.
	 * @param string $discount_type 'percentage' ili 'fixed'.
	 * @return float
	 */
	/**
	 * Ukupan iznos paketa za zadatu količinu.
	 *
	 * Prvi komad obračunava se po osnovnoj ceni, a preostali komadi (qty - 1)
	 * sa popustom izabranog nivoa.
	 *
	 * @param float  $base          Osnovna cena komada.
	 * @param int    $qty           Količina (broj komada).
	 * @param array  $tiers         Niz nivoa sa definisanim popustima.
	 * @param string $discount_type 'percentage' ili 'fixed'.
	 * @param int|null $tier_index  Indeks nivoa (0-indexed).
	 * @return float
	 */
	public function tier_total( $base, $qty, array $tiers, $discount_type = 'percentage', $tier_index = null, $pricing_model = 'model_a' ) {
		$base = max( 0, (float) $base );
		$qty  = max( 1, (int) $qty );

		if ( 'model_b' === $pricing_model ) {
			// Model B: Full Quantity Discount (Discount applies across ALL items in pack)
			$idx = null !== $tier_index ? (int) $tier_index : min( count( $tiers ) - 1, $qty - 1 );
			$idx = max( 0, min( count( $tiers ) - 1, $idx ) );
			$disc_val = isset( $tiers[ $idx ]['discount'] ) ? (float) $tiers[ $idx ]['discount'] : 0;

			if ( 'fixed' === $discount_type ) {
				$unit_disc = max( 0, $base - $disc_val );
				$raw_total = $unit_disc * $qty;
			} else {
				$perc      = max( 0, min( 100, $disc_val ) );
				$raw_total = ( $base * $qty ) * ( 1 - ( $perc / 100 ) );
			}
		} elseif ( 'model_c' === $pricing_model ) {
			// Model C: Fixed Package Price (Exact custom total price for entire pack)
			$idx = null !== $tier_index ? (int) $tier_index : min( count( $tiers ) - 1, $qty - 1 );
			$idx = max( 0, min( count( $tiers ) - 1, $idx ) );

			// Bezbednosna napomena: konfigurisana fiksna cena važi za PAKET
			// veličine $tier_qty (npr. 3 komada za $39.99), ne za bilo koju
			// količinu. $qty ovde može biti korisnički izmenjena vrednost sa
			// strane korpe (WooCommerce standardno dozvoljava izmenu polja
			// za količinu posle dodavanja u korpu) i NE sme se tretirati kao
			// da uvek odgovara originalnoj veličini paketa — u suprotnom bi
			// se ukupna cena paketa ($raw_total) delila i pa množila istim
			// $qty (linije ispod), što se matematički poništava i ostavlja
			// $total konstantnim bez obzira koliko je $qty veliko, čime bi
			// kupac mogao da dobije proizvoljno veliku količinu za cenu
			// jednog paketa. Zato se ovde fiksna cena skalira srazmerno broju
			// paketa u korpi ($qty / $tier_qty) — isti princip po kom već
			// rade model_b/model_d.
			$tier_qty = isset( $tiers[ $idx ]['qty'] ) ? max( 1, (int) $tiers[ $idx ]['qty'] ) : $qty;

			if ( isset( $tiers[ $idx ]['fixed_price'] ) && '' !== $tiers[ $idx ]['fixed_price'] ) {
				$raw_total = max( 0, (float) $tiers[ $idx ]['fixed_price'] ) * ( $qty / $tier_qty );
			} elseif ( isset( $tiers[ $idx ]['discount'] ) && (float) $tiers[ $idx ]['discount'] > 0 ) {
				$raw_total = max( 0, (float) $tiers[ $idx ]['discount'] ) * ( $qty / $tier_qty );
			} else {
				$raw_total = $base * $qty;
			}
		} elseif ( 'model_d' === $pricing_model ) {
			// Model D: Fixed Price Per Unit (Exact custom per-unit price for each tier)
			$idx = null !== $tier_index ? (int) $tier_index : min( count( $tiers ) - 1, $qty - 1 );
			$idx = max( 0, min( count( $tiers ) - 1, $idx ) );

			$unit_val  = isset( $tiers[ $idx ]['discount'] ) ? (float) $tiers[ $idx ]['discount'] : $base;
			$raw_total = ( $unit_val > 0 ) ? ( $unit_val * $qty ) : ( $base * $qty );
		} else {
			// Model A: Incremental Item Discount (Discount applies ONLY to additional 2nd/3rd item)
			$disc_amount = 0;
			if ( $qty >= 2 && isset( $tiers[1]['discount'] ) ) {
				$d2           = (float) $tiers[1]['discount'];
				$disc_amount += ( 'fixed' === $discount_type ) ? $d2 : ( $base * ( $d2 / 100 ) );
			}
			if ( $qty >= 3 && isset( $tiers[2]['discount'] ) ) {
				$d3           = (float) $tiers[2]['discount'];
				$disc_amount += ( 'fixed' === $discount_type ) ? $d3 : ( $base * ( $d3 / 100 ) );
			}
			if ( $qty > 3 && isset( $tiers[2]['discount'] ) ) {
				$d3_extra     = (float) $tiers[2]['discount'];
				$disc_amount += ( $qty - 3 ) * ( ( 'fixed' === $discount_type ) ? $d3_extra : ( $base * ( $d3_extra / 100 ) ) );
			}

			$base_total = $base * $qty;
			$raw_total  = max( 0, $base_total - $disc_amount );
		}

		$decimals   = function_exists( 'wc_get_price_decimals' ) ? wc_get_price_decimals() : 2;
		$unit_price = round( $raw_total / $qty, $decimals );
		$total      = round( $unit_price * $qty, $decimals );

		return apply_filters( 'drbu_tier_total', $total, $base, $qty, $tiers, $discount_type, $tier_index, $pricing_model );
	}

	public function unit_price( $base, $qty, array $tiers, $discount_type = 'percentage', $tier_index = null, $pricing_model = 'model_a' ) {
		$qty = max( 1, (int) $qty );
		return $this->tier_total( $base, $qty, $tiers, $discount_type, $tier_index, $pricing_model ) / $qty;
	}

	public function savings( $base, $qty, array $tiers, $discount_type = 'percentage', $tier_index = null, $pricing_model = 'model_a' ) {
		$qty = max( 1, (int) $qty );
		return max( 0, ( (float) $base * $qty ) - $this->tier_total( $base, $qty, $tiers, $discount_type, $tier_index, $pricing_model ) );
	}

	/**
	 * Veličina paketa koji se primenjuje za zadatu količinu.
	 *
	 * @param int $qty Količina.
	 * @return int
	 */
	public function pack_count( $qty ) {
		return max( 1, (int) $qty );
	}

	/**
	 * Prikazuje vrednost popusta za placeholder-e {d2}/{d3}.
	 *
	 * @param float  $discount      Vrednost popusta.
	 * @param string $discount_type 'percentage' ili 'fixed'.
	 * @param array  $settings      Ceo niz podešavanja (za prikaz valute).
	 * @return string
	 */
	public function format_discount_value( $discount, $discount_type, $settings = array() ) {
		if ( 'fixed' === $discount_type ) {
			return $this->format_price( (float) $discount, $settings );
		}
		return $this->trim_number( (float) $discount ) . '%';
	}

	/**
	 * Formatira cenu uz podršku za 'hide_zeros'.
	 *
	 * @param float $amount   Iznos.
	 * @param array $settings Ceo niz podešavanja.
	 * @return string
	 */
	public function format_price( $amount, $settings = array() ) {
		$amount = (float) $amount;
		$display = isset( $settings['drbu_currency_display'] ) ? $settings['drbu_currency_display'] : 'default';

		if ( 'hide_zeros' === $display && floor( $amount ) === $amount ) {
			return wc_price( $amount, array( 'decimals' => 0 ) );
		}

		return wc_price( $amount );
	}

	/**
	 * Skraćuje broj (40.0 → 40, 12.50 → 12.5).
	 *
	 * @param float $number Broj.
	 * @return string
	 */
	private function trim_number( $number ) {
		$trimmed = rtrim( rtrim( number_format( (float) $number, 2, '.', '' ), '0' ), '.' );
		return ( '' === $trimmed ) ? '0' : $trimmed;
	}
}
