<?php

namespace App\OfferCalculator\Populate;

class InputFromEntity
{

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

	/**
	 * The offer entity object - can have operation 'select', 'insert' or 'update'.
	 * @var \Entity\Offer
	 */
	protected $offerEnt;

	/**
	 * The offer item entity object - can have operation 'select', 'insert' or 'update'.
	 * @var \Entity\OfferItem
	 */
	protected $offerItemEnt;


	/**
	 * The class constructor - assigns the offer input object.
	 * @param \App\OfferCalculator\Input $offerInput
	 */
	public function __construct(\App\OfferCalculator\Input $offerInput)
	{
		$this->offerInput = $offerInput;
	}

	/**
	 * Associates the offer and the offer item.
	 * @param \Entity\Offer $offerEnt
	 * @param \Entity\Offer $offerItemEnt
	 */
	public function setOfferEntity(\Entity\Offer $offerEnt, \Entity\OfferItem $offerItemEnt)
	{
		$this->offerEnt = $offerEnt;
		$this->offerItemEnt = $offerItemEnt;
	}

	/**
	 * Checks if the objects are ok.
	 */
	private function check()
	{
		// check the offer operations
		if ( !in_array($this->offerEnt->getOperation(), ['insert', 'update', 'select']) ) {
			throw new \Exception('The operation for offer entity should be only insert/update/select.');
		}
		// for inserting offer, we need to have inserting offer item
		if ( $this->offerEnt->getOperation()=='insert' and $this->offerItemEnt->getOperation()!='insert' ) {
			throw new \Exception('For `insert`-ing offer entity, the offer item operation should also be `insert`.');
		}
		// for selecting offer, we need to have selecting offer item
		if ( $this->offerEnt->getOperation()=='select' and $this->offerItemEnt->getOperation()!='select' ) {
			throw new \Exception('For `select`-ing offer entity, the offer item operation should also be `select`.');
		}
		// for offer update, the offer item can be both insert or select (this mustn't be checked)
	}

	/**
	 * Populates the offer input.
	 */
	public function populate()
	{

		// CHECKS THE ENTITY OBJECTS --------------------------------------------------------------
		$this->check();

		// POPULATES THE OFFER INPUT DATA ON "SELECT" ---------------------------------------------
		if ($this->offerEnt->getOperation()=='select') {
			$this->populate_select();
		}

		// POPULATES THE OFFER INPUT DATA ON "INSERT" ---------------------------------------------
		if ($this->offerEnt->getOperation()=='insert') {
			$this->populate_insert();
		}

		// POPULATES THE OFFER INPUT DATA ON "UPDATE" ---------------------------------------------
		if ($this->offerEnt->getOperation()=='update') {
			$this->populate_update();
		}

	}

	private function populate_select()
	{

		throw new \Exception('not done');

		$this->offerInput->quantity = $this->offerItemEnt->quantity;

	}


