<?php

namespace Modules\Documents\ImportedDocuments\SplitByInvoiceSign;


class Model extends \App\Mvc\ModelAbstract
{

	/**
	 * Imported document id.
	 * @var int
	 */
	private $idImportedDocument = [];

	/**
	 * The list of matched positive invoices.
	 * @var array
	 */
	private $positiveInvoicesIds = [];

	/**
	 * The total of all positive invoices.
	 * @var float
	 */
	private $positiveInvoicesTotal = 0;

	/**
	 * The list of matched negative invoices.
	 * @var array
	 */
	private $negativeInvoicesIds = [];

	/**
	 * The total of all negative invoices.
	 * @var float
	 */
	private $negativeInvoicesTotal = 0;

	/**
	 * @param int $idImportedDocument
	 */
	public function __construct($idImportedDocument)
	{
		parent::__construct();
		$this->idImportedDocument = $idImportedDocument;
	}



	/**
	 * Adds order/service/invoice/creditNote for this operation.
	 */
	public function process()
	{

		// CHECKS THE IMPORTED DOCS IDS
		$this->check();

		// updates the current imported document and adds a new one
		$this->splitDocument();

	}


	/**
	 * Checks that the current operation is ok to be executed.
	 */
	private function check()
	{
		// check if inside transaction
		if (!\DB::isInsideTransaction()) {
			throw new \Exception('You must be inside a transaction when inserting a refund deposit document.');
		}

		$this->isAvailable(true);

	}


	/**
	 * Checks if the operation is available - checks the imported docs details.
	 * @throws \App\MessagesException - if any problem found
	 */
	public function isAvailable($selectForUpdate)
	{

		$msgs = new \App\MessagesCollector();

		\App\Loader_Lang::getInstance()->loadSection('Modules/Documents/ImportedDocuments/CompensateDocs');
		$_LANG = \App\Loader_Lang::getInstance()->getGlobal();

		// check user access
		if (!\App\UserAccess\CurrentUserAccess::getInstance()->hasAccessToOption('Modules/Documents/ImportedDocuments-splitByInvoiceSign')) {
			$msgs->addWarning($_LANG['noAccessToOption']);
		}

		// INITS AND GENERAL VALIDATIONS ----------------------------------------------------------
		$impDocEnt = \Entity\ImportedDocument::select($this->idImportedDocument, $selectForUpdate);

		// check id
		if (!$impDocEnt->recordExists()) {
			$msgs->addError($this->lang['index']['msgs']['invalidId']);
		}
		elseif ($impDocEnt->status!='new') {
			$msgs->addWarning($this->lang['index']['msgs']['wrongDocSts']);
		}

		$impDocObj = $impDocEnt->object;


		// CHECK IF ANY INVOICES LIST ARE ASSOCIATED ON THE IMPORTED DOCUMENT  --------------------
		if (!$msgs->hasErrorOrWarning()) {
			if ($impDocEnt->docCategory=='cashing') {
				$invoicesList = $impDocObj->getInvoicesList();
			} else {
				$invoicesList = $impDocObj->getSupplierInvoicesList();
			}
			if (!$invoicesList) {
				$msgs->addWarning($this->lang['index']['msgs']['noAssocInv']);
			}
		}

		// CHECK INVOICES ONE BY ONE --------------------------------------------------------------
		if (!$msgs->hasErrorOrWarning()) {
			$posInvoicesList =  $negInvoicesList = [];
			$posInvoicesTotal = $negInvoicesTotal = 0;
			foreach ($invoicesList as $invoiceDetails) {
				$invIds = $invoiceDetails->getMatchedIds();
				if (!$invIds) {
					$msgs->addWarning($this->lang['index']['msgs']['unmatchedInv']);
					break;
				}
				if (count($invIds)>1) {
					$msgs->addWarning($this->lang['index']['msgs']['multiMatchInv']);
					break;
				}
				// determine invoice sign
				$idInvoice = reset($invIds);
				if ($impDocEnt->docCategory=='cashing') {
					$invEnt = \Entity\Invoice::select($idInvoice);
					$invPaidStatus = $invEnt->cashedStatus;
					$invTotalValue = $invEnt->printTotal;
				} else {
					$invEnt = \Entity\SupplierInvoice::select($idInvoice);
					$invPaidStatus = $invEnt->statusFin;
					$invTotalValue = $invEnt->totalValue;
				}

				// check invoice currency vs document currency
				if ($invEnt->currency!=$impDocEnt->docCurrency) {
					$msgs->addWarning($this->lang['index']['msgs']['diffCurrInv']);
					break;
				}
				if ($invPaidStatus!='n') {
					$msgs->addWarning($this->lang['index']['msgs']['invStatusWrong']);
					break;
				}

				// determine total and invoices list
				if ($invTotalValue>0) {
					$posInvoicesList[] = $idInvoice;
					$posInvoicesTotal += $invTotalValue;
				} elseif ($invTotalValue<0) {
					$negInvoicesList[] = $idInvoice;
					$negInvoicesTotal += $invTotalValue;
				}
			}

			if (!$posInvoicesList or !$negInvoicesList) {
				$msgs->addWarning($this->lang['index']['msgs']['notBothInvSigns']);
			}

			// cache the list of positive/negative invoices on the current document
			$this->positiveInvoicesIds = $posInvoicesList;
			$this->positiveInvoicesTotal = $posInvoicesTotal;
			$this->negativeInvoicesIds = $negInvoicesList;
			$this->negativeInvoicesTotal = $negInvoicesTotal;

		}

		// allow plugins to extend the functionality
		\Engine\Plugins\Manager::getInstance()->executeEvent('Modules\Documents\ImportedDocuments\SplitByInvoiceSign-Model-isAvailable', ['idImportedDocument'=>$this->idImportedDocument, 'msgs'=>$msgs] );


		// throw exception is something failed
		if ($msgs->hasErrorOrWarning()) {
			throw new \App\MessagesException($msgs);
		}

	}


