<?php

namespace App\Cron\Job;

/**
 * The class will import customers and corporations profiles from all configured GDS APIs
 * @author Dan Baluta
 */
class GdsProfilesImport extends \App\Cron\Job\Utils\GeneralAbstract
{
	/**
	 * Cronjob category: {system|operational|financial|reporting}
	 * @var string
	 */
	protected $category = 'operational';

	/**
	 * Cache Associations & Formats so we don't make useless requests
	 * @var array
	 */
	protected $cachedAssociations = [];
	protected $cachedFormats = [];

	/**
	 * The method returns the form which is needed for the the current cronjob settings saving.
	 */
	public function getSettingsDialog(array $fields)
	{
		$form = parent::getSettingsDialog($fields);

		$_LANG = \App\Loader_Lang::getInstance()->getGlobal();

		$fieldset = \Engine\Form\Container\Fieldset::create('Import settings');
		$form->addContainer($fieldset);

		$fld = \App\Form\Input\Select_GdsPcc::create('PCC', 'pcc', $fields['settings']['pcc']);
		$fld->setGdsFilter('sabre');
		$fld->showDefaultOption();
		$fld->setDefaultOptionText($_LANG['defaultSelectValue']['all']);
		$fld->setTooltip("Filter by profile PCC");
		$fieldset->addElement($fld);

		// TODO radio/checkboxes?
		$fldType = \Engine\Form\Input\Select::create('Profile Type', 'profileType', $fields['settings']['profileType']);
		$fldType->setPossibleValues([
		    'CRP' => 'Corporate',
			//'TVL' => 'Customer',
		]);
		$fldType->showDefaultOption();
		$fldType->setDefaultOptionText($_LANG['defaultSelectValue']['all']);
		$fldType->setTooltip("Filter by profile type");
		$fieldset->addElement($fldType);

		$fldPage = \Engine\Form\Input\Select::create('Start Page', 'startPage', $fields['settings']['startPage']);
		$fldPage->setPossibleValues([
		    '1' => '1',
		    '2' => '2',
		    '3' => '3',
		    '4' => '4',
		    '5' => '5',
		    '10' => '10',
		    '15' => '15',
		    '20' => '20',
		]);
		$fldPage->setTooltip("Start searching from this page - 100 profiles per page.");
		$fieldset->addElement($fldPage);

		$fld = \Engine\Form\Input\Text::create('Profile Name', 'profileName', $fields['settings']['profileName']);
		$fld->setTooltip("Filter by profile name");
		$fieldset->addElement($fld);

		$listObj = new \App\EntityList\GdsProfilesTemplates();
		$listObj->setColumns(['name', 'category']);
		$listObj->setOrderBy('name', 'ASC');
		$tpls = $listObj->exportRecords();

		$fldCorp = new \Engine\Form\Input\Select('Corporate Template', 'idCorporateTemplate', $fields['settings']['idCorporateTemplate']);
		foreach ($tpls as $tpl) {
		    if ($tpl['category']=='corporate') {
		        $fldCorp->setPossibleValue($tpl['id'], $tpl['name']);
		    }
		}
		$fldCorp->showDefaultOption();
		$fldCorp->setTooltip("The template that the profiles will be copied to");
		$fieldset->addElement($fldCorp);

		// TODO ACTIVATE CUSTOMERS IMPORT
// 		$fldCust = new \Engine\Form\Input\Select('Customer Template', 'idCustomerTemplate', $fields['settings']['idCustomerTemplate']);
// 		foreach ($tpls as $tpl) {
// 		    if ($tpl['category']=='customer') {
// 		        $fldCust->setPossibleValue($tpl['id'], $tpl['name']);
// 		    }
// 		}
// 		$fldCust->showDefaultOption();
// 		$fldCust->setTooltip("The template that the profiles will be copied to");
// 		$fieldset->addElement($fldCust);

		$fldType->setControlledElements([
		    'CRP' => [$fldCorp],
		    //'TVL' => [$fldCust],
		    //'' => [$fldCorp, $fldCust],
		]);

		$fieldset->groupInputsInArray("settings");

		return $form;
	}


