<?php
namespace App\Amadeus\FileParser;

/**
 * Class used as an agregate by lines having "form of payment" or FP style tags.
 * This class adds FP_autoParse(), FP_parse() that handles the specific format of these lines
 * @author Lucian
 */
abstract class Line_FPAbstract extends \App\Amadeus\FileParser\LineAbstract
{
	/**
	 * Automatically parse the FP line (PAYMNT tag)
	 */
	protected function FP_autoParse()
	{
		$FP = $this->FP_parse($this->tree['PAYMNT'], $this->parser);

		// load data from $FP
		foreach ($FP as $k=>$v) {
			$this->tree[$k] = $v;
		}
	}

	/**
	 * General parser for a FP-like line
	 */
	protected function FP_parse($PAYMNT, $parser)
	{
		$FP = [];

		$definedCurrencies = \App\Config\Currencies::getInstance()->getSimple();

		// if no PAYMNT tag is present, we can skip the whole parsing process
		if ($PAYMNT) {
			$FP['type'] = null;
			/***************************************************************************************************************
			 * NR stands for Net Remit, and is used as a alternative to the FM tag
			 */
			$m = [];
			// FPNRHUF6200+INVOICE/HUF68500
			if (!$FP['type'] && preg_match("/^NR(?P<currency>[A-Z]{3})(?P<amount>[0-9\.]+)/", $PAYMNT, $m)) {
				/**
				 * Here, we assume that the format contains the commission in the first part of the FP tag, immediatelly after the FP prefix
				 * @see 2011-01-18_130941_213.178.106.165__131747_01-din DAMA.AIR for an example
				 */
				$FP['commission'] 	 	 = 'value';	// when this is 'value', we should use this instead of the FM line
				$FP['commissionValue'] 	 = $m['amount'];
				$FP['commissionCurrency']= $m['currency'];

				$FP['type'] = 'net-remit';
			}

			// this case was found during testing with uTESTS, and never reported to/by the HU client that actually uses this
			// NR+CCAX377945449361006/0713/HUF82389/A184816
			if (!$FP['type'] && preg_match("/^NR(.{10,})\/(?P<currency>[A-Z]{3})(?P<amount>[0-9\.]+)/", $PAYMNT, $m)) {
				/**
				 * Here, we assume we have a FP tag, without the absolute value expressed as expected initially(see above).
				 * Expect that the total amount does arrive(commission+supplier+tax), expressed in the form HUF82389
				 * @see 2012-01-31_115357_10.193.2.56__M_L_3101_26-manarit.AIR for an example
				 */
				$FP['commission'] 	 	 = 'diff';	// when this is true, we should use this instead of the FM line
				$FP['commissionValue'] 	 = $m['amount'];
				$FP['commissionCurrency']= $m['currency'];

				$FP['type'] = 'net-remit';
			}

			// this case is specific to Fursan, @see TD-3969: Add the possibility to interpret a new format for the form of payment line from Amadeus [C]
			// GOV/GR0000666477-1234-24082017*A003453502
			// GOV/GR0000666477-1234-24082017
			// GOV/GR0000666518-EMP15772-22012018*A003894946
			if (!$FP['type'] && preg_match("/^(GOV\/GR|GR)(?P<accountCode>[0-9]+)-(?P<travelOrderNo>[A-Z0-9]+)-(?P<travelOrderDate>[0-9]+)(\*.+)?/", $PAYMNT, $m)) {
				/**
				 * @see amadeus-201708-20170824 131508 20 02.FluX.24131508.AIR in JIRA:TD-3969
				 */
				$FP['_PAYMNT']['new'][] = [
					'type' => 					'government',
					'accountCode' => 			$m['accountCode'],
					'travelOrderNo' => 			$m['travelOrderNo'],
					'travelOrderDateStr' => 	$m['travelOrderDate'],
				];

				$FP['type'] = 'government';
			}
			// this case is specific to Fursan, @see TD-5886: Add the possibility to interpret new form of payment, GKTKT, as government [E]
			// GRTKT
			if (!$FP['type'] && preg_match("/^(GRTKT)/", $PAYMNT, $m)) {
				/**
				 * @see 20181202 164255 21 10.FluX.02152321.air in TD-5886
				 */
				$FP['_PAYMNT']['new'][] = [
					'type' => 					'government',
				];

				$FP['type'] = 'government';
			}

			if (!$FP['type'] && preg_match("/^(O\/PO)/", $PAYMNT, $m)) {
				/**
				 * @see JIRA: TD-8914(Add the possibility to interpret a new form of payment for Amadeus bookings #63385)
				 */
				$FP['_PAYMNT']['new'][] = [
					'type' => 					'paymentOrder',
				];

				$FP['type'] = 'paymentOrder';

				$PAYMNT = '';
			}


			/***************************************************************************************************************
			 * Generalised FP parser
			 */
			if (!$FP['type']) {
				$CASH = "CASH"
						."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9\.]+))?";

				$CCVersions = [];
				/**
				 * @see RO/Happy, 2013-06-11_164541_81.12.140.206__AIR00204.Txt for an example
				 * They seem to use a different CC format for this file
				 * i dont know what 'CV' stands for in the line below
				 */
				$CCVersions['RO/Happy/2013-06-11_164541_81.12.140.206__AIR00204.Txt'] = "CC"
					."(?P<companyIndicator>[A-Z]{2})?"
					."(\/(?P<number>([0-9]+|[X]+[0-9]{4})))?"
					."D"
					."((?P<expDate>[0-9]{4}))?"
					."\*CV"
					."(\/(?P<approvalCode>[A-Z0-9]+))?";

				/**
				 * @see TravelAir Plus, amadeus_201905_20190506 155809 03 01.FluX.AIR00001.Txt for an example  @JIRA TD-5540
				 * I dont know what 'FP' stands for in the line below
				 * CC TP FP CCTP122093601833294
				 */
				$CCVersions['HU/TravelAir Plus/amadeus_201905_20190506 155809 03 01.FluX.AIR00001.Txt'] = "(CC|CA|AX|TP|VI)"
					."(?P<companyIndicator>[A-Z]{2})?"
					."(FP)";

				/**
				 * The default remark parser.
				 * This regexp was manually composed by analyzing lots of previously build regexps.
				 * The regexp should support most common formats used for this line
				 * @see SVN:46445@2013-04-18 for an example on how the original list of regexps looked like
				 */
				$CCVersions['default:strict'] = "CC"
					."(?P<companyIndicator>[A-Z]{2})?"
					//            1234XXXXXX1234       |123456XXXXX1234      |123456|XXXXXXXX1234
					."(?P<number>([0-9]{4}[X]+[0-9]{4})|[0-9]{6}[X]+[0-9]{4}|[0-9]+|[X]+[0-9]{4})?"
					."(\/(?P<expDate>[0-9]{4}))?"
					."(\/(?P<currency>" . implode("|", $definedCurrencies) . ")(?P<amount>[0-9.]+))?"
					."(\/(?P<approvalCode>[A-Z0-9]+))?"
					."(\/(?P<approvalCode2>[A-Z0-9]+))?"
					."$";

				// JIRA: TD-9719(The amount paid by CC for mixed form of payment is not determined in imported orders for an Amadeus ticket) (EC/Speed Travel)
				$CCVersions['default:looseTD9719'] = "CC"
					."(?P<companyIndicator>[A-Z]{2})?"
					//            1234XXXXXX1234       |123456XXXXX1234      |123456|XXXXXXXX1234
					."(?P<number>([0-9]{4}[X]+[0-9]{4})|[0-9]{6}[X]+[0-9]{4}|[0-9]+|[X]+[0-9]{4})?"
					."((?P<unknown01>E[0-9]{2}))"  // this not is optional, unlike in default:loose
					."(\/(?P<expDate>[0-9]{4}))?"
					."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9.]+))?"
					."(\/(?P<approvalCode>[A-Z0-9]+))?"
					."(\/(?P<approvalCode2>[A-Z0-9]+))?";

				// JIRA: TD-9739(Load the CC Approval code from the MFP line in imported order for MCO/EMDs imported from Amadeus #72279) (KR/Kyowon Travel FCM)
				$CCVersions['default:looseTD9739'] = "CC"
    			    ."(?P<companyIndicator>[A-Z]{2})?"
    		        //            1234XXXXXX1234       |123456XXXXX1234      |123456|XXXXXXXX1234
    				."(?P<number>([0-9]{4}[X]+[0-9]{4})|[0-9]{6}[X]+[0-9]{4}|[0-9]+|[X]+[0-9]{4})?"
    			    ."(\/(?P<expDate>[0-9]{4}))?"
    		        ."((?P<unknown01>(\*)E[0-9]{2}))"
    	            ."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9.]+))?"
                    ."(\/(?P<approvalCode>[A-Z0-9]+))?"
                    ."(\/(?P<approvalCode2>[A-Z0-9]+))?";

				$CCVersions['default:loose'] = "CC"
					."(?P<companyIndicator>[A-Z]{2})?"
					//            1234XXXXXX1234       |123456XXXXX1234      |123456|XXXXXXXX1234
					."(?P<number>([0-9]{4}[X]+[0-9]{4})|[0-9]{6}[X]+[0-9]{4}|[0-9]+|[X]+[0-9]{4})?"
					."(\/(?P<expDate>[0-9]{4}))?"
					."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9.]+))?"
					."(\/(?P<approvalCode>[A-Z0-9]+))?"
					."(\/(?P<approvalCode2>[A-Z0-9]+))?";

				/**
				 * @seee EE/Nautilus, 2011-08-25_112815_80.235.55.172__AIR00433.Txt.txt
				 * this file doesn't have a CC prefix preceding the card information.
				 * CA, AX, TP are common card providers, more will have to be added if this case gets encountered in the future
				 * @Apr 19, 2013 by Lucian Sirbu: i saw similar cases on AS data
				 */
				$CCVersions['EE/Nautilus/2011-08-25_112815_80.235.55.172__AIR00433.Txt.txt'] = "(CC|CA|AX|TP|VI)"
					."(?P<companyIndicator>[A-Z]{2})?"
					."(?P<number>([0-9]+|[X]+[0-9]{4}))?"
					."(\/(?P<expDate>[0-9]{4}))?"
					."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9.]+))?"
					."(\/(?P<approvalCode>[A-Z0-9]+))?"
					."(\/(?P<approvalCode2>[A-Z0-9]+))?"
					."[^A-Z]";


				$INVOICEVersions = [];
				$INVOICEVersions['RU/AMA/2013-04-03_154410_89.175.181.116__151456_00.AIR'] = "(INVOICE)"
					."\*(?P<invoiceNo>[A-Z0-9]+)"
					."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9\.]+))?";

				$INVOICEVersions['NorthStar PH/TD-5679'] = "(INVAGT)";	// don't know what it means

				$INVOICEVersions['default'] = "(INVOICE|INV)"
					."(\/(?P<currency>[A-Z]{3})(?P<amount>[0-9\.]+))?"
					."((,|\/)(?P<invoiceNo>[0-9]+))?";

				$CHECK = "CHECK";

				$MCO = "MCO";

				/**
				 * @see TD-4534: Interpret the VCN form of payment as CC when importing files from Amadeus [B]
				 * Basically we now support Virtual CC's
				 * O/VCN+/CCCA5547563461929396/0818-CVA/A116151
				 */
				$VCC = "VCN";

				// invalid data typed into this field
				$MISTYPES = [];
				$MISTYPES['default'] = "(CAS)";

				// invalid data typed into this field
				$MISTYPES_SKIP_THEM = [];
				/**
				 * TD-5190: Determine the correct form of payment for the reissues paid by CC [B]
				 * Seems that for Thomas Cook, for some reason, in the FP field for reissues, sometimes the pricing mask is reported
				 * FPO/T6/S2,3,9,10/CCAX+/CCAX377944872541002/1020/A163096
				 */
				$MISTYPES_SKIP_THEM['CZ/ThomasCook/20181228 095722 98 01.FluX.B095649_00.AIR'] = "T[0-9]+\/S[0-9,]+";


				$loops = 100;
				$PAYMNTOrig = $PAYMNT;
				$PAYMNTStr = $PAYMNT;
				$PAYMNT = [];
				$state = [];
				$state['section'] = 			'new';
				$state['operation'] = 			'';
				$state['ignoreStateChanges'] = 	0;
				while ($loops-- && $PAYMNTStr) {
					$extracted = false;
					$matches = [];
					// interpret "old" payment methods
					if (!$extracted && preg_match("/^O\//", $PAYMNTStr, $matches)) {
						if ($state['ignoreStateChanges']==0) {
							$state['section'] = 	'old';
						}
						$state['operation'] = 	'';
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
						$state['ignoreStateChanges']+= 1;
					}
					// interpret separators between payment methods
					if (!$extracted && preg_match('/^\//', $PAYMNTStr, $matches)) {
						if ($state['ignoreStateChanges']==0) {
							$state['section'] = 	'new';
						}
						$state['operation'] = 	'';
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}
					// intrepret the plus sign between payment methods
					if (!$extracted && preg_match('/^\+/', $PAYMNTStr, $matches)) {
						$state['operation'] = 'add';
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}
					if ($state['ignoreStateChanges']!=0) {
						$state['ignoreStateChanges']--;
					}



					// skip whitespaces, encountered in a case after 'CASH '
					if (!$extracted && preg_match('/^[\s]+/', $PAYMNTStr, $matches)) {
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}

					if (!$extracted && preg_match("/^{$CASH}/", $PAYMNTStr, $matches)) {
						$PAYMNT[$state['section']][] = [
							'type' => 					'cash',
							'currency' => 				$matches['currency'],
							'amount' => 				($matches['amount']!==null?(float)$matches['amount']:null),
						];
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}


					foreach ($CCVersions as $CCVKey=>$CCV) {
						if (!$extracted && preg_match("/^{$CCV}/", $PAYMNTStr, $matches)) {
							$FP['expDate'] = 	($m['expDate']?self::extractDate($m['expDate'], 'MMYY'):null);

							$PAYMNT[$state['section']][] = [
								'type' => 					'CC',
								'companyIndicator' => 		$matches['companyIndicator'],
								'number' => 				$matches['number'],
								'expDate' => 				($matches['expDate']?self::extractDate($matches['expDate'], 'MMYY'):null),
								'currency' => 				(($matches['currency']!==null && $matches['currency']!=='')?$matches['currency'] :null),
								'amount' => 				(($matches['amount']!==null && $matches['amount']!=='')?(float)$matches['amount']:null),
								'approvalCode' => 			$matches['approvalCode'],
								'_matchedFormat' => 		$CCVKey
							];
							$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
							$extracted = true;
						}
					}

					foreach ($INVOICEVersions as $key=>$INVOICE) {
						if (!$extracted && preg_match("/^{$INVOICE}/", $PAYMNTStr, $matches)) {

							$PAYMNT[$state['section']][] = [
								'type' => 					'invoice',
								'currency' => 				$matches['currency'],
								'amount' => 				($matches['amount']!==null?(float)$matches['amount']:null),
								'invoiceNo' => 				$matches['invoiceNo'],
								'_matchedFormat' => 		$key,
							];
							$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
							$extracted = true;
						}
					}

					if (!$extracted && preg_match("/^{$CHECK}/", $PAYMNTStr, $matches)) {
						$PAYMNT[$state['section']][] = [
							'type' => 					'check',
						];
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}

					if (!$extracted && preg_match("/^{$MCO}/", $PAYMNTStr, $matches)) {
						$PAYMNT[$state['section']][] = [
							'type' => 					'MCO',
						];
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}

					if (!$extracted && preg_match("/^{$VCC}/", $PAYMNTStr, $matches)) {
						$PAYMNT[$state['section']][] = [
							'type' => 					'virtual CC',
						];
						$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
						$extracted = true;
					}

					foreach ($MISTYPES as $key=>$MISTYPE) {
						if (!$extracted && preg_match("/^{$MISTYPE}/", $PAYMNTStr, $matches)) {
							$PAYMNT[$state['section']][] = [
								'type' => 					'unknown',
								'_matchedFormat' => 		$key,
							];
							$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
							$extracted = true;
						}
					}

					foreach ($MISTYPES_SKIP_THEM as $key=>$MISTYPE) {
						if (!$extracted && preg_match("/^{$MISTYPE}/", $PAYMNTStr, $matches)) {
							$PAYMNT[$state['section']][] = [
								'type' => 					'unknown',
								'_matchedFormat' => 		$key,
							];

							$PAYMNTStr = substr($PAYMNTStr, strlen($matches[0]));
							$extracted = true;
							$state['ignoreStateChanges']+= 1;
						}
					}
				}

				if ($loops==-1 && $parser) {
					$parser->addLog(__METHOD__.": unknown format for this line(FP/MFP): {$PAYMNTOrig}. Dont know how to parse starting from {$PAYMNTStr}");
				}

				$FP['_PAYMNT'] = $PAYMNT;
			}


			// CCTP122093601687427/0914/A002A/ADB - i dont know what does the ADB suffix stands for

			/*
			 * MISC tags, in custom format, these are invalid in AIR, but may have any format in IMR files
			* 	O/MS+/CCAX374398537891000/1009/A91
			* 	O/PT/+PT-AG
			* 	O/PT/+PT-CA/4377RON
			*/
		}// PAYMNT tag handling

		return $FP;
	}

	/**
	 * Check if a certain payment type was used
	 */
	static public function isPaidWith($PAYMNT, $formOfPayment)
	{
		$idx = static::getPayment($PAYMNT, $formOfPayment);
		return ($idx===null?null:true);
	}

	/**
	 * Get the used form of payment
	 * @param string $mode
	 */
	static public function getPayment($PAYMNT, $formOfPayment, $mode='new')
	{
		if ((in_array($mode, ['new', 'any'])) && $PAYMNT['new']) {
			foreach ($PAYMNT['new'] as $PAY) {
				if ($PAY['type']==$formOfPayment) {
					return $PAY;
				}
			}
		}

		if ((in_array($mode, ['old', 'any'])) && $PAYMNT['old']) {
			foreach ($PAYMNT['old'] as $PAY) {
				if ($PAY['old']==$formOfPayment) {
					return $PAY;
				}
			}
		}

		return null;
	}

}
