<?php

namespace App\OfferCalculator;

class Output
{

	/**
	 * The offer input details.
	 * @var \App\OfferCalculator\Input
	 */
	protected $offerInput;

	/**
	 * The currency - copied from offerInput object; all offer components below are calculated in this currency.
	 * @var String
	 */
	public $currency;

	/**
	 * The supplier price value (in final currency).
	 * @var Float
	 */
	public $supplierValue;

	/**
	 * The supplier price VAT (in final currency).
	 * @var Float
	 */
	public $supplierVat;

	/**
	 * The commission value (in final currency).
	 * @var Float
	 */
	public $commissionValue;

	/**
	 * The commission VAT (in final currency).
	 * @var Float
	 */
	public $commissionVat;

	/**
	 * The discount value (in final currency).
	 * @var Float
	 */
	public $discountValue;

	/**
	 * The discount VAT (in final currency).
	 * @var Float
	 */
	public $discountVat;

	/**
	 * The service fee value (in final currency).
	 * @var Float
	 */
	public $serviceFeeValue;

	/**
	 * The service fee VAT (in final currency).
	 * @var Float
	 */
	public $serviceFeeVat;

	/**
	 * The tax value (in final currency).
	 * @var Float
	 */
	public $taxValue;

	/**
	 * The tax VAT (in final currency).
	 * @var Float
	 */
	public $taxVat;

	/**
	 * The offer total value (in final currency).
	 * @var Float
	 */
	public $total;

	/**
	 * The net company value (in final currency).
	 * @var Float
	 */
	public $netCompany;

	/**
	 * Attaches the offer input object.
	 * @param Input $offerInput
	 */
	public function __construct(Input $offerInput)
	{
		$this->offerInput = $offerInput;
	}

	/**
	 * Add possibility to overwrite the offerInput object from constructor.
	 * @param Input $offerInput
	 */
	public function setOfferInput(Input $offerInput)
	{
		$this->offerInput = $offerInput;
	}