	/**
	 * {@inheritDoc}
	 * @see \App\Cron\Job\Utils\GeneralAbstract::executeJob()
	 */
	protected function executeJob()
	{
		// force always access for this cron
		\App\UserAccess\CurrentUserAccessFast::getInstance()->becomeSuperUser();

		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

		$settings = $this->settings;

		// TEMPORARY - IMPORT PROFILES FROM SABRE API
		$ConversationId = uniqid('Import-profiles-');
		$BinarySecurityToken = \App\Sabre\Api\Soap\SessionManager::getInstance()->getBinarySecurityToken($ConversationId);
		$profileManager = \App\Sabre\Api\Soap\ProfileManager::getInstance($ConversationId, $BinarySecurityToken);

		$PCC 		 = $settings['pcc'];
		$SearchProfileType = $settings['profileType'];
		$SearchProfileName = $settings['profileName'];
		$SearchStartPage   = $settings['startPage'];
		$idCorporateTemplate = $settings['idCorporateTemplate'];
		$idCustomerTemplate  = $settings['idCustomerTemplate'];

		if ($settings['profileType'] && $settings['profileType']!='CRP' && $settings['profileType']!='TVL') {
		    $this->logger->error("You can only import corporate and customer templates.");
		    return;
		}
		if (!$settings['profileType'] || $settings['profileType']=='CRP') {
		    $tplEnt = \Entity\GdsProfileTemplate::select($idCorporateTemplate);
		    if (!$tplEnt->recordExists() || $tplEnt->deleted || $tplEnt->category!='corporate') {
		        $this->logger->error("Invalid corporate template.");
		        return;
		    }
		}
		if (!$settings['profileType'] || $settings['profileType']=='TVL') {
		    $tplEnt = \Entity\GdsProfileTemplate::select($idCustomerTemplate);
		    if (!$tplEnt->recordExists() || $tplEnt->deleted || $tplEnt->category!='customer') {
		        $this->logger->error("Invalid customer template.");
		        return;
		    }
		}

		// check mandatory remarks are defined
		$mandatoryRemarks = [
			'corporate' => [
				//'GDS_CODES'        => 'GDS CODES-[GDS_CODES]',
				//'INCLUDE_PHONE_PNR'	=> 'INCLUDE PHONE PNR-[INCLUDE_PHONE_PNR]',
			],
            'contactPerson' => [
                'CONTACT_TYPE'        => 'CONTACT TYPE-[CONTACT_TYPE]',
                'TELEPHONE_TYPE_CODE' => 'TELEPHONE TYPE CODE-[TELEPHONE_TYPE_CODE]',
                'EMAIL_TYPE_CODE'     => 'EMAIL TYPE CODE-[EMAIL_TYPE_CODE]',
		    ],
		];
		$hasMissingRemarks = false;
		foreach ($mandatoryRemarks as $category=>$formats) {
		    $remarks = \App\Config\Remarks::getInstance()->getAllRemarksFromCategory($category);
		    foreach ($formats as $var=>$format) {
		        $found = !!array_filter($remarks, function($rmk) use ($var) { return $rmk['vars'][0]['name']==$var; });
		        if (!$found) {
		            $this->logger->error(sprintf("Mandatory remark for category `%s` is not configured: `%s`", $category, $format));
		            $hasMissingRemarks = true;
		        }
		    }
		}
		if ($hasMissingRemarks) {
			return;
		}

		$this->logger->info(sprintf("Searching Sabre profiles using criteria: (PCC: %s, ProfileType: %s, ProfileName: %s). Importing on template: `%s`.", $PCC, $SearchProfileType, $SearchProfileName, $tplEnt->name));

		// Create backup dir
		$dirPath = APP_REAL_PATH . $CFG['paths']['files'] . 'sabreProfilesBackup';
		if (!\Engine\Dir::check($dirPath)) {
		    if (!\Engine\Dir::create($dirPath)) {
		        $this->logger->error(sprintf("The folder `%s` could not be created, probably due to file access.", $dirPath));
		        return;
		    }
		}

		// pagination - 100 per page
		$CurrPageNumber = $SearchStartPage ? $SearchStartPage-1 : 0;
		$ResultsPerPage = 100;
		$HaveMore = null;
		while ($HaveMore!='N') {
			$CurrPageNumber++;

			$this->logger->info(sprintf("Importing profiles: (PCC: %s, ProfileType: %s, ProfileName: %s, Page: %s, Results: %s)", $PCC, $SearchProfileType, $SearchProfileName, $CurrPageNumber, $ResultsPerPage));

			$response = $profileManager->searchProfiles($ConversationId, $BinarySecurityToken, $PCC, $SearchProfileType, $SearchProfileName, $CurrPageNumber, $ResultsPerPage);
			//debug($response->asPretty()); die;

			if ((String)$response['NumReturned'] && (String)$response['PageNumber']) {   // multiple profiles found
			    $NumReturned = (String)$response['NumReturned'];
			    $HaveMore    = (String)$response['HaveMore'];
			    $PageNumber  = (String)$response['PageNumber'];
			    $TotalCount  = (String)$response['TotalCount'];	 // returned only on the 1st page

			    if ($TotalCount) {
			        $this->logger->info(sprintf("Found %s profiles.", $TotalCount));
			    }
			}
			else {   // only 1 pofile found
			    $NumReturned = 1;
			    $HaveMore = 'N';
			    $PageNumber = 1;
			    $this->logger->info("Found 1 profiles.");
			}
			$this->logger->info(sprintf("Importing %s profiles (page %s):", $NumReturned, $PageNumber));

			// group profiles by 10
			$profilesArr = [];
			foreach ($response->Profile as $Profile) {
			    // skip deleted
			    if ((String)$Profile->TPA_Identity['ProfileStatusCode']=='DL') {
			        $this->logger->warning(sprintf("Skipped deleted profile `%s`, Id: `%s`.", (String)$Profile->TPA_Identity['ProfileName'], (String)$Profile->TPA_Identity['UniqueID'],));
			        continue;
			    }
				$profilesArr[] = [
					'UniqueID' => (String)$Profile->TPA_Identity['UniqueID'],
					'ProfileTypeCode' => (String)$Profile->TPA_Identity['ProfileTypeCode'],
					'DomainID' => (String)$Profile->TPA_Identity['DomainID'],
				];
			}
			$profilesSplit = array_chunk($profilesArr, 10);

			foreach ($profilesSplit as $profiles10) {

				// get profile details (bulk read 10)
				$Profs = $profileManager->bulkReadProfiles($ConversationId, $BinarySecurityToken, $profiles10);
				//debug($Profs->asPretty()); die;

				/**
				 * @var \Engine\SimpleXmlElement $Prof
				 */
				foreach ($Profs->Profile as $Prof) {

					$UniqueID 		 = (String)$Prof->TPA_Identity['UniqueID'];
					$ProfileTypeCode = (String)$Prof->TPA_Identity['ProfileTypeCode'];
					$DomainID		 = (String)$Prof->TPA_Identity['DomainID'];
					$ProfileName     = (String)$Prof->TPA_Identity['ProfileName'];

					// BACKUP PROFILE XML FIRST
					$filePath = $dirPath.'/'.sprintf("%s_%s_%s_%s_%s", $UniqueID, \Engine\DateTime::create()->format("Ymd_His"), $DomainID, $ProfileTypeCode, preg_replace("/[^A-Za-z0-9 ]/", '', $ProfileName)).'.xml';
					$create = \Engine\File::create($filePath, $Prof->asPretty());
					if (!$create) {
					    $this->logger->error(sprintf("Could not backup template `%s`, probably due to file access.", $UniqueID));
					    return;
					}

					if ($ProfileTypeCode=='TVL') {

					    // skip travelers for now - they will be imported later
					    continue;

						// search customer
						$idCustomer = $profileManager->getCustomerId($UniqueID);
						$custEnt = \Entity\Customer::select($idCustomer);

						if ($custEnt->recordExists() && !$custEnt->deleted && !$custEnt->idGdsProfileTemplate) {
							$this->logger->error(sprintf("Customer `%s` is linked to Sabre profile `%s` but no GDS template is defined for it. Cannot update fields.", $custEnt->name, $UniqueID));
							continue;
						}

						if ($custEnt->recordExists() && !$custEnt->deleted) {
							// update
							$tplEnt = $custEnt->gdsProfileTemplate;
							$fields = $this->getCustomerFields($Prof, $tplEnt, $custEnt);

							\DB::startTransaction();
							try {
								$entity = \Entity\Customer::update($custEnt->getId());
								$entity->firstNameStandard  = $fields['entityFields']['firstNameStandard'];
								$entity->lastNameStandard   = $fields['entityFields']['lastNameStandard'];
								$entity->middleNameStandard = $fields['entityFields']['middleNameStandard'];
								$entity->firstNameLocal 	= '';
								$entity->lastNameLocal   	= '';
								$entity->middleNameLocal 	= '';
								$entity->type   		 	= $fields['entityFields']['type'] ?: '';
								$entity->comments   	  	= $fields['entityFields']['comments'] ?: '';
								$entity->birthday   	  	= $fields['entityFields']['birthday'] ?: null;
								$entity->telephone   	  	= $fields['entityFields']['telephone'] ?: '';
								$entity->telephoneMobile  	= $fields['entityFields']['telephoneMobile'] ?: '';
								$entity->email  			= $fields['entityFields']['email'] ?: '';
								$entity->address  			= $fields['entityFields']['address'] ?: '';
								$entity->deliveryAddress  	= $fields['entityFields']['deliveryAddress'] ?: '';
								$entity->zipCode  			= $fields['entityFields']['zipCode'] ?: '';
								// TODO match city
								$entity->countryCode 		= $fields['entityFields']['countryCode'] ?: '';
								$entity->ID_number			= $fields['entityFields']['ID_number'] ?: '';
								$entity->ID_expireDate		= $fields['entityFields']['ID_expireDate'] ?: null;
								//$entity->ID_countryCode		= $fields['entityFields']['ID_countryCode'];  // TODO
								$entity->passports			= $fields['entityFields']['documents'] ?: [];
								$entity->gdsProfileFieldsValues = $fields['profileFields'];
								$entity->save();

								\App\EntityPreference\Customer::getInstance($entity->getId())->gdsProfileSabrePcc = $DomainID;

								\DB::commitTransaction();
								$this->logger->success(sprintf("Customer `%s` (profile `%s`, PCC `%s`) was successfully updated.", $custEnt->name, $UniqueID, $DomainID));
							}
							catch (\App\Entity\Exception $e) {
								\DB::rollbackTransaction();
								$this->logger->error(sprintf("Customer `%s` (profile `%s`, PCC `%s`) could not be updated:", $custEnt->name, $UniqueID, $DomainID));
								$this->importFromMessageCollector($e->getMessagesCollector());
							}
						}
						else {
							// insert
							$idFirstTemplate = 11;  // TODO
							$tplEnt = \Entity\GdsProfileTemplate::select($idFirstTemplate);
							$fields = $this->getCustomerFields($Prof, $tplEnt);

							\DB::startTransaction();
							try {
								$entity = \Entity\Customer::insert();
								$entity->firstNameStandard  = $fields['entityFields']['firstNameStandard'];
								$entity->lastNameStandard   = $fields['entityFields']['lastNameStandard'];
								$entity->middleNameStandard = $fields['entityFields']['middleNameStandard'];
								$entity->firstNameLocal 	= '';
								$entity->lastNameLocal   	= '';
								$entity->middleNameLocal 	= '';
								$entity->type   			= $fields['entityFields']['type'] ?: '';
								$entity->comments   		= $fields['entityFields']['comments'] ?: '';
								$entity->birthday   		= $fields['entityFields']['birthday'] ?: null;
								$entity->telephone   	  	= $fields['entityFields']['telephone'] ?: '';
								$entity->telephoneMobile  	= $fields['entityFields']['telephoneMobile'] ?: '';
								$entity->email  			= $fields['entityFields']['email'] ?: '';
								$entity->address  			= $fields['entityFields']['address'] ?: '';
								$entity->deliveryAddress  	= $fields['entityFields']['deliveryAddress'] ?: '';
								$entity->zipCode  			= $fields['entityFields']['zipCode'] ?: '';
								// TODO match city
								$entity->countryCode 		= $fields['entityFields']['countryCode'] ?: '';
								$entity->ID_number			= $fields['entityFields']['ID_number'] ?: '';
								$entity->ID_expireDate		= $fields['entityFields']['ID_expireDate'] ?: null;
								//$entity->ID_countryCode		= $fields['entityFields']['ID_countryCode'];  // TODO
								$entity->passports			= $fields['entityFields']['documents'] ?: [];
								$entity->idGdsProfileTemplate = $idFirstTemplate;
								$entity->save();

								$entity = \Entity\Customer::update($entity->getId());
								$entity->gdsProfileFieldsValues = $fields['profileFields'];
								$entity->save();

								\App\EntityPreference\Customer::getInstance($entity->getId())->gdsProfileSabreId = $UniqueID;
								\App\EntityPreference\Customer::getInstance($entity->getId())->gdsProfileSabrePcc = $DomainID;

								\DB::commitTransaction();
								$this->logger->success(sprintf("Customer `%s` (profile `%s`, PCC `%s`) was successfully inserted.", sprintf("%s %s", $fields['entityFields']['firstNameStandard'], $fields['entityFields']['lastNameStandard']), $UniqueID, $DomainID));
							}
							catch (\App\Entity\Exception $e) {
								\DB::rollbackTransaction();
								$this->logger->error(sprintf("Customer `%s` (profile `%s`, PCC `%s`) could not be inserted:", sprintf("%s %s", $fields['entityFields']['firstNameStandard'], $fields['entityFields']['lastNameStandard']), $UniqueID, $DomainID));
								$this->importFromMessageCollector($e->getMessagesCollector());
							}
						}

					}
					elseif ($ProfileTypeCode=='CRP') {
					    //debug($Prof->asPretty()); die;

						// search corporate
						$idCorporate = $profileManager->getCorporateId($UniqueID);
						$corpEnt = \Entity\Corporate::select($idCorporate);

						if ($corpEnt->recordExists() && !$corpEnt->deleted && !$corpEnt->idGdsProfileTemplate) {
							$this->logger->error(sprintf("Company `%s` is linked to Sabre profile `%s` but no GDS template is defined for it. Cannot update fields.", $corpEnt->alias, $UniqueID));
							continue;
						}

						if ($corpEnt->recordExists() && !$corpEnt->deleted) {
							// update
							\DB::startTransaction();
							try {
							    $tplEnt = $corpEnt->gdsProfileTemplate;
							    $msgs = new \App\MessagesCollector();
							    $fields = $this->getCorporateFields($Prof, $tplEnt, $corpEnt, $msgs);
							    //var_dump($fields); die;

							    if ($msgs->hasErrorOrWarning()) {
							    	throw new \App\MessagesException($msgs);
							    }

							    // match city
							    // TODO cache cities
							    $idCity = null;
							    if ($fields['entityFields']['address']['cityName']) {
    							    $matcher = new \App\EntityMatch\City("intersect");
    							    $matcher->setName($fields['entityFields']['address']['cityName']);
    							    if ($fields['entityFields']['address']['countryCode']) {
    			                         $matcher->filterByCountryCode($fields['entityFields']['address']['countryCode']);
    							    }
    							    $matcher->search();
    							    $idCity = $matcher->getFirstMatchDetails()['id'];
							    }

								$entity = \Entity\Corporate::update($idCorporate);
								$entity->alias = trim($fields['entityFields']['alias']);
								$entity->companyStandard = trim($fields['entityFields']['company']);
								$entity->registryAddress = $fields['entityFields']['address']['address'];
								$entity->telephone = $fields['entityFields']['telephone'] ?: '';
								$entity->email = $fields['entityFields']['email'] ?: '';
								$entity->fiscalCode = $fields['entityFields']['fiscalCode'] ?: '';
								$entity->comments = $fields['entityFields']['comments'] ?: '';
								$entity->gdsProfileFieldsValues = $fields['profileFields'];
								$remarks = "";
								if ($fields['entityFields']['profileIdRemark']) {
								    $remarks .= "PROFILE ID-".$fields['entityFields']['profileIdRemark']."\n";
								}
								if ($fields['entityFields']['dkNumberAdvancedRemark']) {
								    $remarks .= "DK NUMBER-".$fields['entityFields']['dkNumberAdvancedRemark']."\n";
								    if ($fields['entityFields']['dkNumberRemark']) {
								        $remarks .= "DK NUMBER OLD-".$fields['entityFields']['dkNumberRemark']."\n";
								    }
								}
								elseif ($fields['entityFields']['dkNumberRemark']) {
								    $remarks .= "DK NUMBER-".$fields['entityFields']['dkNumberRemark']."\n";
								}
								$entity->remarks = trim($remarks);
								$entity->save();

								$idMainOffice = $corpEnt->idMainOffice;
								$corpOffEnt = \Entity\CorporateOffice::update($idMainOffice);
								$corpOffEnt->address = $fields['entityFields']['address']['address'];
								$corpOffEnt->zipCode = $fields['entityFields']['address']['zipCode'];
								$corpOffEnt->cityId = (int)$idCity;
								$corpOffEnt->countryCode = (String)$fields['entityFields']['address']['countryCode'];
								$corpOffEnt->deliveryAddress = (String)$fields['entityFields']['deliveryAddress']['address'];
								$corpOffEnt->comments = (String)$fields['entityFields']['address']['comments'];
								$corpOffEnt->save();

								// update/insert contact persons
								// TODO if the name changed in Sabre, the contact will be added again in TINA
								if ($fields['entityFields']['contactPersons']) {
    								foreach ($fields['entityFields']['contactPersons'] as $contact) {
    								    $found = false;
    								    foreach ($corpEnt->contactPersonsList as $id=>$contDet) {
    								        if ($contact['firstName']==$contDet['firstName'] && $contact['lastName']==$contDet['lastName']) {
    								            $found = true;
    								            break;
    								        }
    								    }

    								    if ($found) {
    								        $contEnt = \Entity\ContactPerson::update($id);
    								    }
    								    else {
    								        $contEnt = \Entity\ContactPerson::insert();
    								        $contEnt->idCompany = $corpEnt->getId();
    								        $contEnt->type = 'corporate';
    								    }
    								    $contEnt->firstName = (String)$contact['firstName'];
    								    $contEnt->lastName = (String)$contact['lastName'];
    								    $contEnt->email = (String)$contact['email'];
    								    $contEnt->telephone = (String)$contact['telephone'];
    								    $contEnt->title = (String)$contact['title'];
    								    $remarks = "CONTACT TYPE-".$contact['CONTACT_TYPE'];        // TODO update: merge with existing remarks
    								    $remarks .= "\nEMAIL TYPE CODE-".$contact['EMAIL_TYPE_CODE'];
    								    $remarks .= "\nTELEPHONE TYPE CODE-".$contact['TELEPHONE_TYPE_CODE'];
    								    $contEnt->remarks = trim($remarks);
    								    $contEnt->save();
    								}
								}

								\App\EntityPreference\Corporate::getInstance($entity->getId())->gdsProfileSabrePcc = $DomainID;

								\DB::commitTransaction();
								$this->logger->success(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) was successfully updated.", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
							}
							catch (\App\Entity\Exception $e) {
								\DB::rollbackTransaction();
								$this->logger->error(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) could not be updated:", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
								$this->importFromMessageCollector($e->getMessagesCollector());
							}
							catch (\App\MessagesException $e) {
								\DB::rollbackTransaction();
								$this->logger->error(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) could not be updated:", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
								$this->importFromMessageCollector($e->getMessagesCollector());
							}
						}
						else {
							// insert
							\DB::startTransaction();
							try {
							    $idFirstTemplate = $idCorporateTemplate;
							    $tplEnt = \Entity\GdsProfileTemplate::select($idFirstTemplate);
							    $msgs = new \App\MessagesCollector();
							    $fields = $this->getCorporateFields($Prof, $tplEnt, null, $msgs);
							    //var_dump($fields); die;

							    if ($msgs->hasErrorOrWarning()) {
							    	throw new \App\MessagesException($msgs);
							    }

							    // match city
							    // TODO cache cities
							    $idCity = null;
							    if ($fields['entityFields']['address']['cityName']) {
							        $matcher = new \App\EntityMatch\City("intersect");
							        $matcher->setName($fields['entityFields']['address']['cityName']);
							        if ($fields['entityFields']['address']['countryCode']) {
							            $matcher->filterByCountryCode($fields['entityFields']['address']['countryCode']);
							        }
							        $matcher->search();
							        $idCity = $matcher->getFirstMatchDetails()['id'];
							    }

								$entity = \Entity\Corporate::insert();
								$corpOffEnt = \Entity\CorporateOffice::insert();
								$entity->status = 'CLIENT';
								$entity->type = 'CC';
								$entity->alias = trim($fields['entityFields']['alias']);
								$entity->companyStandard = trim($fields['entityFields']['company']);
								$entity->registryAddress = $fields['entityFields']['address']['address'];
								$entity->telephone = $fields['entityFields']['telephone'] ?: '';
								$entity->email = $fields['entityFields']['email'] ?: '';
								$entity->fiscalCode = $fields['entityFields']['fiscalCode'] ?: '';
								$entity->comments = $fields['entityFields']['comments'] ?: '';
								$corpOffEnt->name = $fields['entityFields']['alias'];
								$corpOffEnt->address = $fields['entityFields']['address']['address'];
								$corpOffEnt->zipCode = $fields['entityFields']['address']['zipCode'];
                                $corpOffEnt->cityId = (int)$idCity;
								$corpOffEnt->countryCode = (String)$fields['entityFields']['address']['countryCode'];
								$corpOffEnt->deliveryAddress = (String)$fields['entityFields']['deliveryAddress']['address'];
								$corpOffEnt->comments = (String)$fields['entityFields']['address']['comments'];
								$entity->office = $corpOffEnt;
								$entity->idGdsProfileTemplate = $idFirstTemplate;
								$remarks = "";
								if ($fields['entityFields']['profileIdRemark']) {
								    $remarks .= "PROFILE ID-".$fields['entityFields']['profileIdRemark']."\n";
								}
								if ($fields['entityFields']['dkNumberAdvancedRemark']) {
								    $remarks .= "DK NUMBER-".$fields['entityFields']['dkNumberAdvancedRemark']."\n";
								    if ($fields['entityFields']['dkNumberRemark']) {
								        $remarks .= "DK NUMBER OLD-".$fields['entityFields']['dkNumberRemark']."\n";
								    }
								}
								elseif ($fields['entityFields']['dkNumberRemark']) {
								    $remarks .= "DK NUMBER-".$fields['entityFields']['dkNumberRemark']."\n";
								}
								$entity->remarks = trim($remarks);
								$entity->save();

								// insert contact persons
								if ($fields['entityFields']['contactPersons']) {
								    foreach ($fields['entityFields']['contactPersons'] as $contact) {
								        $contEnt = \Entity\ContactPerson::insert();
								        $contEnt->type = 'corporate';
								        $contEnt->idCompany = $entity->getId();
								        $contEnt->firstName = $contact['firstName'];
								        $contEnt->lastName = $contact['lastName'];
								        if ($contact['email']) {
								            $contEnt->email = $contact['email'];
								        }
								        if ($contact['telephone']) {
								            $contEnt->telephone = $contact['telephone'];
								        }
								        if ($contact['title']) {
								            $contEnt->title = $contact['title'];
								        }
								        $remarks = "CONTACT TYPE-".$contact['CONTACT_TYPE'];
								        $remarks .= "\nEMAIL TYPE CODE-".$contact['EMAIL_TYPE_CODE'];
								        $remarks .= "\nTELEPHONE TYPE CODE-".$contact['TELEPHONE_TYPE_CODE'];
								        $contEnt->remarks = trim($remarks);
								        $contEnt->save();
								    }
								}

								$entity = \Entity\Corporate::update($entity->getId());
								$entity->gdsProfileFieldsValues = $fields['profileFields'];
								$entity->save();

								\App\EntityPreference\Corporate::getInstance($entity->getId())->gdsProfileSabreId = $UniqueID;
								\App\EntityPreference\Corporate::getInstance($entity->getId())->gdsProfileSabrePcc = $DomainID;

								\DB::commitTransaction();
								$this->logger->success(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) was successfully inserted.", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
							}
							catch (\App\Entity\Exception $e) {
								\DB::rollbackTransaction();
								$this->logger->error(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) could not be inserted:", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
								$this->importFromMessageCollector($e->getMessagesCollector());
							}
							catch (\App\MessagesException $e) {
							    \DB::rollbackTransaction();
							    $this->logger->error(sprintf("Company `%s` (profile name `%s`, ID `%s`, PCC `%s`) could not be inserted:", $fields['entityFields']['company'], $ProfileName, $UniqueID, $DomainID));
							    $this->importFromMessageCollector($e->getMessagesCollector());
							}
						}

					}
					else {
						// other profile type
						throw new \Exception("You can only import corporate and customer templates.");
					}

				}

				// wait 2 sec
				sleep(2);
			}

			// wait 2 sec
			sleep(2);
		}

		\App\UserAccess\CurrentUserAccessFast::getInstance()->becomeNormalUser();
	}