	public function getPositiveInvoicesIds()
	{
		return $this->positiveInvoicesIds;
	}

	public function getNegativeInvoicesIds()
	{
		return $this->negativeInvoicesIds;
	}

	public function getPositiveInvoicesTotal()
	{
		return $this->positiveInvoicesTotal;
	}

	public function getNegativeInvoicesTotal()
	{
		return $this->negativeInvoicesTotal;
	}


	/**
	 * Does the actual splitting of the imported document.
	 */
	private function splitDocument()
	{

		$impDocEnt = \Entity\ImportedDocument::selectForUpdate($this->idImportedDocument);
		$impDocObj = $impDocEnt->object;

		// CASHING ////////////////////////////////////////////////////////////////////////////////
		if ($impDocEnt->docCategory=='cashing') {

			$invoicesList = $impDocObj->getInvoicesList();

			// collect which invoices should be kept for original document and which should be included in the new document
			$orgInvoices = $newInvoices = [];
			$adjustment = 0;

			foreach ($invoicesList as $invoiceDetails) {
				$idInvoice = reset($invoiceDetails->getMatchedIds());
				$invoiceEnt = \Entity\Invoice::select($idInvoice);
				if ( ($invoiceEnt->printTotal>0 and $impDocObj->value>0) or ($invoiceEnt->printTotal<0 and $impDocObj->value<0) ) {
					$orgInvoices[] = $invoiceDetails;
				} else {
					$newInvoices[] = $invoiceDetails;
					$adjustment += $invoiceEnt->printTotal;
				}

			}

			// UPDATE ORIGINAL IMPORTED DOCUMENT --------------------------------------------------
 			$orgImportedDocObj = new \App\ImportedDocument\Cashing();
 			$orgImportedDocObj->externalSystemId = $impDocEnt->idExternalSystem;
 			$orgImportedDocObj->idDocumentSystem = $impDocEnt->idSystem;
 			$orgImportedDocObj->docNumber = $impDocEnt->docNumber;
			$orgImportedDocObj->operation = 'update';
			// adjust the values
			$orgImportedDocObj->value = $impDocObj->value-$adjustment;
			$orgImportedDocObj->currency = $impDocObj->currency;

			// set the new invoices
			$orgImportedDocObj->clearInvoicesList();
			foreach($orgInvoices as $invObj) {
				$orgImportedDocObj->addInvoice($invObj);
			}

			// UPDATE NEW DOCUMENT ----------------------------------------------------------------
			$newImportedDocObj = clone $impDocObj;
			$newImportedDocObj->operation = 'add';
			$newImportedDocObj->idDocumentSystem = $newImportedDocObj->idDocumentSystem.'_'.uniqid();
			$newImportedDocObj->value = $adjustment;

			// set the new invoices
			$newImportedDocObj->clearInvoicesList();
			foreach($newInvoices as $invObj) {
				$newImportedDocObj->addInvoice($invObj);
			}

		}

		// PAYMENT ////////////////////////////////////////////////////////////////////////////////
		else {

			$invoicesList = $impDocObj->getSupplierInvoicesList();

			// collect which invoices should be kept for original document and which should be included in the new document
			$orgInvoices = $newInvoices = [];
			$adjustment = 0;

			foreach ($invoicesList as $invoiceDetails) {
				$idInvoice = reset($invoiceDetails->getMatchedIds());
				$invoiceEnt = \Entity\SupplierInvoice::select($idInvoice);
				if ( ($invoiceEnt->totalValue>0 and $impDocObj->value>0) or ($invoiceEnt->totalValue<0 and $impDocObj->value<0) ) {
					$orgInvoices[] = $invoiceDetails;
				} else {
					$newInvoices[] = $invoiceDetails;
					$adjustment += $invoiceEnt->totalValue;
				}

			}

			// UPDATE ORIGINAL IMPORTED DOCUMENT --------------------------------------------------
			$orgImportedDocObj = new \App\ImportedDocument\Payment();
 			$orgImportedDocObj->externalSystemId = $impDocEnt->idExternalSystem;
 			$orgImportedDocObj->idDocumentSystem = $impDocEnt->idSystem;
 			$orgImportedDocObj->docNumber = $impDocEnt->docNumber;
			$orgImportedDocObj->operation = 'update';
			// adjust the values
			$orgImportedDocObj->value = $impDocObj->value-$adjustment;
			$orgImportedDocObj->currency = $impDocObj->currency;

			// set the new invoices
			$orgImportedDocObj->clearSupplierInvoicesList();
			foreach($orgInvoices as $invObj) {
				$orgImportedDocObj->addSupplierInvoice($invObj);
			}

			// UPDATE NEW DOCUMENT ----------------------------------------------------------------
			$newImportedDocObj = clone $impDocObj;
			$newImportedDocObj->operation = 'add';
			$newImportedDocObj->idDocumentSystem = $newImportedDocObj->idDocumentSystem.'_'.uniqid();
			$newImportedDocObj->value = $adjustment;

			// set the new invoices
			$newImportedDocObj->clearSupplierInvoicesList();
			foreach($newInvoices as $invObj) {
				$newImportedDocObj->addSupplierInvoice($invObj);
			}

		}

		// STORE IMPORTED DOCUMENTS IN THE DATABASE -----------------------------------------------
		$orgImpDocEnt = \Entity\ImportedDocument::insert();
		$orgImpDocEnt->document = $orgImportedDocObj;

		$newImpDocEnt = \Entity\ImportedDocument::insert();
		$newImpDocEnt->document = $newImportedDocObj;

		// allow plugins to extend the functionality
		\Engine\Plugins\Manager::getInstance()->executeEvent('Modules\Documents\ImportedDocuments\SplitByInvoiceSign-Model-splitDocsDetails', ['idImportedDocument'=>$this->idImportedDocument, 'orgImportedDocEnt'=>$orgImpDocEnt, 'newImportedDocEnt'=>$newImpDocEnt] );

		$orgImpDocEnt->save();
		$newImpDocEnt->save();

	}

}
