<?php
namespace App\Worldspan\Storage;

/**
 * Class to handle internal DB storage
 * @author Lucian Sirbu
 */
class StorageHandler extends \App\Utils\BaseParser\Structure\Storage\StorageHandlerAbstract
{
	use \App\Utils\BaseParser\SingletonTrait;

	public function __construct()
	{
		global $DBTWorldspanServices;
		$this->table = $DBTWorldspanServices;

		parent::__construct();
	}

	/**
	 * {@inheritDoc}
	 * @see \App\Utils\BaseParser\Structure\Storage\StorageHandlerAbstract::getSelectedEntity()
	 */
	public function getSelectedEntity($id)
	{
		return \Entity\WorldspanService::select($id);
	}

	protected function insertParsedFile()
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());
		$parsedFile = \Entity\WorldspanFile::select($this->processor->getFileParser()->getFileId());
		if ($parsedFile->recordExists()) {
			throw new \App\Worldspan\Storage\Exception("File is already parsed! (original file was '{$parsedFile->filePath}' on {$parsedFile->addTime})");
		}

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
			'getFileId' => $this->processor->getFileParser()->getFileId(),
			'filePath' => $this->processor->getFile(),
		]);

		// let the entity throw exceptions
		$parsedFile = 			\Entity\WorldspanFile::insert();
		$parsedFile->idFile = 	$this->processor->getFileParser()->getFileId();

		$parsedFile->filePath = $this->processor->getFile();

		$parsedFile->source = 	$this->getOption('source');
		$parsedFile->IP = 		$this->getOption('IP');

		$parsedFile->save();
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
	}

	protected function insertParsedFileTransactions()
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());
		// loop through all transactions and store them in the DB flipping various fields as needed
		$transactions = $this->processor->getTransactions();

		if ($transactions) {
			foreach ($transactions as $transaction) {
				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__.":foreach", func_get_args());

				/* @var $transaction \App\Worldspan\StructureParser\Structures\Transaction */

				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logInput([
					'$transaction->getType()' => $transaction->getType(),
					'$transaction->documents->docNumber' => $transaction->documents->docNumber,
				]);

				// create a list of old transactions
				$oldTransactions = [];
				switch ($transaction->getType()) {
					case 'void' :
					case 'refund' :
					case 'ticketing':
						$oldTransactions = $this->selectPreviousServiceArrFromDB([
							'idFile!' => 	$this->processor->getFileParser()->getFileId(),
							'docNumber' => 	$transaction->documents->docNumber,
						]);
					break;

					case 'reissue':
						$oldTransactions = $this->selectPreviousServiceArrFromDB([
							'idFile!' => 	$this->processor->getFileParser()->getFileId(),
							'docNumber' => 	$transaction->relations->filter('reissue')->first()->docNumber,
							// TODO: use the doc date
						]);
					break;
				}

				$oldTransactions = array_map(function($tr) { return \Entity\WorldspanService::select($tr['id']);  }, $oldTransactions);

				$insTransactions = $this->insertTransaction($transaction, $oldTransactions);
				$updTransactions = $this->updateTransaction($transaction, $oldTransactions, $insTransactions);

				// store the resulted entity data
				foreach ($insTransactions as $tr) {
					\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
						'inserted' => $tr->getId(),
					]);
					$this->dbTransactions['inserted'][] = $tr->getId();
				}
				foreach ($updTransactions as $tr) {
					\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
						'updated' => $tr->getId(),
					]);
					$this->dbTransactions['updated'][] = $tr->getId();
				}

				\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
			} // end: foreach $transactions
		}
		else {
			// no transactions defined, dont know what to do next
			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
			throw new \App\Worldspan\Storage\Exception("Received a file without transactions. Dont know how to handle this case.");
		}

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();
		return $this;
	}

	protected function insertTransaction(\App\Utils\BaseParser\Structure\StructureParser\Structures\Transaction $transaction, $oldTransactions = [])
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());
		$insTransactions = [];

		$insTransaction = \Entity\WorldspanService::insert();

		$prevTransaction = end($oldTransactions);

		// for some transactions get the serviceType from the previous transaction
		switch ($transaction->getType()) {
			case 'EMD':
			case 'MCO':
			case 'ticketing':
				$insTransaction->transactionStatus = 	'new';
				$insTransaction->serviceCode = 			$transaction->getType();
			break;

			case 'reissue':
				if (!$oldTransactions) {
					throw new \App\Worldspan\Storage\Exception("We received a reissue operation, but we cannot find the original ticket");
				}

				$insTransaction->serviceCode = 			'ticketing';
				$insTransaction->transactionStatus = 	'reissue';
			break;

			case 'refund':
				if (!$oldTransactions) {
					throw new \App\Worldspan\Storage\Exception("We received a refund operation, but we cannot find the original ticket to apply the refund to");
				}

				$insTransaction->serviceCode = 			$prevTransaction->serviceCode;
				$insTransaction->transactionStatus = 	'refund';
			break;

			case 'void':
				if (!$oldTransactions) {
					throw new \App\Worldspan\Storage\Exception("We received a void operation, but we cannot find the original ticket to apply the void to");
				}

				$insTransaction->serviceCode = 			$prevTransaction->serviceCode;
				$insTransaction->transactionStatus = 	'void';
			break;

			case 'spoil':
				$insTransaction->serviceCode = 			'spoil';
				$insTransaction->transactionStatus = 	'new';
			break;

			default:
				throw new \App\Worldspan\Storage\Exception(sprintf("We received an unhandled operation(%s)", $transaction->getType()));
		}

		$insTransaction->idFile = 				$this->processor->getFileParser()->getFileId();
		$insTransaction->docNumber = 			$transaction->documents->docNumber;
		$insTransaction->docDate = 				$transaction->documents->docDate;
		$insTransaction->transactionCode = 		$transaction->documents->RECLOC;

		$transactionReferences = new \App\Worldspan\Storage\Types\TransactionReferences();
		foreach ($oldTransactions as $tr) {
			if ($transaction->getType()=='reissue') {
				$reference = new \App\Worldspan\Storage\Types\TransactionReference('reissueOf', (string)$tr->getId());
			}
			elseif ($transaction->getType()=='ticketing') {
				$reference = new \App\Worldspan\Storage\Types\TransactionReference('child', (string)$tr->getId());
			}
			else {
				$reference = new \App\Worldspan\Storage\Types\TransactionReference('parent', (string)$tr->getId());
			}

			$transactionReferences->append($reference);
		}

		if ($transaction->relations) {
			foreach ($transaction->relations->relations as $rel) {
				if ($rel->relationType!='reissue') {
					$filters = [];
					$filters['docNumber'] = $rel->docNumber;

					if ($rel->docDate) {
						$filters['docDate'] = $rel->docDate;
					}
					else {
						$filters['docDate>'] = \Engine\DateTime::createFromIsoDate($transaction->documents->createDate)->modify("-6 months")->formatDateIso();
						// $filters['docDate<'] = \Engine\DateTime::createFromIsoDate($transaction->documents->docDate)->modify("+1 day")->formatDateIso();
					}

					$relationEntity = $this->selectServiceArrFromDB($filters);
					if ($relationEntity) {
						$reference = new \App\Sabre\Storage\Types\TransactionReference($rel->relationType, (string)$relationEntity[0]['id']);
						$transactionReferences->append($reference);
					}
					else {
						throw new \App\Sabre\Storage\Exception(sprintf("Cannot find referenced relation %s/%s", $rel->docNumber, $rel->relationType));
					}
				}
			}
		}

		$insTransaction->transactionReferences = $transactionReferences;

		// user / offices
		$insTransaction->idWorldspanBookUser = 		$transaction->agent->bookingAgent->idSystemUser;
		$insTransaction->idWorldspanBookOffice = 	$transaction->agent->bookingAgent->idSystemOffice;
		$insTransaction->idWorldspanIssueUser = 	$transaction->agent->issuingAgent->idSystemUser;
		$insTransaction->idWorldspanIssueOffice = 	$transaction->agent->issuingAgent->idSystemOffice;

		$insTransaction->transactionData = 		$transaction;
		$insTransaction->extraData = 			[];

		// perform the final save
		$insTransaction->save();

		$insTransactions[] = $insTransaction;

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();

		return $insTransactions;
	}

	protected function updateTransaction(\App\Utils\BaseParser\Structure\StructureParser\Structures\Transaction $transaction, $oldTransactions, $insTransactions)
	{
		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallStart($this, __METHOD__, func_get_args());

		$updOldTransactions = [];
		foreach ($oldTransactions as $oldTransaction) {
			// update the old transaction status
			$updOldTransaction = \Entity\WorldspanService::update($oldTransaction->getId());

			switch ($transaction->getType()) {
				case 'ticketing':
					$updOldTransaction->transactionStatus = 'old';
				break;

				case 'reissue':
					$updOldTransaction->transactionStatus = 'reissued';
				break;

				case 'refund':
					$updOldTransaction->transactionStatus = 'refunded';
				break;

				case 'void':
					$updOldTransaction->transactionStatus = 'voided';
				break;
			}

			$transactionReferences = $oldTransaction->transactionReferences;

			foreach ($insTransactions as $insTransaction) {
				if ($transaction->getType()=='reissue') {
					$reference = new \App\Worldspan\Storage\Types\TransactionReference('reissueOf', (string)$insTransaction->getId());
				}
				elseif ($transaction->getType()=='ticketing') {
					$reference = new \App\Worldspan\Storage\Types\TransactionReference('parent', (string)$insTransaction->getId());
				}
				else {
					$reference = new \App\Worldspan\Storage\Types\TransactionReference('child', (string)$insTransaction->getId());
				}

				$transactionReferences->append($reference);
			}

			\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logOutput([
				'$oldTransaction' => 		$oldTransaction->getId(),
				'$transactionReferences' => $transactionReferences->asStr(),
			]);

			$updOldTransaction->transactionReferences = $transactionReferences;

			$updOldTransaction->save();

			$updOldTransactions[]= $updOldTransaction;
		}

		\Engine\Debug\ParserCallTrace\ParserCallTrace::getInstance()->logCallEnd();

		return $updOldTransactions;
	}
}