	/**
	 * Returns an associative array of fields which are about to be applied onto the offer input object.
	 * The keys represent the offer input public attributes and the values represent a set of keys:
	 * source and field; 'source' indicates where the info is stored (offer/offerItem) and the 'field'
	 * indicates the name of the setter attribute on the corresponding entity object instance (for
	 * insert/update operations). For the 'update' operation, the fields may be set onto the new
	 * object but it may also be missing - in this case, the original value is selected.
	 * @return Array
	 */
	private function getFieldsConfigs()
	{

		$applyFields = [];

		$applyFields['quantity'] =						['source' => 'offerItem',		'field' => 'quantity'];

		$applyFields['servicePriceValue'] =				['source' => 'offerItem',	'field' => 'supplierInitialValue', 'valueDependsOn'=>'supplierInitialCurrency'];
		$applyFields['servicePriceVat'] =				['source' => 'offerItem',	'field' => 'supplierInitialVat', 'valueDependsOn'=>'supplierInitialCurrency'];
		$applyFields['servicePriceCurrency'] =			['source' => 'offerItem',	'field' => 'supplierInitialCurrency'];

		$applyFields['commissionable'] =				['source' => 'offer',		'field' => 'commissionable'];
		$applyFields['commissionPercent'] =				['source' => 'offerItem',	'field' => 'commissionPercent'];
		$applyFields['commissionValue'] =				['source' => 'offerItem',	'field' => 'commissionInitialValue', 'valueDependsOn'=>'commissionInitialCurrency'];
		$applyFields['commissionCurrency'] =			['source' => 'offerItem',	'field' => 'commissionInitialCurrency'];

		$applyFields['discountPercentSource'] =			['source' => 'offer',		'field' => 'discountPercentSource'];
		$applyFields['discountPercent'] =				['source' => 'offerItem',	'field' => 'discountPercent'];
		$applyFields['discountValue'] =					['source' => 'offerItem',	'field' => 'discountInitialValue', 'valueDependsOn'=>'discountInitialCurrency'];
		$applyFields['discountCurrency'] =				['source' => 'offerItem',	'field' => 'discountInitialCurrency'];

		$applyFields['serviceFeeMode'] =				['source' => 'offer',		'field' => 'serviceFeeMode'];
		$applyFields['serviceFeePercentSource'] =		['source' => 'offer',		'field' => 'serviceFeePercentSource'];
		$applyFields['serviceFeePercent'] =				['source' => 'offerItem',	'field' => 'serviceFeePercent'];
		$applyFields['serviceFeeValue'] =				['source' => 'offerItem',	'field' => 'serviceFeeInitialValue', 'valueDependsOn'=>'serviceFeeInitialCurrency'];
		$applyFields['serviceFeeCurrency'] =			['source' => 'offerItem',	'field' => 'serviceFeeInitialCurrency'];

		$applyFields['taxValue'] =						['source' => 'offerItem',	'field' => 'taxInitialValue', 'valueDependsOn'=>'taxInitialCurrency'];
		$applyFields['taxVat'] =						['source' => 'offerItem',	'field' => 'taxInitialVat', 'valueDependsOn'=>'taxInitialCurrency'];
		$applyFields['taxCurrency'] =					['source' => 'offerItem',	'field' => 'taxInitialCurrency'];

		$applyFields['servicePriceVatIncluded'] =		['source' => 'offer',		'field' => 'supplierVatIncluded'];
		$applyFields['commissionVatIncluded'] =			['source' => 'offer',		'field' => 'commissionVatIncluded'];
		$applyFields['discountVatIncluded'] =			['source' => 'offer',		'field' => 'discountVatIncluded'];
		$applyFields['serviceFeeVatIncluded'] =			['source' => 'offer',		'field' => 'serviceFeeVatIncluded'];
		$applyFields['taxVatIncluded'] =				['source' => 'offer',		'field' => 'taxVatIncluded'];

		$applyFields['ignoreSupplierOnDocuments'] =		['source' => 'offer',		'field' => 'ignoreSupplierOnDocuments'];
		$applyFields['serviceCode'] =					['source' => 'offer',		'field' => 'serviceCode'];
		$applyFields['currencyDate'] =					['source' => 'offer',		'field' => 'currencyDate'];
		$applyFields['totalCurrency'] =					['source' => 'offer',		'field' => 'totalCurrency'];

		return $applyFields;

	}

	/**
	 * Populates the offer input with data from inserting offer/offerItem. It uses getFieldsConfigs()
	 * to get the list of fieds to be copied from the entities to the offer input; it only sets the
	 * fields that were set in the entities.
	 * @throws \Exception
	 */
	private function populate_insert()
	{

		$offerFlds =		$this->offerEnt->getFields();
		$offerItemFlds =	$this->offerItemEnt->getFields();

		// gets the list of fields
		$applyFields = $this->getFieldsConfigs();

		// fill in the attributes
		foreach($applyFields as $inputFld => $sourceFld) {

			// skips fields that have valueDependsOn (usually values without currencies, but the value must be 0, null, blank)
			if ($sourceFld['valueDependsOn'] and !$this->offerInput->$inputFld and !$offerItemFlds[$sourceFld['valueDependsOn']]) {
				continue;
			}

			// copy field (for offer/item entity)
			if ($sourceFld['source']=='offer') {
				if (isset($offerFlds[$sourceFld['field']])) {
					$this->offerInput->$inputFld = $offerFlds[$sourceFld['field']];
				}
			} elseif ($sourceFld['source']=='offerItem') {
				if (isset($offerItemFlds[$sourceFld['field']])) {
					$this->offerInput->$inputFld = $offerItemFlds[$sourceFld['field']];
				}
			} else {
				throw new \Exception("Invalid config `source` `{$sourceFld['source']}` for field `{$inputFld}`.");
			}
		}

		// currency type
		if (isset($offerFlds['currencyType'])) {
			$this->offerInput->currencyType = $offerFlds['currencyType'];
		}
		// add extra fields that are not simply copied
		if ($offerFlds['itinerary'] and $offerFlds['itinerary']->getSegmentsCount()) {
			$this->offerInput->segmentsCount = $offerFlds['itinerary']->getSegmentsCount();
		}
		// offer duration
		$this->offerInput->duration = 1; // reset to 1 for now; then attempt to load it from the offer entity and based on service type configs
		if ($this->offerEnt->getFinalServiceCode()) {
			$offerDet = \App\Config\Services_OfferDetails::getInstance()->get($this->offerEnt->getFinalServiceCode());
			if ($offerDet['quantityType']=='duration')	{
				$this->offerInput->duration = $this->offerEnt->getOfferDuration() ? : 1;
			}
		}

		if ($offerFlds['serviceCode'] and $offerFlds['location'] and is_numeric($offerFlds['calculationType'])) {
			$this->offerInput->loadVatPercentFromServiceCalculation($offerFlds['serviceCode'], $offerFlds['location'], $offerFlds['calculationType']);
		}
		// force ignoreSupplierOnDoc (null is very seldom used; usually it is false)
		if ( $this->offerInput->ignoreSupplierOnDocuments === null ) {
			$this->offerInput->ignoreSupplierOnDocuments = false;
		}

	}

