<?php

namespace App\AutoProcessRules;

class RefundInvoiceInsert_Execution extends ExecutionAbstract
{

	public function execute(\App\AutoProcessRules\Logs\Rule $ruleLogs)
	{

		// get the $stopOnFirstError flag
		$ruleEnt = \Entity\AutoProcessRule::select($this->idRule);
		$stopOnFirstError = $ruleEnt->stopOnFirstError;
		$ruleObj = $ruleEnt->object;
		$ruleFields = $ruleObj->getFields();

		// MAIN LOOP - go through each invoice and attach its services //////////////////////////////
		foreach($this->recordsList as $groupRecords) {

			$idTransaction = uniqid();

			try {

				// get the list of services
				$servicesList = [];
				foreach($groupRecords as $servDet) {
					$servicesList[] = $servDet['idService'];
				}

				$ruleLogs->addQuickLog('info', sprintf('Start inserting a new refund invoice with the following service(s): %s.', implode(', ', $servicesList)), $idTransaction );

				\DB::startTransaction();

				// START INSERTING AN INVOICE /////////////////////////////////////////////////////
				$invoiceInsertEnt = \Entity\Invoice::insert();

				// populate the order and associate the services
				$this->populateInvoice($ruleLogs, $invoiceInsertEnt, $servicesList, $idTransaction);

				$invoiceInsertEnt->save();

				// get invoice id
				$idInvoice = $invoiceInsertEnt->getId();


				// GROUP INVOICE ROWS /////////////////////////////////////////////////////////////
				if ($ruleFields['rowsGrouping']) {
					$invoiceUpdateEnt = \Entity\Invoice::update($idInvoice);
					$groupInvoiceRowsObj = new \App\Utils\Invoices\GroupInvoiceRows($invoiceUpdateEnt);
					if ($ruleFields['rowsGrouping']=='vatPercentAndVatQuota') {
						$groupInvoiceRowsObj->groupRowsByVatPercentAndQuota();
					}
					if ($ruleFields['rowsGrouping']=='serviceType') {
						$groupInvoiceRowsObj->groupRowsByServiceType();
					}
					if ($ruleFields['rowsGrouping']=='serviceId') {
						$groupInvoiceRowsObj->groupRowsByServiceId();
					}
					if ($ruleFields['rowsGrouping']=='serviceIdAndRowType') {
						$groupInvoiceRowsObj->groupRowsByServiceIdAndRowType();
					}
					$invoiceUpdateEnt->save();
				}


				// add msg
				if (!$ruleFields['rowsGrouping']) {
					$msg = 'Successfully added refund invoice #%s.';
				} else {
					$msg = 'Successfully added refund invoice #%s with grouped rows.';
				}
				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf($msg, $idInvoice))->setEntityDetails('Invoice', $idInvoice, 'insert')->setIdTransaction($idTransaction) );

				\DB::commitTransaction();

			} catch (\App\Entity\Exception $e) {

				\DB::rollbackTransaction();

				$ruleLogs->addFromMessagesCollector( $e->getMessagesCollector(), $idTransaction );

				// if "stopOnFirstError" - we need to stop this whole process
				if ($stopOnFirstError) {
					$ruleLogs->addQuickLog('warning', 'The process is set to stop after the first encountered problem.');
					break;
				}

			}

		}

	}


	/**
	 * Populates the invoice fields to be save() after that.
	 * @param \App\AutoProcessRules\Logs\Rule $ruleLogs
	 * @param \Entity\Invoice $invoiceEnt
	 */
	protected function populateInvoice(\App\AutoProcessRules\Logs\Rule $ruleLogs, \Entity\Invoice $invoiceEnt, Array $servicesList, $idTransaction)
	{

		$ruleEnt = \Entity\AutoProcessRule::select($this->idRule);
		$ruleObj = $ruleEnt->object;
		$ruleFields = $ruleObj->getFields();

		// attach services to be invoiced
		$servicesInvoiceOptions = [];
		foreach ($servicesList as $idService) {

			// prepare refund details
			$serviceEnt = \Entity\Service::selectForUpdate($idService);
			$impServiceEnt = $serviceEnt->relevantImportedService;
			$impServiceObj = $impServiceEnt->object;
			$impServRefundDetails = $impServiceObj->refundDetails;

			// populate the service invoice options
			$thisServiceInvoiceOptions = [
				'invoiceContent' =>			'refund',
				'idReservationService' =>	$idService,
				'refundDetails' =>			[
					'supplierTotal' =>		$impServRefundDetails->fareUsedSupplierValue,
					'supplierVat' =>		$impServRefundDetails->fareUsedSupplierVat,
					'supplierCurrency' =>	$impServRefundDetails->currency,
					'commissionTotal' =>	$impServRefundDetails->fareUsedCommissionValue,
					'commissionCurrency' =>	$impServRefundDetails->currency,
					'serviceFeeTotal' =>	$impServRefundDetails->serviceFeeValue,
					'serviceFeeCurrency' =>	$impServRefundDetails->currency,
					'taxTotal' =>			$impServRefundDetails->fareUsedTaxValue,
					'taxVat' =>				$impServRefundDetails->fareUsedTaxVat,
					'taxCurrency' =>		$impServRefundDetails->currency,
					'penaltyTotal' =>		$impServRefundDetails->penaltyValue,
					'penaltyCurrency' =>	$impServRefundDetails->currency,
					'returnServiceFee' =>	$impServRefundDetails->returnOriginalServiceFee,
				],
			];

			// overwrite the serviceFee (from offer profile) if nothing set from refund details
			if ($impServRefundDetails->serviceFeeValue===null) {

				// get fields for this offer
				$fields = new \App\OfferProfileRules\Fields();
				$fields->populateFromOfferEntitySelect($serviceEnt->chosenOffer);
				$fields->operation = 'refund';

				// get profile for these fields
				$getProfile = new \App\OfferProfileRules\GetProfile($fields);
				$getProfile->search();

				// the service fee values
				$serviceFeeDetails = $getProfile->getServiceFeeDetails();

				// only if service fee is "value" - apply the profile
				if ($serviceFeeDetails and $serviceFeeDetails->mode=='value') {
					$thisServiceInvoiceOptions['refundDetails']['serviceFeeTotal'] = $serviceFeeDetails->value;
					$thisServiceInvoiceOptions['refundDetails']['serviceFeeCurrency'] = $serviceFeeDetails->currency;
				}

			}

			// add this service invoice option
			$servicesInvoiceOptions[] = $thisServiceInvoiceOptions;
		}
		$invoiceEnt->servicesInvoiceOptions = $servicesInvoiceOptions;

		// get details for the first service
		$idFirstService = reset($servicesList);
		$firstServiceEnt = \Entity\Service::selectForUpdate($idFirstService);
		$orderEnt = $firstServiceEnt->order;
		$relevantInvoiceEnt = $firstServiceEnt->relevantInvoice;

		// decide invoice date
		if ($ruleFields['invoiceDateMode']=='servicesMinStartDate') { // min start date
			$invoiceDate = DATE_LIMIT_MAX;
			foreach ($servicesList as $idServ) {
				$servEnt = \Entity\Service::selectForUpdate($idServ);
				$serviceDate = $servEnt->chosenOffer->startDatetime->formatDateIso();
				if ($serviceDate<$invoiceDate) {
					$invoiceDate = $serviceDate;
				}
			}
		} elseif ($ruleFields['invoiceDateMode']=='servicesMaxEndDate') { // max end date (endDate may be missing, so we consider start date);
			$invoiceDate = DATE_LIMIT_MIN;
			foreach ($servicesList as $idServ) {
				$servEnt = \Entity\Service::selectForUpdate($idServ);
				if (!$servEnt->chosenOffer->endDatetime->isEmpty()) {
					$serviceDate = $servEnt->chosenOffer->endDatetime->formatDateIso();
				} else {
					$serviceDate = $servEnt->chosenOffer->startDatetime->formatDateIso();
				}
				if ($serviceDate>$invoiceDate) {
					$invoiceDate = $serviceDate;
				}
			}
		} else { // currentDate or nothing set
			$invoiceDate = TODAY_ISO;
		}
		$invoiceEnt->invoiceDate = $invoiceDate;

		// add invoice fields
		$invoiceEnt->idReservation =	$orderEnt->getId();
		$invoiceEnt->idAutoProcessRule = $this->idRule;
		$invoiceEnt->currency =			$relevantInvoiceEnt->currency;
		$invoiceEnt->formOfPayment =	$ruleFields['formOfPayment'];
		$invoiceEnt->temporary =		(Bool)$ruleFields['temporaryInvoice'];
		$invoiceEnt->roundTotal =		(Bool)$ruleFields['roundTotal'];
		$invoiceEnt->remarks = 			trim($ruleFields['remarks'] . "\n" . $invoiceEnt->get_invoiceRemarksFromServices());

		// responsible user/office
		if ($ruleFields['invoiceResponsibleUserType']=='order') {
			$userEnt = $orderEnt->user;
		} elseif ($ruleFields['invoiceResponsibleUserType']=='refundDetails') {
			// get from refund user in IO object
			$idRefundUser = 0;
			$impServoceObj = $firstServiceEnt->importedService->object;
			if ($impServoceObj and $impServoceObj->refundDetails) {
				$issueUserObj = $impServoceObj->refundDetails->issueUser;
				if ($issueUserObj) {
					$idRefundUser = $issueUserObj->getMatchedId($firstServiceEnt->idExternalSystem);
				}
			}
			// fallback to manual user if the refund user is not found
			$userEnt = \Entity\User::select($idRefundUser?:$ruleFields['invoiceIdResponsibleUser']);
		} else { // 'manual' user
			$userEnt = \Entity\User::select($ruleFields['invoiceIdResponsibleUser']);
		}

		$invoiceEnt->idUser = $userEnt->getId();
		$invoiceEnt->idOffice = $userEnt->idOffice;

		// set invoice client
		if ($relevantInvoiceEnt->idCustomer) {
			$invoiceEnt->idCustomer = $relevantInvoiceEnt->idCustomer;
		} else {
			$invoiceEnt->idCorporate = $relevantInvoiceEnt->idCorporate;
			$invoiceEnt->idCorporateOffice = $relevantInvoiceEnt->idCorporateOffice;
		}

		// rows description scheme
		if ($ruleFields['invoiceRowsDescriptionScheme']) {
			$invoiceEnt->rowDescriptionScheme = $ruleFields['invoiceRowsDescriptionScheme'];
		} elseif ($relevantInvoiceEnt->idCorporate) {
			$profileDescriptionScheme = (String)\App\EntityPreference\Corporate::getInstance($relevantInvoiceEnt->idCorporate)->invoiceRowsScheme;
			if ($profileDescriptionScheme) {
				$invoiceEnt->rowDescriptionScheme = $profileDescriptionScheme;
			}
		}

		// overwrite currency type
		$invoiceRowsCurrencyType = '';
		if ($relevantInvoiceEnt->idCorporate) {
			$invoiceRowsCurrencyType = \App\EntityPreference\Corporate::getInstance($relevantInvoiceEnt->idCorporate)->invoiceCurrencyType;
		}
		if (!$invoiceRowsCurrencyType) {
			$invoiceRowsCurrencyType = $ruleFields['invoiceRowsCurrencyType'];
		}
		if ($invoiceRowsCurrencyType) {
			$invRows = $invoiceEnt->createInvoiceRows();
			$rowsCurrTypes = [];
			foreach($invRows as $rowIdx=>$rowDet) {
				$rowsCurrTypes[$rowIdx] = $invoiceRowsCurrencyType;
			}
			$invoiceEnt->invoiceRowCurrencyTypes = $rowsCurrTypes;
		}

		// get info from profiles/defaults
		$invoiceEnt->fulfillmentDate = $invoiceEnt->get_defaultFulfillmentDate();
		// due date - from dueDays or profile
		if (strlen($ruleFields['dueDays'])) {
			$dueDays = round($ruleFields['dueDays']);
			$invoiceEnt->dueDate = \Engine\DateTime::create()->add(new \DateInterval("P{$dueDays}D"))->formatDateIso();
		} else {
			$invoiceEnt->dueDate = $invoiceEnt->get_defaultDueDate();
		}

		// set the agency bank account - corporate profile has priority
		$idBankAccount = 0;
		if ($orderEnt->idMiceProject) { // try MICE project first
			$idBankAccount = \App\EntityPreference\MiceProject::getInstance($orderEnt->idMiceProject)->idAgencyBankAccount;
		}
		if (!$idBankAccount and $relevantInvoiceEnt->idCorporate) { // then try corporate profile
			$idBankAccount = \App\EntityPreference\Corporate::getInstance($relevantInvoiceEnt->idCorporate)->agencyBankAccount;
		}
		if (!$idBankAccount) { // then the manual field
			$idBankAccount = $ruleFields['idAgencyBankAccount'];
		}
		if ($idBankAccount) {
			$invoiceEnt->idBankAccount =	$idBankAccount;
		}

		// set the invoice series
		$idSeries = 0;
		if ($relevantInvoiceEnt->idCorporate) {
			$idSeries = \App\EntityPreference\Corporate::getInstance($relevantInvoiceEnt->idCorporate)->invoiceSeries;
		}
		if (!$idSeries and $ruleFields['idInvoiceSeries']) {
			$idSeries = $ruleFields['idInvoiceSeries'];
		}
		$invoiceEnt->idInvoiceSeries = $idSeries;

	}

}
