<?php
namespace App\Utils\BaseParser\Structure\StructureParser\Structures\Base;

/**
 * Price parser structure, specially designed for distributed refund transactions
 * @author Lucian Sirbu
 */
abstract class PriceRefundDistributed extends \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price
{
	protected $cachedResponse = null;

	/**
	 * Create a tax compoenent
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent
	 */
	abstract protected function createPriceTaxComponentObject($code=null, $amount=null, $currency=null, $VAT=null);

	/**
	 * Create a price object
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\Price
	 */
	abstract protected function createPriceObject();

	/**
	 * Overrides the base function and implementes an auto-caching mechanism(calling the function twice will only lead to real data-extraction once)
	 * (non-PHPdoc)
	 * @see \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Base::getData()
	 */
	public function getData()
	{
		if ($this->cachedResponse===null) {
			$this->cachedResponse = $this->_getData();
		}
		return $this->cachedResponse;
	}

	protected function getTransactionDetails()
	{
		return $this->transaction->getTransactionDetails();
	}

	/**
	 * Redefine the parent transaction object
	 * @var \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\PriceRefundDistributed
	 */
	protected $transaction = null;

	protected function _getData()
	{
		return $this->_getData_withExplicitRefundCommission();
	}

	protected function _getData_withExplicitRefundCommission()
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());
		list(, , $transactionIndexes, ) = $this->getTransactionDetails();
		list($totalPrice, $remainingPrice) = $this->transaction->getAmounts();

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
			'$transactionIndexes'=> $transactionIndexes,
		]);

		/* @var $totalPrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		/* @var $remainingPrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		$referenceService = $this->transaction->getReferenceService();
		$referencePrice =  $referenceService->transactionData->price;

		/* @var $referencePrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		if (!$referencePrice) {
			$referencePrice = $this->createPriceObject();
		}

		// $referencePrice is positive, we need to floatInvert() it everythere we use it
		// $remainingPrice is negative already, there is no need to floatInvert() it everythere we use it

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
			'$referenceService'=> sprintf("%s/%s/%s", $referenceService->serviceCode, $referenceService->transactionStatus, $referenceService->getId()),
			'$referencePrice'=> [
				'total' => 			sprintf("%.2f %s", $referencePrice->total, $referencePrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $referencePrice->supplier, $referencePrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $referencePrice->tax->amount, $referencePrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $referencePrice->serviceFee, $referencePrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $referencePrice->commission, $referencePrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $referencePrice->discount, $referencePrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $referencePrice->penalty, $referencePrice->penaltyCurrency),
			],
			'$remainingPrice'=> [
				'total' => 			sprintf("%.2f %s", $remainingPrice->total, $remainingPrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $remainingPrice->supplier, $remainingPrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $remainingPrice->tax->amount, $remainingPrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $remainingPrice->serviceFee, $remainingPrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $remainingPrice->commission, $remainingPrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $remainingPrice->discount, $remainingPrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $remainingPrice->penalty, $remainingPrice->penaltyCurrency),
			],
		]);

		$item = $this->createPriceObject();

		// penalties should be handled didfferently than other components. They should be added entirely on the first found child, and ignore any subsequent childs
		if ($totalPrice->penalty && $this->transaction->getServiceIndex()===0) {
			$item->penalty = 			$totalPrice->penalty;
			$item->penaltyVAT = 		$totalPrice->penaltyVAT;
			$item->penaltyCurrency = 	$totalPrice->penaltyCurrency;

			$remainingPrice->total-= $remainingPrice->penalty;
			$remainingPrice->total-= $remainingPrice->penaltyVAT;

			$remainingPrice->penalty = null;
			$remainingPrice->penaltyVAT = null;
			$remainingPrice->penaltyCurrency = null;

			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("Put all the refund penalties on the first referenced service");
			$this->transaction->messages->addInfo(sprintf(__METHOD__.": Put all the refund penalties on the first referenced service"));
		}

		/**
		 * we have to distribuite in total: $totalPrice->total
		 * the rest of values to distribuite: $remainingPrice->total
		 * we had distribuited a total of: $totalPrice->total - $remainingPrice->total
		 */
		$item->supplierCurrency = 	$remainingPrice->supplierCurrency;
		if ($this->floatIsNegative($remainingPrice->supplier + $referencePrice->supplier)) {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("supplier: referencePrice<remaining. Use the reference price");
			$item->supplier = 		$this->floatInvert($referencePrice->supplier);
			$item->supplierVAT = 	$this->floatInvert($referencePrice->supplierVAT);
		}
		else {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("supplier: referencePrice>remaining. Use the remaining price");
			$item->supplier = 		$remainingPrice->supplier;
			$item->supplierVAT = 	$remainingPrice->supplierVAT;
		}
		$remainingPrice->supplier-= 	$item->supplier;
		$remainingPrice->supplierVAT-= 	$item->supplierVAT;
		$remainingPrice->total-= 		$item->supplier;
		$remainingPrice->totalVAT-= 	$item->supplierVAT;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'total' => sprintf("%.2f %s", $item->total, $item->totalCurrency),
			'supplier' => sprintf("%.2f %s", $item->supplier, $item->supplierCurrency),
		]);


		$item->commissionCurrency = 	$remainingPrice->commissionCurrency;
		if ($this->floatIsNegative($remainingPrice->commission + $referencePrice->commission)) {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("commission: referencePrice<remaining. Use the reference price");
			$item->commission = 	$this->floatInvert($referencePrice->commission);
			$item->commissionVAT = 	$this->floatInvert($referencePrice->commissionVAT);
		}
		else {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("commission: referencePrice>remaining. Use the remaining price");
			$item->commission = 	$remainingPrice->commission;
			$item->commissionVAT = 	$remainingPrice->commissionVAT;
		}
		$remainingPrice->commission-= 		$item->commission;
		$remainingPrice->commissionVAT-= 	$item->commissionVAT;
		$remainingPrice->total-= 			$item->commission;
		$remainingPrice->totalVAT-= 		$item->commissionVAT;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'total' => sprintf("%.2f %s", $item->total, $item->totalCurrency),
			'commission' => sprintf("%.2f %s", $item->commission, $item->commissionCurrency),
		]);

		if ($remainingPrice->tax && $referencePrice->tax) {
			$item->tax->setCurrency($remainingPrice->tax->getCurrency());

			if ($this->floatIsNegative($remainingPrice->tax->getAmount() + $referencePrice->tax->getAmount() + $remainingPrice->tax->getVAT() + $referencePrice->tax->getVAT())) {
				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("tax: referencePrice<remaining. Use the reference price");

				foreach ($referencePrice->tax->components as $comp) {
					/* @var $comp \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent */
					$item->tax->append($this->createPriceTaxComponentObject(
						$comp->code,
						$this->floatInvert($comp->amount),
						$comp->currency,
						$this->floatInvert($comp->VAT)
					));

					$remainingPrice->total-= 	$this->floatInvert($comp->amount);
					$remainingPrice->totalVAT-= $this->floatInvert($comp->VAT);
					$remainingPrice->tax->add($comp);
				}
			}
			else {
				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("tax: referencePrice>remaining. Use the remaining price");

				foreach ($remainingPrice->tax->components as $comp) {
					// $item->tax->append($comp);
					$item->tax->append($this->createPriceTaxComponentObject(
						$comp->code,
						$comp->amount,
						$comp->currency,
						$comp->VAT
					));

					$remainingPrice->total-= 	$comp->amount;
					$remainingPrice->totalVAT-= $comp->VAT;
				}

				$remainingPrice->tax->setAmount(null)->setVAT(null)->setCurrency(null)->removeAllComponents();
			}
		}

		$item->total = 			$item->supplier + $item->tax->getAmount() + $item->commission + $item->penalty;
		$item->totalVAT = 		$item->supplierVAT + $item->tax->getVAT() + $item->commissionVAT + $item->penaltyVAT;
		$item->totalCurrency = 	$item->supplierCurrency;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'total' => sprintf("%.2f %s", $item->total, $item->totalCurrency),
			'tax' => sprintf("%.2f %s", $item->tax->amount, $item->tax->currency),
		]);

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'$remainingPrice'=> [
				'total' => 			sprintf("%.2f %s", $remainingPrice->total, $remainingPrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $remainingPrice->supplier, $remainingPrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $remainingPrice->tax->amount, $remainingPrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $remainingPrice->serviceFee, $remainingPrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $remainingPrice->commission, $remainingPrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $remainingPrice->discount, $remainingPrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $remainingPrice->penalty, $remainingPrice->penaltyCurrency),
			],
		]);

		$this->transaction->messages->addInfo(sprintf(__METHOD__.": Distribuited %.2f %s over %s (#%d of %d)). Remaining: %.2f %s from %.2f %s",
			$item->total, $item->totalCurrency, $referenceService->docNumber,
			$this->transaction->getServiceIndex()+1, $this->transaction->getServicesCount(),
			$remainingPrice->total, $remainingPrice->totalCurrency,
			$totalPrice->total, $totalPrice->totalCurrency
		));

		if ($remainingPrice->total || $remainingPrice->totalVAT) {
			$item->remainingPriceAfterRefundDistribution = $remainingPrice;
		}

		// echo $item->componentsDebug();	// DEBUG: enable this
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
		return $item;
	}

	protected function _getData_withoutExplicitRefundCommission()
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());
		list(, , $transactionIndexes, ) = $this->getTransactionDetails();
		list($totalPrice, $remainingPrice) = $this->transaction->getAmounts();

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
			'$transactionIndexes'=> $transactionIndexes,
		]);

		/* @var $totalPrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		/* @var $remainingPrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		$referenceService = $this->transaction->getReferenceService();
		$referencePrice =  $referenceService->transactionData->price;

		/* @var $referencePrice \App\Utils\BaseParser\Structure\StructureParser\Structures\Base\Price */
		if (!$referencePrice) {
			$referencePrice = $this->createPriceObject();
		}

		// $referencePrice is positive, we need to floatInvert() it everythere we use it
		// $remainingPrice is negative already, there is no need to floatInvert() it everythere we use it

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
			'$referenceService'=> sprintf("%s/%s/%s", $referenceService->serviceCode, $referenceService->transactionStatus, $referenceService->getId()),
			'$referencePrice'=> [
				'total' => 			sprintf("%.2f %s", $referencePrice->total, $referencePrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $referencePrice->supplier, $referencePrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $referencePrice->tax->amount, $referencePrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $referencePrice->serviceFee, $referencePrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $referencePrice->commission, $referencePrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $referencePrice->discount, $referencePrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $referencePrice->penalty, $referencePrice->penaltyCurrency),
			],
			'$remainingPrice'=> [
				'total' => 			sprintf("%.2f %s", $remainingPrice->total, $remainingPrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $remainingPrice->supplier, $remainingPrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $remainingPrice->tax->amount, $remainingPrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $remainingPrice->serviceFee, $remainingPrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $remainingPrice->commission, $remainingPrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $remainingPrice->discount, $remainingPrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $remainingPrice->penalty, $remainingPrice->penaltyCurrency),
			],
		]);

		$item = $this->createPriceObject();

		// penalties should be handled didfferently than other components. They should be added entirely on the first found child, and ignore any subsequent childs
		if ($totalPrice->penalty && $this->transaction->getServiceIndex()===0) {
			$item->penalty = 			$totalPrice->penalty;
			$item->penaltyVAT = 		$totalPrice->penaltyVAT;
			$item->penaltyCurrency = 	$totalPrice->penaltyCurrency;

			$remainingPrice->total-= $remainingPrice->penalty;
			$remainingPrice->total-= $remainingPrice->penaltyVAT;

			$remainingPrice->penalty = null;
			$remainingPrice->penaltyVAT = null;
			$remainingPrice->penaltyCurrency = null;

			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("Put all the refund penalties on the first referenced service");
			$this->transaction->messages->addInfo(sprintf(__METHOD__.": Put all the refund penalties on the first referenced service"));
		}

		/**
		 * NOTE:
		 * 	- we have to distribuite in total: $totalPrice->total
		 * 	- the rest of values to distribuite: $remainingPrice->total
		 * 	- we had distribuited a total of: $totalPrice->total - $remainingPrice->total
		 */
		$item->supplierCurrency = 	$remainingPrice->supplierCurrency;

		// even if theoretically the commission is 0/null at this point, we need to take it into account as it might not be null, and bbe set only for the TICK operation, as it dopne for Sabre/TD-8794
		$remainingSupplierPriceToDistribuite = 0
			+ $remainingPrice->supplier + $remainingPrice->supplierVAT + $remainingPrice->commission + $remainingPrice->commissionVAT
			+ $referencePrice->supplier + $referencePrice->supplierVAT + $referencePrice->commission + $referencePrice->commissionVAT;

		if ($this->floatIsNegative($remainingSupplierPriceToDistribuite)) {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("supplier: referencePrice<remaining. Use the reference price");
			$item->supplier = 		$this->floatInvert($referencePrice->supplier) +    $this->floatInvert($referencePrice->commission);
			$item->supplierVAT = 	$this->floatInvert($referencePrice->supplierVAT) + $this->floatInvert($referencePrice->commissionVAT);
		}
		else {
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("supplier: referencePrice>remaining. Use the remaining price");
			$item->supplier = 		$remainingPrice->supplier +    $remainingPrice->commission;
			$item->supplierVAT = 	$remainingPrice->supplierVAT + $remainingPrice->commissionVAT;
		}
		$remainingPrice->supplier-= 	$item->supplier;
		$remainingPrice->supplierVAT-= 	$item->supplierVAT;

		$remainingPrice->commission = 		0;
		$remainingPrice->commissionVAT = 	0;

		$remainingPrice->total-= 		$item->supplier;
		$remainingPrice->totalVAT-= 	$item->supplierVAT;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'total' =>    sprintf("%.2f %s", $item->total, $item->totalCurrency),
			'supplier' => sprintf("%.2f %s", $item->supplier, $item->supplierCurrency),
		]);

		if ($remainingPrice->tax && $referencePrice->tax) {
			$item->tax->setCurrency($remainingPrice->tax->getCurrency());

			if ($this->floatIsNegative($remainingPrice->tax->getAmount() + $referencePrice->tax->getAmount())) {
				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("tax: referencePrice<remaining. Use the reference price");

				foreach ($referencePrice->tax->components as $comp) {
					/* @var $comp \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent */
					$item->tax->append($this->createPriceTaxComponentObject(
						$comp->code,
						$this->floatInvert($comp->amount),
						$comp->currency,
						$this->floatInvert($comp->VAT)
					));

					$remainingPrice->total-= 	$this->floatInvert($comp->amount);
					$remainingPrice->totalVAT-= $this->floatInvert($comp->VAT);
					$remainingPrice->tax->add($comp);
				}
			}
			else {
				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logStep("tax: referencePrice>remaining. Use the remaining price");

				foreach ($remainingPrice->tax->components as $comp) {
					// $item->tax->append($comp);
					$item->tax->append($this->createPriceTaxComponentObject(
						$comp->code,
						$comp->amount,
						$comp->currency,
						$comp->VAT
					));

					$remainingPrice->total-= 	$comp->amount;
					$remainingPrice->totalVAT-= $comp->VAT;
				}

				$remainingPrice->tax->setAmount(null)->setVAT(null)->setCurrency(null)->removeAllComponents();
			}
		}

		$item->total = 			$item->supplier + $item->tax->getAmount() + $item->commission + $item->penalty;
		$item->totalVAT = 		$item->supplierVAT + $item->tax->getVAT() + $item->commissionVAT + $item->penaltyVAT;
		$item->totalCurrency = 	$item->supplierCurrency;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'total' => sprintf("%.2f %s", $item->total, $item->totalCurrency),
			'tax' => sprintf("%.2f %s", $item->tax->amount, $item->tax->currency),
		]);

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
			'$remainingPrice'=> [
				'total' => 			sprintf("%.2f %s", $remainingPrice->total, $remainingPrice->totalCurrency),
				'supplier' => 		sprintf("%.2f %s", $remainingPrice->supplier, $remainingPrice->supplierCurrency),
				'tax' => 			sprintf("%.2f %s", $remainingPrice->tax->amount, $remainingPrice->tax->currency),
				'serviceFee' => 	sprintf("%.2f %s", $remainingPrice->serviceFee, $remainingPrice->serviceFeeCurrency),
				'commission' => 	sprintf("%.2f %s", $remainingPrice->commission, $remainingPrice->commissionCurrency),
				'discount' => 		sprintf("%.2f %s", $remainingPrice->discount, $remainingPrice->discountCurrency),
				'penalty' => 		sprintf("%.2f %s", $remainingPrice->penalty, $remainingPrice->penaltyCurrency),
			],
		]);

		$this->transaction->messages->addInfo(sprintf(__METHOD__.": Distribuited %.2f %s over %s (#%d of %d)). Remaining: %.2f %s from %.2f %s",
			$item->total, $item->totalCurrency, $referenceService->docNumber,
			$this->transaction->getServiceIndex()+1, $this->transaction->getServicesCount(),
			$remainingPrice->total, $remainingPrice->totalCurrency,
			$totalPrice->total, $totalPrice->totalCurrency
		));

		if ($remainingPrice->total) {
			$item->remainingPriceAfterRefundDistribution = $remainingPrice;
		}

		// echo $item->componentsDebug();	// DEBUG: enable this
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
		return $item;
	}

}
