<?php

namespace App\AutoProcessRules;

class InvoiceIssue_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();
		$stopOnFirstError = $ruleEnt->stopOnFirstError;


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

			$idTransaction = uniqid();

			// ISSUE INVOICE //////////////////////////////////////////////////////////////////////
			$currentInvoiceIssueOk = false;

			try {

				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', 'Start issuing the invoice')->setEntityDetails('Invoice', $idInvoice)->setIdTransaction($idTransaction) );

				\DB::startTransaction();

				// IF SPECIFICALLY INDICATED, THE INVOICE DATE MUST BE RESET TO CURRENT DAY ///////
				if ($ruleFields['resetInvoiceDate']) {
					$invoiceEnt = \Entity\Invoice::selectForUpdate($idInvoice);

					// only if the invoice date is in the past compared to current date
					if ($invoiceEnt->invoiceDate<TODAY_ISO) {
						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('The invoice has the date in the past and the execution will attempt to update its date from `%s` to `%s`.', $invoiceEnt->invoiceDate, TODAY_ISO))->setEntityDetails('Invoice', $idInvoice)->setIdTransaction($idTransaction) );

						// UPDATE INVOICE DETAILS (DATE AND DUE DATE) -----------------------------
						$invoiceUpdateEnt = \Entity\Invoice::update($idInvoice);
						$invoiceUpdateEnt->invoiceDate = TODAY_ISO;
						// reset the due date also
						if ($invoiceEnt->dueDate<TODAY_ISO) {
							$invoiceUpdateEnt->dueDate = TODAY_ISO;
							$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('The invoice due date is in the past also and the execution will attempt to update the due date from `%s` to `%s`.', $invoiceEnt->dueDate, TODAY_ISO))->setEntityDetails('Invoice', $idInvoice)->setIdTransaction($idTransaction) );
						}
						$invoiceUpdateEnt->save();

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', 'The invoice was successfully updated.')->setEntityDetails('Invoice', $idInvoice)->setIdTransaction($idTransaction) );
					}

				}


				// START INSERTING A DOCUMENT ON THE INVOICE (issue it) ///////////////////////////
				$invoiceIssueEnt = \Entity\Document::insert();

				// populate the order and associate the services
				$this->populateDocument($ruleLogs, $invoiceIssueEnt, $idInvoice, $ruleFields, $idTransaction);

				$invoiceIssueEnt->save();

				// get invoice id
				$idDoc = $invoiceIssueEnt->getId();

				// add msg
				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', 'Successfully issued invoice.')->setEntityDetails('Invoice', $idInvoice)->setIdTransaction($idTransaction) );
				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', 'Successfully issued invoice and created document.')->setEntityDetails('Document', $idDoc, 'insert')->setIdTransaction($idTransaction) );

				\DB::commitTransaction();

				$currentInvoiceIssueOk = true;

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

				\DB::rollbackTransaction();

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

				// if "stopOnFirstError" - we need to stop this whole process
				if ($stopOnFirstError) {
					$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('warning', 'The process is set to stop after the first encountered problem.') );
					break;
				}
			}

			// SEND INVOICE VIA EMAIL /////////////////////////////////////////////////////////////
			if ($currentInvoiceIssueOk and $ruleFields['sendEmail']) {

				$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', 'Start sending the email with the doc attached.')->setIdTransaction($idTransaction) );

				try {

					\DB::startTransaction();

					$documentSendEmailEntity = \Entity\Document::sendMail($idDoc);

					// populate entity document
					$this->populateDocumentSendEmail($documentSendEmailEntity, $idInvoice, $idDoc, $ruleFields);

					if ($documentSendEmailEntity->getField('receiversTo')) {

						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create( 'info', sprintf('Preparing email to be sent to the following addresses: %s.', implode(', ', $documentSendEmailEntity->getField('receiversTo'))) )->setIdTransaction($idTransaction) );

						$documentSendEmailEntity->save();

						// add msg
						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('success', sprintf('The issued invoice document #%s was successfully send via email to the client.', $idDoc))->setEntityDetails('Document', $idDoc, 'sendMail')->setIdTransaction($idTransaction) );

					} else {
						// add msg
						$ruleLogs->addLog( \App\AutoProcessRules\Logs\Log::create('info', sprintf('The sending of the document #%s (via email) was skipped (no email address selected/found).', $idDoc))->setEntityDetails('Document', $idDoc)->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->addLog(\App\AutoProcessRules\Logs\Log::create('warning', 'The process is set to stop after the first encountered problem.'));
						break;
					}
				}

			}

		}

	}


	/**
	 * Populates the invoice issue document entity.
	 * @param \App\AutoProcessRules\Logs\Rule $ruleLogs
	 * @param \Entity\Invoice $invoiceEnt
	 * @param String idInvoice
	 * @param Array $ruleFields
	 */
	protected function populateDocument(\App\AutoProcessRules\Logs\Rule $ruleLogs, \Entity\Document $invoiceIssueEnt, $idInvoice, Array $ruleFields, $idTransaction)
	{

		// determine the invoice series
		$idDocSeries = $ruleFields['idDocSeries'];
		if ($ruleFields['idDocSeries']=='pivotSeries') { // from pivot series
			$invoiceEnt = \Entity\Invoice::selectForUpdate($idInvoice);
			$idDocSeries = $invoiceEnt->idInvoiceSeries;
		} elseif ($ruleFields['idDocSeries']=='invOfficeFirstSeries') { // or from invoice office first series
			// gets the first series from the invoice responsible office (applies other filters as well)
			$invoiceEnt = \Entity\Invoice::selectForUpdate($idInvoice);
			$fldDocumentSeries = new \App\Form\Input\Select_DocumentSeries();
			$fldDocumentSeries->setDocTypesFilter([$ruleFields['invoiceDocType']]);
			$fldDocumentSeries->setIdOffice($invoiceEnt->idOffice);
			$fldDocumentSeries->setCurrency($invoiceEnt->currency);
			if ($ruleFields['invoiceIdIssueUser']) {
				$fldDocumentSeries->setIdUser($ruleFields['invoiceIdIssueUser']);
			}
			$fldDocumentSeries->setIdTemplate($ruleFields['idInvoiceTemplate']);
			$availableSeriesList = $fldDocumentSeries->getPossibleValues();
			reset($availableSeriesList);
			$idDocSeries = key($availableSeriesList);
		}

		// populate the doc object
		$invoiceIssueEnt->generation =			'auto';
		$invoiceIssueEnt->idPivot =				$idInvoice;
		$invoiceIssueEnt->pivot =				'invoice';
		$invoiceIssueEnt->docType =				$ruleFields['invoiceDocType'];
		$invoiceIssueEnt->idTemplate =			$ruleFields['idInvoiceTemplate'];
		$invoiceIssueEnt->idDocumentSeries =	$idDocSeries;
		if ($ruleFields['invoiceIdIssueUser']) {
			$invoiceIssueEnt->idIssueUser =		$ruleFields['invoiceIdIssueUser'];
		}

		$invoiceIssueEnt->idAutoProcessRule = 	$this->idRule;

	}

	/**
	 * Populates the document send email entity
	 * @param \Entity\Document $documentSendEmailEntity
	 * @param Int $invoiceEntity - ths invoice id
	 * @param Int $idDocument - the doc id
	 * @param Array $ruleFields - rule fields
	 */
	private function populateDocumentSendEmail(\Entity\Document $documentSendEmailEntity, int $idInvoice, int $idDocument, Array $ruleFields)
	{

		$invoiceEntity = \Entity\Invoice::selectForUpdate($idInvoice);
		$documentEntity = \Entity\Document::selectForUpdate($idDocument);

		$idUserSender = $ruleFields['invoiceIdIssueUser'] ? : $invoiceEntity->idUser;

		// POPULATE THE DOCUMENT OBJECT
		$documentSendEmailEntity->selfBcc = 		(Bool)$ruleFields['selfBcc'];
		$documentSendEmailEntity->sendReadReceipt = (Bool)$ruleFields['sendReadReceipt'];
		$documentSendEmailEntity->generatedFiles =	[$idDocument];
		$documentSendEmailEntity->subject =			$documentEntity->getSendMailDefaultSubject($idUserSender);
		$documentSendEmailEntity->body =			$documentEntity->getSendMailDefaultBody($idUserSender);
		$documentSendEmailEntity->idSenderUser	=	$idUserSender;

		// populate receipient
		$receipients = [];
		if ($ruleFields['emailRecipientClient']) {
			if ($invoiceEntity->idCustomer) {
				$receipients[] = $invoiceEntity->customer->email;
			}
			if ($invoiceEntity->idCorporate) {
				if ($invoiceEntity->corporateOffice->email) {
					$receipients[] = $invoiceEntity->corporateOffice->email;
				} elseif ($invoiceEntity->corporate->email) {
					$receipients[] = $invoiceEntity->corporate->email;
				}
			}
		}

		// adds all passenger emails from invoice
		if ($ruleFields['emailRecipientPassengers']) {
			// adds all passengrs (with email) from the current invoice
			$processedServicesList = []; // manages services so they are considered only once per invoice
			foreach($invoiceEntity->rowsList as $rowDet) {
				if (!$processedServicesList[$rowDet['idReservationService']]) {
					foreach (\Entity\Service::select($rowDet['idReservationService'])->idCustomersList as $idCustomer) {
						$customerEmail = \Entity\Customer::select($idCustomer)->email;
						if ($customerEmail) {
							$receipients[] = $customerEmail;
						}
					}
				}
				$processedServicesList[$rowDet['idReservationService']] = true;
			}
		}

		// adds invoice responsible
		if ($ruleFields['emailRecipientInvResp']) {
			if ($invoiceEntity->user->email) {
				$receipients[] = $invoiceEntity->user->email;
			}
		}

		// adds recipient(s) from invoice remark
		if ($ruleFields['emailRecipientInvRemarkVar']) {
			$rmEmail = $invoiceEntity->processedRemarks[$ruleFields['emailRecipientInvRemarkVar']];
			if ($rmEmail) {
				$receipients = array_merge($receipients, array_map('trim', preg_split('/[\;\,]/', $rmEmail)));
			}
		}

		// adds manual emails
		if ($ruleFields['emailRecipientManual']) {
			$receipients = array_merge($receipients, array_map('trim', preg_split('/[\;\,]/', $ruleFields['emailRecipientManual'])));
		}

		// cleanup empty emails and delete duplicates
		$receipients = array_unique(array_filter($receipients));

		$documentSendEmailEntity->receiversTo = $receipients;

	}
}