	protected function getCorporateFields(\Engine\SimpleXmlElement $Profile, \Entity\GdsProfileTemplate $tplEnt, ?\Entity\Corporate $corpEnt, \App\MessagesCollector $msgs)
	{
	    $profileManager = \App\Sabre\Api\Soap\ProfileManager::getInstance();

	    $entityFields  = [];

		$UniqueID 		 	= (String)$Profile->TPA_Identity['UniqueID'];
		$DomainID		 	= (String)$Profile->TPA_Identity['DomainID'];
		//$ProfileTypeCode 	= (String)$Profile->TPA_Identity['ProfileTypeCode'];        // CRP

		$ProfileName		= (String)$Profile->TPA_Identity['ProfileName'];
		//$parsedProfileName  = false;

		$ProfileStatusCode 	= (String)$Profile->TPA_Identity['ProfileStatusCode'];		// AC - active, MG - merged, DL - deleted
		$parsedStatus       = false;

		$ProfileDescription = (String)$Profile->TPA_Identity['ProfileDescription'];
		//$parsedDescription  = false;

		$CorporationName 	= (String)$Profile->Corporation->CorporateInfo['CorporationName'];
		$CorporationTypeCode= (String)$Profile->Corporation->CorporateInfo['CorporationTypeCode'];	// GLB, LRG, MED, SML, TRB
		$parsedCorpType     = false;

		$TaxID              = null;
		if ($Profile->Corporation->CorporateInfo->TaxInfo) {
		    foreach ($Profile->Corporation->CorporateInfo->TaxInfo as $TaxInfo) {
		        if ((String)$TaxInfo['OrderSequenceNo']==1) {
		            $TaxID = (String)$TaxInfo['TaxID'];
		            break;
		        }
		    }
		}

		$BranchID 			= (String)$Profile->Corporation->CustomerReferenceInfo['BranchID'];			// DK
		$ReferenceID 		= (String)$Profile->Corporation->CustomerReferenceInfo['ReferenceID'];		// NUMBR
        $parsedDkNumber     = false;

		$PriorityRemarks	= (String)$Profile->Corporation->PriorityRemarks['Text'];
        $parsedPriority     = false;

		$telephonesArr = [];
		$parsedTelephones = [];
		if ($Profile->Corporation->Telephone) {
		    foreach ($Profile->Corporation->Telephone as $Telephone) {
		        if ((String)$Telephone->FullPhoneNumber) {
		            $telephoneNumber = (String)$Telephone->FullPhoneNumber;
		        }
		        elseif ($Telephone->ParsedPhoneNumber) {
		            $telephoneNumber = (String)$Telephone->ParsedPhoneNumber['CountryCd']
		            .(String)$Telephone->ParsedPhoneNumber['AreaCd']
		            .(String)$Telephone->ParsedPhoneNumber['PhoneNumber'];
		        }

		        $telephonesArr[(int)$Telephone['OrderSequenceNo']] = [
		            'OrderSequenceNo' => (int)$Telephone['OrderSequenceNo'],
		            'FullPhoneNumber' => $telephoneNumber,
		            'LocationTypeCode' => (String)$Telephone['LocationTypeCode'],
		            'PhoneCityCode' => (String)$Telephone['PhoneCityCode'],
		            'InformationText' => (String)$Telephone['InformationText'],
		            'PNRTelephoneTagIndicator' => (String)$Telephone['PNRTelephoneTagIndicator'],
		        ];
		    }
		}
		ksort($telephonesArr);

		$emailsArr = [];
		$parsedEmails = [];
		if ($Profile->Corporation->Email) {
		    foreach ($Profile->Corporation->Email as $Email) {
		        $emailsArr[(int)$Email['OrderSequenceNo']] = [
		            'OrderSequenceNo' => (int)$Email['OrderSequenceNo'],
		            'EmailTypeCode' => (String)$Email['EmailTypeCode'],
		            'EmailAddress' => (String)$Email['EmailAddress'],
		            'EmailRemark' => (String)$Email['EmailRemark'],
		        ];
		    }
		}
		ksort($emailsArr);

		$remarksArr = [];
		$parsedRemarks = [];
		if ($Profile->Corporation->Remark) {
		    foreach ($Profile->Corporation->Remark as $Remark) {
		        if ((String)$Remark['CategoryCode']=='PNR') {
		            $remarksArr[(int)$Remark['OrderSequenceNo']] = [
		                'OrderSequenceNo' => (int)$Remark['OrderSequenceNo'],
		                'Text' => (String)$Remark['Text'],
		                'CategoryCode' => (String)$Remark['CategoryCode'],
		                'TypeCode' => (String)$Remark['TypeCode'],
		            ];
		        }
		    }
		}
		ksort($remarksArr);

		// The first telephone is synced with TINA telephone
		if ($telephonesArr[1]) {
		    $entityFields['telephone'] = $telephonesArr[1]['FullPhoneNumber'];
		    $parsedTelephones[] = $telephonesArr[1]['OrderSequenceNo'];
		}

		// The first email is synced with TINA email
		if ($emailsArr[1]) {
		    $entityFields['email'] = $emailsArr[1]['EmailAddress'];
		    $parsedEmails[] = $emailsArr[1]['OrderSequenceNo'];
		}

		$Addresses			= $Profile->Corporation->Address;

		//$AssociatedFilters  = $Profile->Corporation->AssociatedFilters;

		$entityFields['alias'] 	  = trim(strtoupper($ProfileName));
		$entityFields['company']  = $CorporationName ? trim(strtoupper($CorporationName)) : trim(strtoupper($ProfileName));
		$entityFields['comments'] = $ProfileDescription;
		$entityFields['fiscalCode'] = $TaxID;

		// TODO Forms of payment
		$FormOfPayment = null;
		if ($Profile->Corporation->PaymentForm) {
			foreach ($Profile->Corporation->PaymentForm as $Fop) {
				if ($Fop->Cash) {
					$FormOfPayment = 'Cash';
				}
				if ($Fop->Check) {
					$FormOfPayment = (String)$Fop->Check['PNRCheckCode'];	// CK, CHECK, CHECQUE
				}
				if ($Fop->AccountsReceivable) {	// Invoice
					$FormOfPayment = 'Invoice';
				}
				if ($Fop->PaymentCard) {
					$FormOfPayment = 'CreditCard';
				}
			}
		}

		// contact persons
		$titles = [
			'mr'   => 'm',
			'mrs'  => 'f',
			'miss' => 's',
			'ms'   => 's',
			'mstr' => 't',
		];

		$contactPersons = [];
		if ($Profile->Corporation->EmergencyContactPerson) {
		    foreach ($Profile->Corporation->EmergencyContactPerson as $EmergencyContactPerson) {
		        $key = sprintf("%s-%s", (String)$EmergencyContactPerson->SurName, (String)$EmergencyContactPerson->GivenName);

		        $contactPersons[$key]['firstName'] = (String)$EmergencyContactPerson->GivenName;
		        $contactPersons[$key]['lastName'] = (String)$EmergencyContactPerson->SurName;
		        $contactPersons[$key]['email'] = $EmergencyContactPerson->Email ? (String)$EmergencyContactPerson->Email['EmailAddress'] : '';
		        $contactPersons[$key]['telephone'] = $EmergencyContactPerson->Telephone ? (String)$EmergencyContactPerson->Telephone->FullPhoneNumber : '';
		        $contactPersons[$key]['CONTACT_TYPE'] = 'EMERGENCY';
		        $contactPersons[$key]['EMAIL_TYPE_CODE'] = $EmergencyContactPerson->Email ? (String)$EmergencyContactPerson->Email['EmailTypeCode'] : '';
		        $contactPersons[$key]['TELEPHONE_TYPE_CODE'] = $EmergencyContactPerson->Telephone ? (String)$EmergencyContactPerson->Telephone['LocationTypeCode'] : '';
		    }
		}
		if ($Profile->Corporation->ContactName) {
		    foreach ($Profile->Corporation->ContactName as $ContactName) {
		        $key = sprintf("%s-%s", (String)$ContactName['SurName'], (String)$ContactName['GivenName']);

		        if (!$contactPersons[$key]['firstName']) {
		            $contactPersons[$key]['firstName'] = (String)$ContactName['GivenName'];
		        }
		        if (!$contactPersons[$key]['lastName']) {
		            $contactPersons[$key]['lastName'] = (String)$ContactName['SurName'];
		        }
		        if (!$contactPersons[$key]['title']) {
		            $contactPersons[$key]['title'] = $titles[strtolower((String)$ContactName['NamePrefix'])];
		        }
		        if (!$contactPersons[$key]['CONTACT_TYPE']) {
		            $contactPersons[$key]['CONTACT_TYPE'] = 'COMPANY';
		        }
		    }
		}
		$entityFields['contactPersons'] = $contactPersons;

		// profile ID remark
		$entityFields['profileIdRemark'] = $UniqueID;

		// DK number remark
		$dkRemarks = $this->remarksThatStartsWith('5X/-DK NUMBER-', $remarksArr);
		$dkRemark = reset($dkRemarks);
		if ($dkRemark) {
		    $entityFields['dkNumberRemark'] = substr($dkRemark['Text'], 14);
		}

		// DK number remark - advanced formats
		if ($Profile->Association) {
		    foreach ($Profile->Association as $Assoc) {
		        // read the DK from advanced format (Association->AssociatedFormats)
		        // if found it takes preference over the 5X/ remark
		        if ($this->cachedAssociations[(int)$Assoc['AssociationID']]) {
		            $Association = $this->cachedAssociations[(int)$Assoc['AssociationID']];
		        }
		        else {
		            $Association = $profileManager->readAssociation((int)$Assoc['AssociationID'], (String)$Assoc['DomainID']);
		            $this->cachedAssociations[(int)$Assoc['AssociationID']] = $Association;
		        }

		        if ($Association->AssociatedFormats) {
		            foreach ($Association->AssociatedFormats as $AssocFormat) {
		                if ($this->cachedFormats[(int)$AssocFormat['FormatID']]) {
		                    $Format = $this->cachedFormats[(int)$AssocFormat['FormatID']];
		                }
		                else {
                            $Format = $profileManager->readFormat((int)$AssocFormat['FormatID'], (String)$AssocFormat['DomainID']);
                            $this->cachedFormats[(int)$AssocFormat['FormatID']] = $Format;
		                }
		                $FormatData = (String)$Format['FormatData'];
		                if ($FormatData) {
		                    // search texts that are concatenated
		                    $substr = strstr($FormatData, 'concat("X/-DK NUMBER');
		                    $substr = strstr($substr, 'concat((//Profile/Corporation/CustomerReferenceInfo', true);
		                    $split = null;
		                    preg_match_all('/"(.*?)"/', $substr, $split);
		                    if ($split[1] && $BranchID && $ReferenceID) {
		                        $dkNumberFormat = implode("", $split[1]).substr($BranchID.$ReferenceID, 1);
		                        $entityFields['dkNumberAdvancedRemark'] = substr($dkNumberFormat, 13);
		                    }
		                }
		            }
		        }
		    }
		}

		$fieldsDetails    = $tplEnt->staticFieldsDetails;
		uasort($fieldsDetails, function($f1, $f2) {		// reorder alphabetically, maintain index
			return strnatcmp($f1["label"], $f2["label"]);
		});
		$idProfileFields  = array_keys($fieldsDetails);
		$gdsProfileFields = $corpEnt ? $corpEnt->gdsProfileFieldsValues : [];

		$profileFields = [];
		foreach ($idProfileFields as $idField) {
			$profileFields[$idField] = $gdsProfileFields[$idField] ?: '';

			// ID
			if ($fieldsDetails[$idField]['tagSabre']=='UniqueID') {
				$profileFields[$idField] = $UniqueID;
			}

			// PCC
			if ($fieldsDetails[$idField]['tagSabre']=='PCC') {
				$profileFields[$idField] = $DomainID;
			}

			// profile name
// 			if ($fieldsDetails[$idField]['tagSabre']=='ProfileName') {
// 				$profileFields[$idField] = $ProfileName;
// 				$parsedProfileName = true;
// 			}

			// profile description
// 			if ($fieldsDetails[$idField]['tagSabre']=='ProfileDescription') {
// 				$profileFields[$idField] = $ProfileDescription;
// 				$parsedDescription = true;
// 			}

			// profile status
			if ($fieldsDetails[$idField]['tagSabre']=='ProfileStatusCode') {
			    $profileFields[$idField] = $ProfileStatusCode;
			    $parsedStatus = true;
			}

			// corporation type
			if ($fieldsDetails[$idField]['tagSabre']=='CorporationTypeCode') {
				$profileFields[$idField] = $CorporationTypeCode;
				$parsedCorpType = true;
			}

			// DK number (BranchID+ReferenceID)
			if ($fieldsDetails[$idField]['tagSabre']=='DkNumber') {
				$profileFields[$idField] = $BranchID.$ReferenceID;
				$parsedDkNumber = true;
			}

			// Remark starts with (value only)
			if (substr($fieldsDetails[$idField]['tagSabre'], 0, 17)=='RemarkStartsWith:') {
				$startsWith = substr($fieldsDetails[$idField]['tagSabre'], 17);
				$rmks = $this->remarksThatStartsWith($startsWith, $remarksArr);
				foreach ($rmks as $sequence=>$rmk) {
    				if (!in_array($sequence, $parsedRemarks)) {
    					$profileFields[$idField] = substr($rmk['Text'], strlen($startsWith));
    					$parsedRemarks[] = $sequence;
    					break;
    				}
				}
			}

			// Priority remarks
			if ($fieldsDetails[$idField]['tagSabre']=='PriorityRemarks') {
				$profileFields[$idField] = $PriorityRemarks;
				$parsedPriority = true;
			}

			// Form of Payment  TODO multiple, complex
			if ($fieldsDetails[$idField]['tagSabre']=='FormOfPayment' && $FormOfPayment) {	// TODO WHY blank?
				$profileFields[$idField] = $FormOfPayment;
			}

			// Telephones
			if ($fieldsDetails[$idField]['tagSabre']=='Telephone') {
			    foreach ($telephonesArr as $sequence=>$tel) {
			        if (!in_array($sequence, $parsedTelephones)) {
			            $profileFields[$idField] = sprintf("%s:%s:%s:%s:%s", $tel['FullPhoneNumber'], $tel['LocationTypeCode'], $tel['PhoneCityCode'], $tel['InformationText'], $tel['PNRTelephoneTagIndicator']);
			            $parsedTelephones[] = $sequence;
			            break;
			        }
			    }
			}

			// Emails
			if ($fieldsDetails[$idField]['tagSabre']=='Email') {
			    foreach ($emailsArr as $sequence=>$eml) {
			        if (!in_array($sequence, $parsedEmails)) {
    			        $profileFields[$idField] = sprintf("%s:%s:%s", $eml['EmailAddress'], $eml['EmailTypeCode'], $eml['EmailRemark']);
    			        $parsedEmails[] = $sequence;
    			        break;
			        }
			    }
			}

			// Other PNR Move Data
			if ($fieldsDetails[$idField]['tagSabre']=='OtherPnrMoveData') {
			    foreach ($remarksArr as $sequence=>$rmk) {
			        if (!in_array($sequence, $parsedRemarks)) {
			            $profileFields[$idField] = $rmk['Text'];
			            $parsedRemarks[] = $sequence;
			            break;
			        }
			    }
			}
		}

		// chek profile name field
// 		if ($ProfileName && !$parsedProfileName) {
// 		    $msgs->addError(sprintf("Profile Name could not be saved, please define `ProfileName` field: `%s`", $ProfileName));
// 		}

		// check description field
// 		if ($ProfileDescription && !$parsedDescription) {
// 		    $msgs->addError(sprintf("Profile Description could not be saved, please define `ProfileDescription` field: `%s`", $ProfileDescription));
// 		}

		// check status
		if ($ProfileStatusCode && !$parsedStatus) {
		    $msgs->addError(sprintf("Profile Status could not be saved, please define `ProfileStatusCode` field: `%s`", $ProfileStatusCode));
		}

		// check corp type field
		if ($CorporationTypeCode && !$parsedCorpType) {
		    $msgs->addError(sprintf("Corporation Type could not be saved, please define `CorporationTypeCode` field: `%s`", $CorporationTypeCode));
		}

		// check priority remarks
		if ($PriorityRemarks && !$parsedPriority) {
		    $msgs->addError(sprintf("Priority Remarks could not be saved, please define `PriorityRemarks` field: `%s`", $PriorityRemarks));
		}

		// check dk number
		if ($BranchID && $ReferenceID && !$parsedDkNumber) {
		    $msgs->addError(sprintf("DK Number could not be saved, please define new `DkNumber` field: `%s`", $BranchID.$ReferenceID));
		}

		// check if all PNR remarks were parsed
		$unparsedRemarks = array_diff(array_keys($remarksArr), array_values($parsedRemarks));
		if ($unparsedRemarks) {
		    foreach ($unparsedRemarks as $sequence) {
		        $msgs->addError(sprintf("PNR Move Data could not be saved, please define new `RemarkStartsWith` or `OtherPnrMoveData` field: `%s`", $remarksArr[$sequence]['Text']));
			}
		}

		// check if all telephones were parsed
		$unparsedTelephones = array_diff(array_keys($telephonesArr), array_values($parsedTelephones));
		if ($unparsedTelephones) {
		    foreach ($unparsedTelephones as $sequence) {
		        $msgs->addError(sprintf("Telephone could not be saved, please define new `Telephone` field: `%s`", $telephonesArr[$sequence]['FullPhoneNumber']));
		    }
		}

		// check if all emails were parsed
		$unparsedEmails = array_diff(array_keys($emailsArr), array_values($parsedEmails));
		if ($unparsedEmails) {
		    foreach ($unparsedEmails as $sequence) {
		        $msgs->addError(sprintf("Email could not be saved, please define new `Email` field: `%s`", $emailsArr[$sequence]['EmailAddress']));
		    }
		}

		if ($Addresses) {
		    // TODO First address?
		    // TODO LocationTypeCode="CMP" ?
			foreach ($Addresses as $Address) {

			    $addressLinesArr = [];
			    if ($Address->AddressLine) {
			        foreach ($Address->AddressLine as $AddressLine) {
			            $addressLinesArr[] = (String)$AddressLine;
			        }
			    }
			    $addressLinesStr = implode("\n", $addressLinesArr);

				if ((String)$Address['LocationTypeCode']=='DEL') {
					$entityFields['deliveryAddress'] = [
					    'address' => $addressLinesStr,
					];
				}
				else {
					$entityFields['address'] = [
					    'address' => $addressLinesStr,
						'zipCode' => (String)$Address->PostalCd,
					    'cityName' => (String)$Address->CityName,
					    'countryCode' => (String)$Address->CountryCode,
						'comments' => (String)$Address->InformationText,
					];
				}
			}
		}

		// GDS Codes
// 		if ($Profile->Corporation->GDS) {
// 			$gdsCodes = [];
// 			foreach ($Profile->Corporation->GDS as $GDS) {
// 				$gdsCodes[(int)$GDS['OrderSequenceNo']] = (String)$GDS['GDSCode'];
// 			}
// 			ksort($gdsCodes);
// 			$entityFields['GDS_CODES'] = $gdsCodes;
// 		}

		//var_dump($entityFields, $profileFields); die;

		return [
			'entityFields'  => $entityFields,
			'profileFields' => $profileFields,
		];
	}

