<?php
namespace Modules\Documents\ImportedDocuments\ImportStatements\Parsers\MT940;

/**
 * Class for converting a MT940 file into structured statements (\stdClass)
 * @author Dan Baluta
 */
class Parser
{

	/**
	 * Convert MT940 file contents into structured statements (\stdClass[])
	 * @param string $fileContents
	 * @return array
	 */
	public function extract($fileContents)
	{
		// convert \r\n line deliminters to unix standard format, because it easier in future regexps and debugs
		$fileContents = str_replace(["\r\n", "\r", "\n"], "\n", $fileContents);

		$statementBlocks = [];
		if (preg_match_all('/{1:(.*?)-}/ms', $fileContents, $match)) {
			$statementBlocks = $match[0];
		}

		$statementBodies = [];
		foreach ($statementBlocks as $statementStr) {
			if (preg_match('/{4:(.*?)-}/ms', $statementStr, $match)) {
				$statementBodies[] = $match[1];
			}

		}

		$statements = [];

		// TODO: [refactor-bankStatements-unicredit] start from $statementBlocks
		foreach ($statementBodies as $statementStr) {

			// GENERATE STATEMENT
			$statementObj = new Structure\Statement();

			// extract reference (:20:)  TODO: [refactor-bankStatements-unicredit] all chars?
			if (preg_match('/:20:([A-Za-z0-9 .]{1,16})$/m', $statementStr, $match)) {
				$statementObj->reference = $match[1];
			}

			// extract account identification (:25:)
			if (preg_match('/:25:([A-Za-z0-9\/]{1,35})$/m', $statementStr, $match)) {
				$statementObj->accountIdentification = $match[1];
			}

			// extract statement number (:28C:)
			if (preg_match('/:28C:([A-Za-z0-9\/]{1,8})$/m', $statementStr, $match)) {
				$statementObj->statementNumber = $match[1];
			}

			// extract start balance (:60X:)
			if (preg_match('/:60(?<balanceType>F|M):(?<dcMark>C|D{1})(?<date>[0-9]{6})(?<currency>[A-Z]{3})(?<amount>[0-9,]{1,16})$/m', $statementStr, $match)) {
				$balanceObj = new Structure\Balance();
				$balanceObj->balanceType = $match['balanceType'];
				$balanceObj->dcMark 	 = $match['dcMark'];
				$balanceObj->date 		 = $match['date'];
				$balanceObj->currency	 = $match['currency'];
				$balanceObj->amount		 = $match['amount'];

				$statementObj->startBalance = $balanceObj;
			}

			// extract statement lines blocks (:61: <-> :61:|:62M:|:62F:)
			if (preg_match_all('/:61:(.*?)(?=\n:61:|\n:62M:|\n:62F:)/ms', $statementStr, $match)) {
				$lineBodies = $match[0];
				foreach ($lineBodies as $lineStr) {

					// extract statement line (:61:)
					// TODO: [refactor-bankStatements-unicredit] test without bankReference
					if (preg_match('/:61:(?<valueDate>[0-9]{6})(?<entryDate>[0-9]{4})(?<dcMark>C|RC|D|RD{1,2})(?<amount>[0-9,]{1,16})(?<transactionCode>[A-Za-z0-9]{1,4})(?<accountOwnerReference>[A-Za-z0-9]{1,16})(\/\/|\n\/\/)?(?<bankReference>[A-Za-z0-9-]{1,16}\s*)?$/m', $lineStr, $match)) {
						$lineObj = new Structure\StatementLine();
						$lineObj->valueDate = $match['valueDate'];	// TODO: [refactor-bankStatements-unicredit] doc date?
						$lineObj->entryDate = $match['entryDate'];
						$lineObj->dcMark 	= $match['dcMark'];
						$lineObj->amount 	= $match['amount'];
						$lineObj->transactionCode 		= $match['transactionCode'];
						$lineObj->accountOwnerReference = $match['accountOwnerReference'];
						$lineObj->bankReference 		= trim((String)$match['bankReference']);
						$lineObj->line61 = $match[0];

						// extract additional information (:86:)
						if (preg_match('/:86:(.*)/ms', $lineStr, $match)) {
							$lineObj->line86 = $match[0];

							$detailsStr = $match[1];

							// Invoice number
							if (preg_match('/\?24\/VS(?<invoiceNumber>[0-9].*?)\//ms', $detailsStr, $match)) {
								$lineObj->invoiceNumber = $match['invoiceNumber'];
							}
							elseif (preg_match('/\?22(VS|VS:)(?<invoiceNumber>[0-9\s].*?)\?23/ms', $detailsStr, $match)) {
								$lineObj->invoiceNumber = trim((int)$match['invoiceNumber']);
							}

							// Structured Field 86 for SEPA domestic payments
							// Business Transactions Code: 004, 008, 020, 021, 051, 071, 087, 088
							if (preg_match('/(?<businessTransactionCode>004|008|020|021|051|071|087|088)(\?00.*?)$/m', $detailsStr, $match)) {
								$lineObj->businessTransactionCode = $match['businessTransactionCode'];
							}

							// Structured Field 86 for Cash operations
							// Business Transactions Code: 082, 083
							elseif (preg_match('/(?<businessTransactionCode>082|083)(\?00.*?)$/m', $detailsStr, $match)) {
								$lineObj->businessTransactionCode = $match['businessTransactionCode'];
							}

							// Structured Field 86 for Foreign payments
							// Business Transactions Code: 201, 202
							elseif (preg_match('/(?<businessTransactionCode>201|202)(\?00.*?)$/m', $detailsStr, $match)) {
								$lineObj->businessTransactionCode = $match['businessTransactionCode'];
							}

							// Structured Field 86 for Other structured transactions
							// Business Transactions Code: 899

							// Unstructured Field 86 for Other unstructured transactions
							// Business Transactions Code: 999
							elseif (preg_match('/(?<businessTransactionCode>999)(?<otherUnstructuredDetails>.*)/ms', $detailsStr, $match)) {
								$lineObj->businessTransactionCode = $match['businessTransactionCode'];
							}
							else {
								//var_dump($detailsStr); die;
							}

						}

						$statementObj->lines[] = $lineObj;
					}

				}
			}

			// extract end balance (:62X:)
			if (preg_match('/:62(?<balanceType>F|M):(?<dcMark>C|D{1})(?<date>[0-9]{6})(?<currency>[A-Z]{3})(?<amount>[0-9,]{1,16})$/m', $statementStr, $match)) {
				$balanceObj = new Structure\Balance();
				$balanceObj->balanceType = $match['balanceType'];
				$balanceObj->dcMark 	 = $match['dcMark'];
				$balanceObj->date 		 = $match['date'];
				$balanceObj->currency	 = $match['currency'];
				$balanceObj->amount		 = $match['amount'];

				$statementObj->endBalance = $balanceObj;
			}

			// extract closing balance (Available funds) (:64:)
			if (preg_match('/:64:(?<dcMark>C|D{1})(?<date>[0-9]{6})(?<currency>[A-Z]{3})(?<amount>[0-9,]{1,16})$/m', $statementStr, $match)) {
				$balanceObj = new Structure\Balance();
				$balanceObj->dcMark 	 = $match['dcMark'];
				$balanceObj->date 		 = $match['date'];
				$balanceObj->currency	 = $match['currency'];
				$balanceObj->amount		 = $match['amount'];

				$statementObj->closingBalance = $balanceObj;
			}

			// extract Forward balance (:65:)
			if (preg_match('/:65:(?<dcMark>C|D{1})(?<date>[0-9]{6})(?<currency>[A-Z]{3})(?<amount>[0-9,]{1,16})$/m', $statementStr, $match)) {
				$balanceObj = new Structure\Balance();
				$balanceObj->dcMark 	 = $match['dcMark'];
				$balanceObj->date 		 = $match['date'];
				$balanceObj->currency	 = $match['currency'];
				$balanceObj->amount		 = $match['amount'];

				$statementObj->forwardBalance = $balanceObj;
			}

			$statements[] = $statementObj;
		}

		return $statements;
	}

}