	/**
	 * Populates the offer input with data from updating offer/offerItem. It uses getFieldsConfigs()
	 * to get the list of fieds to be copied from the entities to the offer input; it only sets the
	 * fields that were set in the entities, for the rest of them it gets the fields from the entity (from db);
	 * IMPORTANT: the fields must have the same name for both update / select operation.
	 * @throws \Exception
	 */
	private function populate_update()
	{

		// get offer/offerItem update/select objects
		$offerUpdFlds = $this->offerEnt->getFields();
		$offerSelEnt = \Entity\Offer::selectForUpdate($this->offerEnt->getId());

		if ($this->offerItemEnt->getOperation()=='insert') {
			$offerItemInsFlds = $this->offerItemEnt->getFields();
			$offerItemSelEnt = null;
		} else {
			$offerItemSelEnt = \Entity\OfferItem::selectForUpdate($this->offerItemEnt->getId());
			$offerItemInsFlds = null;
		}

		// gets the list of fields
		$applyFields = $this->getFieldsConfigs();

		// fill in the attributes
		foreach($applyFields as $inputFld => $sourceFld) {

			// skips fields that have valueDependsOn (usually values without currencies, but the value must be 0, null, blank)
 			if ($sourceFld['valueDependsOn'] and !$this->offerInput->$inputFld) {
 				if ($offerItemSelEnt and !$offerItemSelEnt->{$sourceFld['valueDependsOn']}) {
 					continue;
 				}
 				if ($offerItemInsFlds and !$offerItemInsFlds[$sourceFld['valueDependsOn']]) {
 					continue;
 				}

 			}

			// copy field (for offer/item entity) (for offer components we can set only partial info)
			if ($sourceFld['source']=='offer') {
				if (isset($offerUpdFlds[$sourceFld['field']])) {
					$this->offerInput->$inputFld = $offerUpdFlds[$sourceFld['field']];
				} else {
					$this->offerInput->$inputFld = $offerSelEnt->{$sourceFld['field']};
				}
			} elseif ($sourceFld['source']=='offerItem') {
				// for offer item all info must be set onto the offer item object
				if (isset($offerItemInsFlds[$sourceFld['field']])) {
					$this->offerInput->$inputFld = $offerItemInsFlds[$sourceFld['field']];
				} else {
					$this->offerInput->$inputFld = $offerItemSelEnt->{$sourceFld['field']}??null;
				}
			} else {
				throw new \Exception("Invalid config `source` `{$sourceFld['source']}` for field `{$inputFld}`.");
			}
		}

		// currency type
		if (isset($offerUpdFlds['currencyType'])) {
			$this->offerInput->currencyType = $offerUpdFlds['currencyType'];
		} else {
			$this->offerInput->currencyType = $offerSelEnt->currencyTypeCode;
		}
		// add extra fields that are not simply copied
		if ($offerUpdFlds['itinerary']) {
			if ($offerUpdFlds['itinerary']->getSegmentsCount()) {
				$this->offerInput->segmentsCount = $offerUpdFlds['itinerary']->getSegmentsCount();
			}
		} else {
			if ($offerSelEnt->itinerary and $offerSelEnt->itinerary->getSegmentsCount()) {
				$this->offerInput->segmentsCount = $offerSelEnt->itinerary->getSegmentsCount();
			} else {
				$this->offerInput->segmentsCount = 1;
			}
		}

		// offer duration
		$this->offerInput->duration = 1; // reset to 1 for now; then attempt to load it from the offer entity and based on service type configs
		$offerDet = \App\Config\Services_OfferDetails::getInstance()->get($this->offerEnt->getFinalServiceCode()); // getFinalServiceCode() always returns a code
		if ($offerDet['quantityType']=='duration')	{
			$this->offerInput->duration = $this->offerEnt->getOfferDuration() ? : 1;
		}

		// auto-load the VAT percents
		$serviceCode =	$offerUpdFlds['serviceCode']?$offerUpdFlds['serviceCode']:$offerSelEnt->serviceCode;
		$location = 	$offerUpdFlds['location']?$offerUpdFlds['location']:$offerSelEnt->locationCode;
		$calcType =		(is_numeric($offerUpdFlds['calculationType']))?$offerUpdFlds['calculationType']:$offerSelEnt->calculationType;
		$this->offerInput->loadVatPercentFromServiceCalculation($serviceCode, $location, $calcType);

	}

}