	protected function getCustomerFields(\Engine\SimpleXmlElement $Profile, \Entity\GdsProfileTemplate $tplEnt, ?\Entity\Customer $custEnt=null)
	{
		$ProfileName	 	= (String)$Profile->TPA_Identity['ProfileName'];
		//$UniqueID 		 	= (String)$Profile->TPA_Identity['UniqueID'];
		//$ProfileTypeCode 	= (String)$Profile->TPA_Identity['ProfileTypeCode'];
		//$DomainID		 	= (String)$Profile->TPA_Identity['DomainID'];
		$ProfileStatusCode 	= (String)$Profile->TPA_Identity['ProfileStatusCode']; 		// AC - active, MG - merged, DL - deleted
		$ProfileDescription = (String)$Profile->TPA_Identity['ProfileDescription'];

		$BirthDate 			= (String)$Profile->Traveler->Customer['BirthDate'];
		$GenderCode			= (String)$Profile->Traveler->Customer['GenderCode'];		// F, FI, M, MI, U, UI, X, XI
		$ChildIndicator 	= (String)$Profile->Traveler->Customer['ChildIndicator'];
		$SeniorIndicator 	= (String)$Profile->Traveler->Customer['SeniorIndicator'];
		$LapInfantIndicator = (String)$Profile->Traveler->Customer['LapInfantIndicator'];
		$InformationText 	= (String)$Profile->Traveler->Customer->PersonName['InformationText'];		// Name Reference Number
		$GivenName 			= (String)$Profile->Traveler->Customer->PersonName->GivenName;
		$MiddleName			= (String)$Profile->Traveler->Customer->PersonName->MiddleName;
		$SurName 			= (String)$Profile->Traveler->Customer->PersonName->SurName;

		$BranchID 			= (String)$Profile->Traveler->TPA_Extensions->CustomerReferenceInfo['BranchID'];
		$ReferenceID 		= (String)$Profile->Traveler->TPA_Extensions->CustomerReferenceInfo['ReferenceID'];
		$PriorityRemarks	= (String)$Profile->Traveler->TPA_Extensions->PriorityRemarks['Text'];  // TODO

		$Documents			= $Profile->Traveler->Customer->Document;
		//$AssociatedFilters  = $Profile->Traveler->TPA_Extensions->AssociatedFilters;

		$ID_number = null;
		$ID_expireDate = null;
		$ID_countryCode = null;
		$documents = [];
		if ($Documents) {
			foreach ($Documents as $Doc) {
				if ((String)$Doc['DocTypeCode']=='NTID') {
					$ID_number = (String)$Doc['DocID'];
					$ID_expireDate = (String)$Doc['ExpireDate'];
					//$ID_countryCode = (String)$Doc['BirthCountryCode'];
					$ID_countryCode = (String)$Doc['DocHolderNationalityCode'];
				}
				elseif ((String)$Doc['DocTypeCode']=='VISA') {
					$documents[] = [
						'type' => 'visa_',
						'number' => (String)$Doc['DocID'],
						'expireDate' => (String)$Doc['ExpireDate'],
						'citizenship' => (String)$Doc['DocHolderNationalityCode'],
						'firstName' => (String)$Doc->DocHolder->GivenName,
						'lastName'  => (String)$Doc->DocHolder->SurName,
						//'issueCountryCode' BirthCountryCode
					];
				}
				else {
					$documents[] = [
						'type' => 'passport_',
						'number' => (String)$Doc['DocID'],
						'expireDate' => (String)$Doc['ExpireDate'],
						'citizenship' => (String)$Doc['DocHolderNationalityCode'],
						'firstName' => (String)$Doc->DocHolder->GivenName,
						'lastName'  => (String)$Doc->DocHolder->SurName,
						//'issueCountryCode'
					];
				}
			}
		}

		$Telephone = null;
		$TelephoneMobile = null;
		$Email = null;
		if ($Profile->Traveler->Customer->Telephone) {
			foreach ($Profile->Traveler->Customer->Telephone as $Tel) {
				if ((String)$Tel['LocationTypeCode']=='MOB') {
					$TelephoneMobile = (String)$Tel->FullPhoneNumber;
				} else {
					$Telephone = (String)$Tel->FullPhoneNumber;
				}
			}
		}
		if ($Profile->Traveler->Customer->Email) {
			$Email = (String)$Profile->Traveler->Customer->Email['EmailAddress'];
		}

		$Address = null;
		$DeliveryAddress = null;
		$ZipCode = null;
		$CityName = null;
		$CountryCode = null;
		if ($Profile->Traveler->Customer->Address) {
			foreach ($Profile->Traveler->Customer->Address as $Addr) {
				if ((String)$Addr['LocationTypeCode']=='DEL') {
					$DeliveryAddress = (String)$Addr->AddressLine;
				}
				else {
					$Address	= (String)$Addr->AddressLine;
					$CityName	= (String)$Addr->CityName;
					$CountryCode= (String)$Addr->CountryCode;
					$ZipCode	= (String)$Addr->PostalCd;
				}
			}
		}

		$remarksArr = [];
		if ($Profile->Traveler->TPA_Extensions->Remark) {
			foreach ($Profile->Traveler->TPA_Extensions->Remark as $Remark) {
				if ((String)$Remark['CategoryCode']=='PNR') {
					$remarksArr[] = (String)$Remark['Text'];
				}
			}
		}

		// TODO Email, Address, Document

		$fieldsDetails    = $tplEnt->staticFieldsDetails;
		uasort($fieldsDetails, function($f1, $f2) {		// reorder alphabetically, maintain index
			return strnatcmp($f1["label"], $f2["label"]);
		});
		$idProfileFields  = array_keys($fieldsDetails);
		$gdsProfileFields = $custEnt ? $custEnt->gdsProfileFieldsValues : [];

		$parsedRemarks = [];
		$profileFields = [];
		foreach ($idProfileFields as $idField) {
			$profileFields[$idField] = $gdsProfileFields[$idField] ?: '';

			// ID
// 			if ($fieldsDetails[$idField]['tagSabre']=='UniqueID') {
// 				$profileFields[$idField] = $UniqueID;
// 			}

			// PCC
// 			if ($fieldsDetails[$idField]['tagSabre']=='PCC') {
// 				$profileFields[$idField] = $DomainID;
// 			}

			// profile name
			if ($fieldsDetails[$idField]['tagSabre']=='ProfileName') {
				$profileFields[$idField] = $ProfileName;
			}

			// profile description
			if ($fieldsDetails[$idField]['tagSabre']=='ProfileDescription') {
				$profileFields[$idField] = $ProfileDescription;
			}

			// first name
// 			if ($fieldsDetails[$idField]['tagSabre']=='GivenName') {
// 				$profileFields[$idField] = $GivenName;
// 			}

			// middle name
// 			if ($fieldsDetails[$idField]['tagSabre']=='MiddleName') {
// 				$profileFields[$idField] = $MiddleName;
// 			}

			// last name
// 			if ($fieldsDetails[$idField]['tagSabre']=='SurName') {
// 				$profileFields[$idField] = $SurName;
// 			}

			// birth date
// 			if ($fieldsDetails[$idField]['tagSabre']=='BirthDate') {
// 				$profileFields[$idField] = $BirthDate;
// 			}

			// child indicator
			if ($fieldsDetails[$idField]['tagSabre']=='ChildIndicator') {
				$profileFields[$idField] = $ChildIndicator;
			}

			// senior indicator
			if ($fieldsDetails[$idField]['tagSabre']=='SeniorIndicator') {
				$profileFields[$idField] = $SeniorIndicator;
			}

			// infant indicator
			if ($fieldsDetails[$idField]['tagSabre']=='LapInfantIndicator') {
				$profileFields[$idField] = $LapInfantIndicator;
			}

			// Name Reference Number
			if ($fieldsDetails[$idField]['tagSabre']=='InformationText') {
				$profileFields[$idField] = $InformationText;
			}

			// Remark starts with (value only)
			if (substr($fieldsDetails[$idField]['tagSabre'], 0, 17)=='RemarkStartsWith:') {
				$startsWith = substr($fieldsDetails[$idField]['tagSabre'], 17);
				$rmks = $this->remarksThatStartsWith($startsWith, $remarksArr);
				foreach ($rmks as $rmk) {
				    if ($rmk && !$parsedRemarks[$rmk]) {
				        $profileFields[$idField] = substr($rmk, strlen($startsWith));
				        $parsedRemarks[$rmk] = true;
				        break;
				    }
				}
			}

			// DK number (BranchID+ReferenceID)
			if ($fieldsDetails[$idField]['tagSabre']=='DkNumber') {
				$profileFields[$idField] = $BranchID.$ReferenceID;
			}

			// Profile Status code
			// TODO don't use
			if ($fieldsDetails[$idField]['tagSabre']=='ProfileStatusCode') {
				$profileFields[$idField] = $ProfileStatusCode;
			}

			// Gender code
			if ($fieldsDetails[$idField]['tagSabre']=='GenderCode') {
				$profileFields[$idField] = $GenderCode;
			}

			// Priority remarks
			if ($fieldsDetails[$idField]['tagSabre']=='PriorityRemarks') {
				$profileFields[$idField] = $PriorityRemarks;
			}

		}

		// loop again and populate remaining Other PNR Move Data fields
		foreach ($idProfileFields as $idField) {
			if ($fieldsDetails[$idField]['tagSabre']=='OtherPnrMoveData') {
				foreach ($remarksArr as $rmk) {
					if ($rmk && !$parsedRemarks[$rmk]) {
						$profileFields[$idField] = $rmk;
						$parsedRemarks[$rmk] = true;
						break;
					}
				}
			}
		}

		$entityFields  = [];
		$entityFields['firstNameStandard']  = strtoupper($GivenName);
		$entityFields['lastNameStandard']   = strtoupper($SurName);
		$entityFields['middleNameStandard'] = strtoupper($MiddleName);
		$entityFields['comments']  = $ProfileDescription;
		$entityFields['birthday']  = $BirthDate;
		$entityFields['telephone']  = $Telephone;
		$entityFields['telephoneMobile']  = $TelephoneMobile;
		$entityFields['email']  = $Email;
		$entityFields['address'] = $Address;
		$entityFields['deliveryAddress'] = $DeliveryAddress;
		$entityFields['zipCode'] = $ZipCode;
		$entityFields['cityName'] = $CityName;
		$entityFields['countryCode'] = $CountryCode;
		$entityFields['ID_number'] = $ID_number;
		$entityFields['ID_expireDate'] = $ID_expireDate;
		$entityFields['ID_countryCode'] = $ID_countryCode;
		$entityFields['documents'] = $documents;

		if ($LapInfantIndicator=='Y') {
			$entityFields['type'] = 'i';
		}
		elseif ($ChildIndicator=='Y') {
			$entityFields['type'] = 'c';
		}
		else {
			$entityFields['type'] = 'a';
		}

		//var_dump($entityFields, $profileFields); die;

		return [
			'entityFields'  => $entityFields,
			'profileFields' => $profileFields,
		];
	}

	/**
	 * Search the first remark in a list that starts with a string
	 * @param string $startsWith
	 * @param array $remarks
	 */
// 	private function remarkThatStartsWith($startsWith, Array $remarks=[])
// 	{
// 		foreach ($remarks as $rmk) {
// 			if (substr($rmk, 0, strlen($startsWith))==$startsWith) {
// 				return $rmk;
// 			}
// 		}
// 	}

	/**
	 * Search all remarks in a list that start with a string
	 * @param string $startsWith
	 * @param array $remarks
	 */
	private function remarksThatStartsWith($startsWith, Array $remarks=[])
	{
	    $filterRemarks = [];

	    foreach ($remarks as $rmk) {
	        if (substr($rmk['Text'], 0, strlen($startsWith))==$startsWith) {
	            $filterRemarks[$rmk['OrderSequenceNo']] = $rmk;
	        }
	    }

	    return $filterRemarks;
	}

}