	/**
	 * Calculates all offer attributes and associates them to the current object.
	 * @throws \Exception - if the offer was not processed
	 */
	public function calculate()
	{

		// apply percents if it was not already applied
		if (!$this->offerInput->isProcessed()) {
			throw new \Exception('The offer was not processed - you must call applyPercents() on the offer input first.');
		}

		// populate the currency
		$curr = $this->currency = $this->offerInput->totalCurrency;

		$convObj = \App\CurrencyExchange\Rates::getInstance($this->offerInput->currencyDate, $this->offerInput->currencyType);

		// CALCULATES THE COMMISSION --------------------------------------------------------------
		$commissionBulk = $convObj->convert($this->offerInput->commissionValue, $this->offerInput->commissionCurrency, $curr, null);
		if ($this->offerInput->commissionVatIncluded) {
			$commissionVal = $commissionBulk * 100 / (100+$this->offerInput->commissionVatPercent);
			$commissionVat = $commissionBulk * $this->offerInput->commissionVatPercent / (100+$this->offerInput->commissionVatPercent);
		} else {
			$commissionVal = $commissionBulk;
			$commissionVat = $commissionBulk * $this->offerInput->commissionVatPercent / 100;
		}

		// associate commission components
		$this->commissionValue = $commissionVal;
		$this->commissionVat = $commissionVat;

		// apply quntity multiplier
		$this->commissionValue *= $this->offerInput->quantity;
		$this->commissionVat *= $this->offerInput->quantity;


		// CALCULATE SUPPLIER PRICE ---------------------------------------------------------------
		$servicePriceBulk = $convObj->convert($this->offerInput->servicePriceValue, $this->offerInput->servicePriceCurrency, $curr, null);
		// -1 VAT percent for service price
		if ($this->offerInput->servicePriceVatPercent==-1) {
			$servicePriceBulkVat = $convObj->convert($this->offerInput->servicePriceVat, $this->offerInput->servicePriceCurrency, $curr, null);
			if ($this->offerInput->servicePriceVatIncluded) {
				$servicePriceBulkVal = $servicePriceBulk - $servicePriceBulkVat;
			} else {
				$servicePriceBulkVal = $servicePriceBulk;
			}
		}
		// normal VAT percent for service price
		else {
			$vatPercent = $this->offerInput->servicePriceVatPercent;
			if ($this->offerInput->servicePriceVatIncluded) {
				$servicePriceBulkVal = $servicePriceBulk * 100 / (100+$vatPercent) ;
				$servicePriceBulkVat = $servicePriceBulk * $vatPercent / (100+$vatPercent) ;
			} else {
				$servicePriceBulkVal = $servicePriceBulk ;
				$servicePriceBulkVat = $servicePriceBulk * $vatPercent / 100 ;
			}
		}

		// depending on commissionable/net price - determine the supplier price
		if (!$this->offerInput->commissionable) { // net price
			$this->supplierValue = $servicePriceBulkVal;
			$this->supplierVat = $servicePriceBulkVat;
		} else { // commissionable
			// for -1 VAT on service price - the VATs are substracted (price-commission)
			if ($this->offerInput->servicePriceVatPercent==-1) {
				$this->supplierValue = $servicePriceBulkVal - $commissionVal;
				$this->supplierVat = $servicePriceBulkVat - $commissionVat;
			}
			// for normal VAT perent - the supplier price is calculated by eliminating the commission first, and then the VAT is recalculated
			else {
				$servicePriceBulk = $servicePriceBulkVal+$servicePriceBulkVat-$commissionVal-$commissionVat;
				$this->supplierValue = $servicePriceBulk * 100 / (100+$vatPercent) ;
				$this->supplierVat = $servicePriceBulk * $vatPercent / (100+$vatPercent) ;
			}
		}

		// apply quntity multiplier
		$this->supplierValue *= $this->offerInput->quantity;
		$this->supplierVat *= $this->offerInput->quantity;


		// CALCULATE DISCOUNT VALUE ---------------------------------------------------------------
		$discountBulk = $convObj->convert($this->offerInput->discountValue, $this->offerInput->discountCurrency, $curr, null);

		$vatPercent = $this->offerInput->discountVatPercent;
		if ($this->offerInput->discountVatIncluded) {
			$this->discountValue = $discountBulk * 100 / (100+$vatPercent);
			$this->discountVat = $discountBulk * $vatPercent / (100+$vatPercent);
		} else {
			$this->discountValue = $discountBulk ;
			$this->discountVat = $discountBulk * $vatPercent / 100 ;
		}

		// apply quntity multiplier
		$this->discountValue *= $this->offerInput->quantity;
		$this->discountVat *= $this->offerInput->quantity;


		// CALCULATE SERVICE FEE VALUE ------------------------------------------------------------
		$serviceFeeBulk = $convObj->convert($this->offerInput->serviceFeeValue, $this->offerInput->serviceFeeCurrency, $curr, null);
		if ($this->offerInput->serviceFeeMode=='segment') {
			$serviceFeeBulk *= $this->offerInput->segmentsCount;
		}
		elseif ($this->offerInput->serviceFeeMode=='duration') {
			$serviceFeeBulk *= $this->offerInput->duration;
		}
		$vatPercent = $this->offerInput->serviceFeeVatPercent;
		if ($this->offerInput->serviceFeeVatIncluded) {
			$this->serviceFeeValue = $serviceFeeBulk * 100 / (100+$vatPercent);
			$this->serviceFeeVat= $serviceFeeBulk * $vatPercent / (100+$vatPercent);
		} else {
			$this->serviceFeeValue = $serviceFeeBulk ;
			$this->serviceFeeVat= $serviceFeeBulk * $vatPercent / 100 ;
		}


		// CALCULATE TAX VALUE --------------------------------------------------------------------
		$taxBulk = $convObj->convert($this->offerInput->taxValue, $this->offerInput->taxCurrency, $curr, null);
		if ($this->offerInput->taxVatPercent==-1) {
			$this->taxVat = $convObj->convert($this->offerInput->taxVat, $this->offerInput->taxCurrency, $curr, null);
			if ($this->offerInput->taxVatIncluded) {
				$this->taxValue = $taxBulk - $this->taxVat ;
			} else {
				$this->taxValue = $taxBulk ;
			}
		} else {
			$vatPercent = $this->offerInput->taxVatPercent;
			if ($this->offerInput->taxVatIncluded) {
				$this->taxValue = $taxBulk * 100 / (100+$vatPercent) ;
				$this->taxVat = $taxBulk * $vatPercent / (100+$vatPercent) ;
			} else {
				$this->taxValue = $taxBulk ;
				$this->taxVat = $taxBulk * $vatPercent / 100 ;
			}
		}

		// CALCULATE NET COMPANY AND OFFER TOTAL //////////////////////////////////////////////////
		if ($this->offerInput->ignoreSupplierOnDocuments) {
			$this->netCompany = 0 ;
			$this->total = - $this->discountValue - $this->discountVat + $this->serviceFeeValue + $this->serviceFeeVat;
		} else {
			$this->netCompany = $this->supplierValue + $this->supplierVat + $this->taxValue + $this->taxVat;
			$this->total = $this->netCompany + $this->commissionValue + $this->commissionVat - $this->discountValue - $this->discountVat + $this->serviceFeeValue + $this->serviceFeeVat;
		}

		// VALIDATE THAT ANY OF THE PRICES ARE NEGATIVE ///////////////////////////////////////////
		$msgs = new \App\MessagesCollector();
		$this->check($msgs);
		if ($msgs->hasErrorOrWarning()) {
			throw new \App\MessagesException($msgs);
		}

	}

	/**
	 * Checks the current offer output.
	 * @param \App\MessagesCollector $msgs
	 */
	private function check(\App\MessagesCollector $msgs)
	{

		// check that values are not negative
		$checkNegativeFields = [
			'supplierValue', 'supplierVat', 'commissionValue', 'commissionVat',
			'discountValue', 'discountVat', 'serviceFeeValue', 'serviceFeeVat',
			'taxValue', 'taxVat', 'netCompany', // 'total' is independently tested based on a config
		];
		foreach($checkNegativeFields as $fld) {
			if ( $this->$fld < -0.000001 ) {
				$msgs->addWarning("The offer component `{$fld}` is negative.");
			}
		}

		// check total
		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();
		if (!$CFG['offers']['allowNegativeTotals']) {
			if ( $this->total < -0.000001 ) {
				$msgs->addWarning("The total offer is negative.");
			}
		}

	}


}
