<?php

namespace App\AutoProcessRules;

class ImportedServiceCancel_Execution extends ExecutionAbstract
{

	/**
	 * (non-PHPdoc)
	 * @see \App\AutoProcessRules\ExecutionAbstract::execute()
	 */
	public function execute(\App\AutoProcessRules\Logs\Rule $ruleLogs)
	{

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

		// MAIN LOOP - go through each invoice and issue it ///////////////////////////////////////
		foreach($this->recordsList as $importedServiceDetails) {

			$idTransaction = uniqid();

			$idService = $importedServiceDetails['idServiceTina'];

			try {

				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('Start cancelling the TINA service #%s.', $idService))->setEntityDetails('Service', $idService)->setIdTransaction($idTransaction) );

				\DB::startTransaction();

				$serviceEnt = \Entity\Service::selectForUpdate($idService);

				// CHECK IF THE SERVICE IS INVOICED - IN THIS CASE A CREDIT NOTE IS ADDED ---------
				// OR FOR SIMPLE TEMPORARY INVOICES - THEY ARE CANCELLED --------------------------
				if ($serviceEnt->statusFin=='TI') {

					$serviceInvoicingDetails = $this->getServiceInvoicingDetails($serviceEnt);

					// FOR SIMPLE TEMPORARY INVOICES WE SHOULD CANCEL THE INVOICE -----------------
					if ($serviceInvoicingDetails['shouldCancelTemporaryInvoice']) {

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('The service #%s is invoiced, but the invoice is temporary - start voiding it.', $idService))->setEntityDetails('Service', $idService)->setIdTransaction($idTransaction) );

						$invoiceCancel = \Entity\Invoice::cancel($serviceInvoicingDetails['idInvoice']);
						$invoiceCancel->voidReason = 'autoVoidBySystem';
						$invoiceCancel->save();

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf('Successfully voiding the temporary invoice with id #%s for service #%s.', $serviceInvoicingDetails['idInvoice'], $idService))->setEntityDetails('Invoice', $serviceInvoicingDetails['idInvoice'], 'cancel')->setIdTransaction($idTransaction) );

					}
					// FOR OTHER MORE COMPLEX CASES, WE CREATE A CREDIT NOTE ----------------------
					else {

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('The service #%s is invoiced - start adding a credit note for it.', $idService))->setEntityDetails('Service', $idService)->setIdTransaction($idTransaction) );

						$origInvoiceEnt = $serviceEnt->relevantInvoice;

						$invoiceInsert = \Entity\Invoice::insert();
						// service details
						$invoiceInsert->idReservation = $serviceEnt->idOrder;
						$invoiceInsert->servicesInvoiceOptions = [
							[
								'idReservationService' =>	$idService,
								'invoiceContent' =>			'reverseTotal',
							],
						];
						// main info
						$invoiceInsert->invoiceDate = TODAY_ISO;
						$invoiceInsert->idInvoiceSeries = $ruleFields['idInvoiceSeries']?:0;
						$invoiceInsert->currency = $origInvoiceEnt->currency;
						// client details
						if ($origInvoiceEnt->idCustomer) {
							$invoiceInsert->idCustomer = $origInvoiceEnt->idCustomer;
						} else {
							$invoiceInsert->idCorporate = $origInvoiceEnt->idCorporate;
							$invoiceInsert->idCorporateOffice = $origInvoiceEnt->idCorporateOffice;
						}
						// due date - from dueDays or profile
						if (strlen($ruleFields['dueDays'])) {
							$dueDays = round($ruleFields['dueDays']);
							$invoiceInsert->dueDate = \Engine\DateTime::create()->add(new \DateInterval("P{$dueDays}D"))->formatDateIso();
						} else {
							$invoiceInsert->dueDate = $invoiceInsert->get_defaultDueDate();
						}
						// responsible user/office
						$idUser = \Entity\Corporate::select($origInvoiceEnt->idCorporate)->idInvoiceResponsible?:$ruleFields['idInvoiceUser'];
						$invoiceInsert->idUser = $idUser;
						$invoiceInsert->idOffice = \Entity\User::select($idUser)->idOffice;

						// set the agency bank account - mice project, corporate profile ior manually set
						$idBankAccount = 0;
						if ($serviceEnt->order->idMiceProject) { // try MICE project first
							$idBankAccount = \App\EntityPreference\MiceProject::getInstance($serviceEnt->order->idMiceProject)->idAgencyBankAccount;
						}
						if (!$idBankAccount and $origInvoiceEnt->idCorporate) { // then try corporate profile
							$idBankAccount = \App\EntityPreference\Corporate::getInstance($origInvoiceEnt->idCorporate)->agencyBankAccount;
						}
						if (!$idBankAccount) { // then the manual field
							$idBankAccount = $ruleFields['idAgencyBankAccount'];
						}
						if ($idBankAccount) {
							$invoiceInsert->idBankAccount = $idBankAccount;
						}

						// other fields
						$invoiceInsert->formOfPayment = (String)$ruleFields['formOfPayment'];
						$invoiceInsert->temporary = (Int)$ruleFields['temporaryInvoice'];
						$invoiceInsert->remarks = (String)$ruleFields['invoiceRemarks'];

						$invoiceInsert->save();

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf('Successfully added the credit note invoice with id #%s for service #%s.', $invoiceInsert->getId(), $idService))->setEntityDetails('Invoice', $invoiceInsert->getId(), 'insert')->setIdTransaction($idTransaction) );


					}

				}

				// check if this is the last active service on the order
				$activeServicesCount = 0;
				foreach ($serviceEnt->order->servicesList as $servDet) {
					if ($servDet['statusOp']!='XX') {
						$activeServicesCount++;
					}
				}

				// CANCEL THE SERVICE -------------------------------------------------------------
				$serviceCancel = \Entity\Service::cancel($idService);
				$serviceCancel->cancelReason = $ruleFields['serviceCancelReason'];
				$serviceCancel->cancelReasonDetails = $ruleFields['serviceCancelDetails'];
				$serviceCancel->save();

				\DB::commitTransaction();

				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf('The service #%s was successfully cancelled.', $idService))->setEntityDetails('Service', $idService, 'cancel')->setIdTransaction($idTransaction) );
				if ($activeServicesCount==1) {
					$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf('The order #%s was also cancelled.', $serviceEnt->idOrder))->setEntityDetails('Order', $serviceEnt->idOrder, 'cancel')->setIdTransaction($idTransaction) );
				}

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

				\DB::rollbackTransaction();

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

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

		}

	}

	/**
	 * Gets the service invoicing details - if there is only one temporary invoice or any other
	 * more complex situation.
	 * @param \Entity\Service $serviceEnt
	 */
	private function getServiceInvoicingDetails(\Entity\Service $serviceEnt)
	{

		$return = [
			'oneInvoice' => null,
			'idInvoice' => null,
			'temporaryInvoice' => null,
			'singleServiceOnInvoice' => null,
			'shouldCancelTemporaryInvoice' => null
		];

		$invoicesList = [];
		foreach($serviceEnt->invoiceRowsList as $rowDet) {

			if (!$rowDet['voidTime'] and !$rowDet['reversedRow'] and !$rowDet['reversesInvoiceRowId']) {
				$invoicesList[$rowDet['idInvoice']] = true;
			}
			// if more than one invoice reached - already skip
			if (count($invoicesList)>1) {
				break;
			}

		}

		// prepare return info
		if (count($invoicesList)!=1) {
			$return['oneInvoice'] = false;
			$return['idInvoice'] = 0;
		} else {
			$return['oneInvoice'] = true;
			$return['idInvoice'] = key($invoicesList);
			$return['temporaryInvoice'] = \Entity\Invoice::selectForUpdate($return['idInvoice'])->temporary;
		}

		// check that the selected invoice (if any) contains only one current service
		if ($return['idInvoice'] and $return['temporaryInvoice']) {
			$return['singleServiceOnInvoice'] = true;
			$invoiceEnt = \Entity\Invoice::selectForUpdate($return['idInvoice']);
			foreach($invoiceEnt->rowsList as $rowDet) {
				if ($rowDet['idReservationService']!=$serviceEnt->getId()) {
					$return['singleServiceOnInvoice'] = false;
					break;
				}
			}
		}

		// prepare output
		if ($return['oneInvoice'] and $return['idInvoice'] and $return['temporaryInvoice'] and $return['singleServiceOnInvoice']) {
			$return['shouldCancelTemporaryInvoice'] = true;
		} else {
			$return['shouldCancelTemporaryInvoice'] = false;
		}

		return $return;
	}


}

