<?php

/**
 * Manage Catalog Supplier
 *
 * PHP version 5.6
 *
 * @category  Settings
 * @package   Eloquent
 * @author    Debasis Rath <dan@imprintnext.com>
 * @copyright 2019-2022 Riaxe Systems
 * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
 * @link      http://inkxe-v10.inkxe.io/xetool/admin
 */

namespace App\Modules\Integration\Controllers;

use App\Components\Controllers\Component as ParentController;

use App\Modules\Integration\Models\PluginSettings;
use App\Modules\Products\Models\CatalogProductRel;

/**
 * CatalogSupplier Controller
 *
 * @category Class
 * @package  Integration
 * @author   Debasis Rath <dan@imprintnext.com>
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @link     http://inkxe-v10.inkxe.io/xetool/admin
 */
class CatalogSupplierController extends ParentController
{
	// supplier's configuration data
	private $supConfig = [];
	private $breathStamp = [];
	private $providers = [
		'sanmar',
		'alp',
		'sns'
	];

	public function __construct()
	{
		parent::__construct();
		// keep the breath stamp for each API provider
		$this->breathStamp = array_reduce($this->providers, function ($accumulator, $provider) {
			$accumulator[$provider] = 0;
			return $accumulator;
		});
	}
	/**
	 * Internal: API breath gap function
	 * @param $breathGap time in seconds (default 1 sec - can be fraction)
	 */
	private function waitForApiBreath($breathGap = 1, $provider = 'sanmar')
	{
		while ((microtime(true) - $this->breathStamp[$provider]) < $breathGap) {
			usleep(50 * 1000); // sleep for 50 mili sec upto breath time
		}
		$this->breathStamp[$provider] = microtime(true);
		return true;
	}

	/**
	 * Post: Save catalog suppliers
	 *
	 * @param $request  Slim's Request object
	 * @param $response Slim's Response object
	 * @param $args     Slim's Argument parameters
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return json
	 */

	public function saveSuppliers($request, $response, $args)
	{
		$serverStatusCode = OPERATION_OKAY;
		$jsonResponse = [
			'status' => 0,
			'message' => 'Install Failed'
		];
		// maintain the relationship if file is uploaded
		if ($request->getUploadedFiles()) {
			$this->updateCatalogRelation();
		}
		// updated the old catalog-relation having blank catalog-code
		$this->updateOldCatalog();
		// get data
		$allPostPutVars = $request->getParsedBody();
		$slugData = (!empty($allPostPutVars['slug_data']) ? json_decode($allPostPutVars['slug_data'], true) : []);

		// decrpt frontend fields
		$this->frontEncrpt($slugData, 'decrypt');
		// filter the format of slug data
		$suppliers = array_filter($slugData, function ($data, $provider) {
			return in_array($provider, $this->providers) && is_array($data);
		}, ARRAY_FILTER_USE_BOTH);

		$suppliersConfig = [];
		$errorMsg = false;
		// validate each provider
		foreach ($this->providers as $provider) {
			$result = $this->validateData($provider, $suppliers[$provider]);
			if ($result['status'] == 0) {
				$errorMsg = $result['message'];
				break;
			}
		}
		if (!$errorMsg) {
			// validate the common config
			$result = $this->validateData('config', $slugData['config']);
			if ($result['status'] == 0) {
				$errorMsg = $result['message'];
			} else {
				$suppliersConfig['config'] = $slugData['config'];
			}
		}
		if ($errorMsg) {
			$jsonResponse['message'] = $errorMsg;
			return response($response, [
				'data' => $jsonResponse, 'status' => $serverStatusCode
			]);
		}
		// encrypt the necessary fields
		$suppliersConfig['suppliers'] = $this->secureData($suppliers, 'encrypt');

		// find the existing record
		$pluginSetting = $pluginsettingsInit = new PluginSettings();
		$checkRecord = $pluginsettingsInit->where(['catalog_code' => 'catalog_providers'])->get();
		if ($checkRecord->count() > 0) {
			$pluginSetting = $checkRecord->first();
			$pluginSetting->updated_date = date('Y-m-d H:i:s', time());
		}
		// build the data for the entity
		$pluginData = [
			'settings' => json_encode($suppliersConfig),
			'catalog_code' => 'catalog_providers',
			'status' => 1
		];

		// update the entity
		$pluginSetting->fill($pluginData);

		if ($pluginSetting->save()) {
			if (!empty($suppliers)) {
				$url = BASE_URL . 'update-central-catalog';
				// cron job every 30 minutes
				$job = "*/30 * * * * wget -q -O /dev/null $url";
				$isAutoSyncEnabled = in_array(1, array_column($suppliers, 'auto_sync'));
				// set / remove cronjob based on the auto sync parameter (Sanmar)
				if ($isAutoSyncEnabled) {
					$this->addJob($job);
					create_log('catalog_provider', 'info', [
						'message' => "Connjob added for the URL: $url", 'extra' => []
					]);
				} else {
					$this->removeJob($job);
					create_log('catalog_provider', 'info', [
						'message' => "Connjob removed for the URL: $url", 'extra' => []
					]);
				}
			}
			// set variant cache dir - will be used while update
			$cacheDir = ASSETS_PATH_W . 'catalog/variants/';
			if (!is_dir($cacheDir)) {
				mkdir($cacheDir, 0777, true);
			}
			$jsonResponse = [
				'status' => 1, 'message' => 'Configuration updated successfully'
			];
		}

		// return the response
		return response($response, [
			'data' => $jsonResponse, 'status' => $serverStatusCode
		]);
	}
	/* Validate the data */
	private function validateData($provider, $data)
	{
		$result = [
			'status' => 0, 'message' => 'Validation error occurred!'
		];
		$err = false;
		switch ($provider) {
			case 'sanmar':
				// allow empty credentials
				if (empty($data) || !array_filter($data)) {
					break;
				}
				if (empty($data['client_id'])) {
					$err = "Client ID is required";
				} elseif (empty($data['app_id'])) {
					$err = "App ID is required";
				} elseif (empty($data['app_password'])) {
					$err = "App password is required";
				} else {
					$authStatus = $this->checkApiAuth($data, $provider);
					if (!$authStatus) {
						$err = "Api authentication failed for Sanmar";
					}
				}
				break;
			case 'alp':
			case 'sns':
				// allow empty credentials
				if (empty($data) || !array_filter($data)) {
					break;
				}
				if (empty($data['app_id'])) {
					$err = "App ID is required";
				} elseif (empty($data['app_password'])) {
					$err = "App password is required";
				} else {
					$authStatus = $this->checkApiAuth($data, $provider);
					if (!$authStatus) {
						$err = "Api authentication failed for $provider";
					}
				}
				break;
			default:
				if (
					!empty($data['margin_value']) &&
					(empty($data['margin_type']) || !in_array($data['margin_type'], ['flat', 'percentage']))
				) {
					$err = "Margin type required";
				}
				break;
		}
		if ($err) {
			$result['message'] = $err;
		} else {
			$result = ['status' => 1];
		}
		return $result;
	}

	/**
	 * Get: list of catalog suppliers
	 *
	 * @param $request  Slim's Request object
	 * @param $response Slim's Response object
	 * @param $args     Slim's Argument parameters
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return json
	 */

	public function getCatalogSuppliers($request, $response, $args)
	{
		$serverStatusCode = OPERATION_OKAY;
		$jsonResponse = [
			'status' => 1,
			'message' => 'Please update the supplier\'s configuration',
			'catalog_suppliers' => []
		];

		// get all supplier data with front-end data encrypt
		$catalogSupplier = $this->getSuppliersData(1);

		if (!empty($catalogSupplier)) {
			$jsonResponse = [
				'status' => 1,
				'catalog_suppliers' => $catalogSupplier
			];
		}

		return response($response, [
			'data' => $jsonResponse, 'status' => $serverStatusCode
		]);
	}

	/**
	 * Internal and call from other controllers
	 */
	public function getSuppliersData($encryptFront = 0)
	{
		$catalogSupplier = [];

		$pluginsInit = new PluginSettings();
		$fields = ['catalog_code', 'settings as slug_data', 'created_date', 'updated_date', 'status'];

		$getCatalogSupplier = $pluginsInit->select($fields)->where([
			['catalog_code', '=', 'catalog_providers'],
			['status', '=', 1]
		]);

		if ($getCatalogSupplier->count()) {

			$catalogSupplier = $getCatalogSupplier->first();
			
			// convert to array format
			$slugData = json_decode($catalogSupplier->slug_data, true);

			// for backward compartibility
			if (!isset($slugData['suppliers'])) {
				if (!empty($slugData['sanmar'])) {
					$slugData['suppliers'] = $slugData;
					$slugData['config'] = [
						'margin_type' =>	$slugData['sanmar']['margin_type'],
						'margin_value' => $slugData['sanmar']['margin_value']
					];
				} else {
					$slugData = [
						'suppliers' => [], 'config' => []
					];
				}
			}
			
			// decrypt the necessary fields
			$suppliers = $this->secureData($slugData['suppliers'], 'decrypt');

			$suppliers['config'] = $slugData['config'];

			if ($encryptFront) {
				// encrypt for the frontend
				$this->frontEncrpt($suppliers, 'encrypt');
			}

			// decrypt the necessary fields
			$catalogSupplier->slug_data = $suppliers;
		}

		return $catalogSupplier;
	}

	/**
	 * Get: Downlaod the sample template to link existing products
	 *
	 * @param $request  Slim's Request object
	 * @param $response Slim's Response object
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return CSV file
	 */

	public function downloadCsvSample($request, $response)
	{
		// default header
		$rows = [
			['product_id','sku', 'catalog_provider'],
			['1001','SAN1001', 'sanmar'],
			['1002','SNS1002', 'sns'],
			['1003','ALP1003', 'alp'],
		];

		$delimiter = ',';
		$enclosure = '"';
		$escapeChar = "\\";

		$fs = fopen('php://memory', 'r+');

		// save csv in the php memory
		foreach ($rows as $data) {
			fputcsv($fs, $data, $delimiter, $enclosure, $escapeChar);
		}
		
		rewind($fs);

		$csvTemplate = stream_get_contents($fs);

		fclose($fs);

		$response   = $response->withHeader('Content-Type', 'text/csv')
		->withHeader('Content-Description', 'File Transfer')
		->withHeader('Content-Disposition', 'attachment; filename="productProviderSample.csv"')
		->withHeader('Content-Transfer-Encoding', 'binary')
		->withHeader('Expires', '0')
		->withHeader('Cache-Control', 'must-revalidate')
		->withHeader('Content-Length', strlen($csvTemplate))
		->withHeader('Pragma', 'public');

		echo $csvTemplate;

		return $response;
	}

	/**
	 * Get: Update provider for the old catalog products
	 *
	 * @return Integer update count
	 */

	private function updateOldCatalog()
	{
		$catlogProductRelsInit = new CatalogProductRel();

		$getOldProducts = $catlogProductRelsInit->select(['catalog_product_id'])
		->whereNull('catalog_code')
		->orWhere('catalog_code', '=', '')
		->orWhere('catalog_code', '=', '0');

		$totalCatalogs = $getOldProducts->count();
		if ($totalCatalogs == 0) {
			return 0;
		}

		$totalSuccess = 0;
		$oldProductSkus = $getOldProducts->get()->pluck('catalog_product_id')->toArray();

		$providerDetails = $this->getProviderFromIds($oldProductSkus);

		foreach ($providerDetails as $provider => $skus) {
			// some values treated as int, float
			$skus = array_map('strval', $skus);

			$successCount = $catlogProductRelsInit->whereIn('catalog_product_id', $skus)
			->update(['catalog_code' => $provider]);

			$totalSuccess += $successCount;
		}

		return $totalSuccess;
	}

	/**
	 * Internal: remove cronjob (to call from other program)
	 */
	public function removeCronJob($job)
	{
		return $this->removeJob($job);
	}


	/**
	 * Internal: to update the relationship
	 */
	private function updateCatalogRelation()
	{
		$dir = path('abs', 'product') . 'catalog_provider/';
		if (!is_dir($dir)) {
			mkdir($dir);
		}
		// upload the CSV file
		$fileName = do_upload('upload', $dir, [], 'string');

		if ($fileName) {
			$catlogProductRelsInit = new CatalogProductRel();

			$productArr = $this->csvToArray($dir . $fileName);
			// remove existing product ids
			$productIds = array_column($productArr, 'product_id');
			$existingProducts = $catlogProductRelsInit->select('product_id')->whereIn('product_id', $productIds);

			if ($existingProducts->count()) {
				$existingIds = $existingProducts->get()->pluck('product_id')->toArray();
				$productArr = array_filter($productArr, function ($product) use ($existingIds) {
					return !in_array($product['product_id'], $existingIds);
				});
			}

			// save the relation
			foreach ($productArr as $product) {
				$catRelData = [
					'product_id' => $product['product_id'],
					'catalog_product_id' => strval($product['sku']),
					'catalog_code' => $product['catalog_provider']
				];
				$newCatlogProduct = new CatalogProductRel($catRelData);

				$newCatlogProduct->save();
			}

			// Remove the file when update is comepleted
			unlink($dir . $fileName);
		}
	}
	/**
	 * Internal: convert CSV to array
	 */
	private function csvToArray($file)
	{
		$result = [];
		$fp = fopen($file, 'r');

		// get the header
		$header = fgetcsv($fp);
		// special charater remove from the header(usecase)
		$header = array_map(function ($item) {
			return trim($item, "\xEF\xBB\xBF");
		}, $header);
		$headerCount = count($header);

		// convert to array
		while ($row = fgetcsv($fp)) {
			while ($headerCount < count($row)) {
				array_pop($row);
			}
			while ($headerCount > count($row)) {
				$row[] = '';
			}
			$result[]  = array_combine($header, $row);
		}

		fclose($fp);
		return $result;
	}

	/**
	 * Internal: get the provider from central server using SKUs
	 */
	private function getProviderFromIds($productIds = [])
	{
		$result = [];

		$apiUrl = CATALOG_API_URL . "catalog/services/api/v1/catalogs/providers";

		$body = json_encode(['skus' => $productIds]);
		
		// API header set
		$headers = [
			"Content-type: application/json;charset=\"utf-8\"",
			"Accept: application/json",
			"Cache-Control: no-cache",
			"Pragma: no-cache",
			"Content-length: " . strlen($body)
		];

		// PHP cURL  for https connection with auth
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
		curl_setopt($ch, CURLOPT_URL, $apiUrl);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

		// username and password - declared at the top of the doc
		// curl_setopt($ch, CURLOPT_USERPWD, $soapUser . ":" . $soapPassword);
		curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
		curl_setopt($ch, CURLOPT_TIMEOUT, 10);
		curl_setopt($ch, CURLOPT_POST, true);

		// the SOAP request
		curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

		// converting
		$response = curl_exec($ch);

		if (curl_errno($ch)) {
			$errorMsg = curl_error($ch);
			create_log('catalog_provider', 'info', [
				'message' => "Error while fetching provider from the central server: {$errorMsg}",
				'extra' => [
					'url' => $apiUrl,
					'body' => $body
				]
			]);
		}
		curl_close($ch);

		if ($response) {
			// responseData = {'provider':['sku1','sku2', ...], ...}
			$jsonDecoded = json_clean_decode($response);
			$result = ($jsonDecoded['status'] == 1 ? $jsonDecoded['data'] : []);
		}
		
		return $result;
	}

	/**
	 * Internal: Encrypt / descrypt required fields
	 *
	 * @param &$data  Array of supplier data
	 * @param $type   String - 'encrypt/decrypt'
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return Array / False
	 */

	public function secureData(&$data, $type = 'encrypt')
	{
		// fields to encrypt / decrypt
		$encKeys = ['app_id', 'app_password'];
		if (!empty($data)) {
			$providerKeys = array_keys($data);
			foreach ($providerKeys as $providerKey) {
				array_walk($data[$providerKey], function (&$providerVal, $key) use ($encKeys, $type) {
					if (in_array($key, $encKeys)) {
						$providerVal = encryptor($type, $providerVal);
					}
				});
			}
			return $data;
		}
		return false;
	}

	/**
	 * Internal: Encrypt / decrypt required fields for front-end
	 *
	 */
	public function frontEncrpt(&$data, $type = 'encrypt')
	{
		// append word to detect as encrypted text
		$passKey = 'passkey-';
		// fields to encrypt / decrypt
		$encKeys = ['app_password'];
		$getJWTSecret = get_app_settings('jwt_secret');
		if (!empty($data)) {
			$providerKeys = array_keys($data);
			// keep only suppliers credentials only
			$providerKeys = array_intersect($providerKeys, $this->providers);
			foreach ($providerKeys as $providerKey) {
				array_walk($data[$providerKey], function (&$providerVal, $key) use ($encKeys, $type, $passKey, $getJWTSecret) {
					if (in_array($key, $encKeys)) {
						if ($type == 'encrypt' && !empty($providerVal)) {
							$providerVal = $passKey . encryption($providerVal, $getJWTSecret);
						} else {
							if (substr($providerVal, 0, strlen($passKey)) === $passKey) {
								// remove left appended text, then decrypt
								$providerVal = (string)decryption(substr($providerVal, strlen($passKey)));
							}
						}
					}
				});
			}
			return $data;
		}
		return false;
	}

	/**
	 * Internal: Update/Read the catalog product id
	 *
	 * @param $catalogProvider  String - provider name
	 * @param $productId   Interger - catalog product_id(-1 = read id)
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return Integer product id
	 */

	private function lastCatalogRecord($catalogProvider, $productId = -1)
	{
		$dir = ASSETS_PATH_W . 'products/catalog_provider/';
		if (!is_dir($dir)) {
			mkdir($dir);
		}

		// file path
		$file = $dir . "$catalogProvider.json";

		// default return the last product id
		if ($productId < 0) {
			return intval(file_get_contents($file));
		} else {
			// updated the last product id
			file_put_contents($file, $productId);
		}

		return $productId;
	}

	/**
	 * Check API Authentication
	 */
	public function checkApiAuth($supConfig, $provider)
	{
		$authResponse = 0;
		try {
			if ($provider == 'sanmar') {
				if (empty($supConfig['client_id']) || empty($supConfig['app_id']) || empty($supConfig['app_password'])) {
					throw new \Exception('Supplier configuration not ready!');
				}
			} else {
				if (
					empty($supConfig['app_id']) || empty($supConfig['app_password'])
				) {
					throw new \Exception('Supplier configuration not ready!');
				}
			}
			// set the supplier config data
			$this->supConfig[$provider] = $supConfig;

			$result = $this->getApiData($provider, ['type' => 'auth']);
			$authResponse = $result['status'];
			if ($authResponse == 0) {
				throw new \Exception('Authentication failed for the provider!');
			}

		} catch (\Exception $e) {
			create_log('catalog_provider', 'info', [
				'message' => $e->getMessage(),
				'extra' => [
					'module' => 'CatalogSuppliers',
					'provider' => $provider
				]
			]);
		}
		return $authResponse;
	}

	/**
	 * Call the API
	 */
	public function getApiData($provider, $params)
	{
		$providerFuncPart = ucfirst($provider);
		if ($provider == 'sns') {
			$params += ['promo' => 0, 'method'=> 'get'];
		} else {
			$params += ['promo' => 1, 'method'=> 'post'];
		}
		$templates = $this->{"getTemplate$providerFuncPart"}($params);
		if (!$templates) {
			return ['status' => 0];
		}

		// keep gap for 1 sec between each API call
		$this->waitForApiBreath(1, $provider);

		// API header set
		$headers = [
			"Cache-Control: no-cache",
			"Pragma: no-cache",
			"Content-length: " . strlen($templates['body'])
		];
		if ($templates['header']) {
			$headers = array_merge($headers, $templates['header']);
		}
		// PHP cURL  for https connection with auth
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
		curl_setopt($ch, CURLOPT_URL, $templates['apiUrl']);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

		// username and password - declared at the top of the doc
		// curl_setopt($ch, CURLOPT_USERPWD, $soapUser . ":" . $soapPassword);
		curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
		curl_setopt($ch, CURLOPT_TIMEOUT, 10);

		if ($params['method'] == 'post') {
			curl_setopt($ch, CURLOPT_POST, true);
			// the SOAP request
			curl_setopt($ch, CURLOPT_POSTFIELDS, $templates['body']);
		}

		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
		// converting
		$response = curl_exec($ch);
		curl_close($ch);
		return $this->{"formatResponse$providerFuncPart"}($response, $params['type']);
	}
	/**
	 * Internal: format the response of the Sanmar API call
	 */
	private function formatResponseSanmar($response, $type)
	{
		$result = ['status' => 0, 'message' => 'Format error!'];
		// for Sanamr catalog
		$arrResponse = $this->xml2Arr($response, '<return>', '</return>');
		switch ($type) {
			case 'auth':
				$authStatus = (empty($arrResponse['message']) || strpos($arrResponse['message'], 'authentication failed') === false);
				$result = [
					'status' => intval($authStatus),
					'message' => $arrResponse['message']
				];
				break;
			case 'inventory':
				if ($arrResponse['errorOccurred'] == 'false') {
					$result = [
						'status' => 1,
						'stock' => !empty($arrResponse['listResponse']) ? array_sum($arrResponse['listResponse']) : 0
					];
				} else {
					$result['message'] = 'Some error occurred while fetching inventory';
				}
				break;
			case 'price':
				if ($arrResponse['errorOccurred'] == 'false') {
					$result = [
						'status' => 1,
						'price' => floatval($arrResponse['listResponse']['casePrice'])
					];
				} else {
					$result['message'] = 'Some error occurred while fetching price';
				}

				break;
			default:
				break;
		}

		return $result;
	}
	/**
	 * Internal: format the response of the ALP API call
	 */
	private function formatResponseAlp($response, $type)
	{
		$result = ['status' => 0, 'message' => 'Format error!'];
		// for ALP catalog
		$arrResponse = $this->xml2Arr($response, '<SOAP-ENV:Body>', '</SOAP-ENV:Body>');
		switch ($type) {
			case 'auth':
				$authStatus = !(!isset($arrResponse['ns1:GetOrderStatusTypesResponse']) || !empty($arrResponse['ns1:GetOrderStatusTypesResponse']['ns1:errorMessage']));
				$result = [
					'status' => intval($authStatus),
					'message' => $arrResponse['message']
				];
				break;
			case 'inventory':
				if (!empty($arrResponse['ns2:GetInventoryLevelsResponse']['ns1:Inventory'])) {
					$inventory = intval($arrResponse['ns2:GetInventoryLevelsResponse']['ns1:Inventory']['ns1:PartInventoryArray']['ns1:PartInventory']['ns1:quantityAvailable']['ns1:Quantity']['ns1:value']);
					$result = [
						'status' => 1,
						'stock' => $inventory
					];
				}
				break;
			case 'price':
				if (!empty($arrResponse['ns2:GetConfigurationAndPricingResponse']['ns2:Configuration'])) {
					$stock = floatval($arrResponse['ns2:GetConfigurationAndPricingResponse']['ns2:Configuration']['ns2:PartArray']['ns2:Part']['ns2:PartPriceArray']['ns2:PartPrice']['ns2:price']);
					$result = [
						'status' => 1,
						'price' => $stock
					];
				}
				break;
			default:
				break;
		}
		return $result;
	}
	/**
	 * Internal: format the response of the SNS API call
	 */
	private function formatResponseSns($response, $type)
	{
		$result = ['status' => 0, 'message' => 'Format error!'];
		// for ALP catalog
		$response = preg_replace('/}\s*,\s*\]/i', '}]', $response);
		$arrResponse = json_decode($response, true);
		switch ($type) {
			case 'auth':
				if (isset($arrResponse['message']) && strpos($arrResponse['message'], 'Authorization') !== false) {
					$result['message'] =  $arrResponse['message'];
				} else {
					$result = ['status' => 1];
				}
				break;
			case 'inventory':
				if (isset($arrResponse['message'])) {
					$result['message'] =  $arrResponse['message'];
					return $result;
				}
				if (isset($arrResponse[0]['casePrice']) && isset($arrResponse[0]['qty'])) {
					$result = [
						'status' => 1,
						'price' => floatval($arrResponse[0]['casePrice']),
						'stock' => intval($arrResponse[0]['qty'])
					];
				}
				break;
			default:
				break;
		}
		return $result;
	}
	/**
	 * Internal: get the sanmar template for soap API call
	 */
	private function getTemplateSanmar($params)
	{
		$apiUrl = $body = '';
		$baseUrl = SANMAR_WEBSERVICE_URL;
		if (empty($this->supConfig['sanmar'])) {
			return false;
		}
		switch ($params['type']) {
			case 'auth':
				// authenication method not provided, by checking false brand- auth checked
				$apiUrl = "{$baseUrl}SanMarProductInfoServicePort";
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
				xmlns:impl=\"http://impl.webservice.integration.sanmar.com/\">
				<soapenv:Header/>
				<soapenv:Body>
				   <impl:getProductInfoByBrand>
					  <arg0><brandName>null</brandName></arg0>
					  <arg1>
					  <sanMarCustomerNumber>{$this->supConfig['sanmar']['client_id']}</sanMarCustomerNumber>
					  <sanMarUserName>{$this->supConfig['sanmar']['app_id']}</sanMarUserName>
					  <sanMarUserPassword>{$this->supConfig['sanmar']['app_password']}</sanMarUserPassword>
					  </arg1>
				   </impl:getProductInfoByBrand>
				</soapenv:Body>
			 </soapenv:Envelope>";
				break;
			case 'inventory':
				$apiUrl = "{$baseUrl}SanMarWebServicePort";
				// get Sanmar product details
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
			xmlns:web=\"http://webservice.integration.sanmar.com/\">
				<soapenv:Header/>
				<soapenv:Body>
					<web:getInventoryQtyForStyleColorSize>
						<arg0>{$this->supConfig['sanmar']['client_id']}</arg0>
						<arg1>{$this->supConfig['sanmar']['app_id']}</arg1>
						<arg2>{$this->supConfig['sanmar']['app_password']}</arg2>
						<arg3>{$params['product_id']}</arg3>
						<arg4>{$params['color']}</arg4>
						<arg5>{$params['size']}</arg5>
					</web:getInventoryQtyForStyleColorSize>
				</soapenv:Body>
			</soapenv:Envelope>";
				break;
			case 'price':
				$apiUrl = "{$baseUrl}SanMarPricingServicePort";
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
				xmlns:impl=\"http://impl.webservice.integration.sanmar.com/\">
				<soapenv:Header/>
				<soapenv:Body>
				   <impl:getPricing>
					  <arg0>
					  <color>{$params['color']}</color><size>{$params['size']}</size><style>{$params['product_id']}</style>
					  </arg0>
					  <arg1>
						 <sanMarCustomerNumber>{$this->supConfig['sanmar']['client_id']}</sanMarCustomerNumber>
						 <sanMarUserName>{$this->supConfig['sanmar']['app_id']}</sanMarUserName>
						 <sanMarUserPassword>{$this->supConfig['sanmar']['app_password']}</sanMarUserPassword>
					  </arg1>
				   </impl:getPricing>
				</soapenv:Body>
			 </soapenv:Envelope>";
				break;
			default:
				// do not continue if url / body is empty
				return false;
		}
		// begin tag / end tag used to trim the result and get the essential xml
		return [
			'apiUrl' => $apiUrl,
			'body' => $body,
			'header' => [
				"Content-type: text/xml;charset=\"utf-8\"",
				"Accept: text/xml"
			]
		];
	}
	/**
	 * Internal: get the ALP template for soap API call
	 */
	private function getTemplateAlp($params)
	{
		$apiUrl = $body = '';
		$baseUrl = ALP_WEBSERVICE_URL;
		if (empty($this->supConfig['alp'])) {
			return false;
		}
		switch ($params['type']) {
			case 'auth':
				// authenication method not provided, by checking available order status - auth checked
				$apiUrl = "{$baseUrl}orderStatus-1-0/service/index.php";
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns=\"http://www.promostandards.org/WSDL/OrderStatusService/1.0.0/\">
				<soapenv:Header/>
				<soapenv:Body>
				   <ns:GetOrderStatusTypesRequest>
					  <ns:wsVersion>1.0.0</ns:wsVersion>
					   <ns:id>{$this->supConfig['alp']['app_id']}</ns:id>
					 <ns:password>{$this->supConfig['alp']['app_password']}</ns:password>
				   </ns:GetOrderStatusTypesRequest>
				</soapenv:Body>
			 </soapenv:Envelope>";
				break;
			case 'inventory':
				$apiUrl = "{$baseUrl}inventory-2-0/service/index.php";
				// get Sanmar product details
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns=\"http://www.promostandards.org/WSDL/Inventory/2.0.0/\" xmlns:shar=\"http://www.promostandards.org/WSDL/Inventory/2.0.0/SharedObjects/\">
				<soapenv:Header/>
				<soapenv:Body>
				   <ns:GetInventoryLevelsRequest>
					  <shar:wsVersion>2.0.0</shar:wsVersion>
					  <shar:id>{$this->supConfig['alp']['app_id']}</shar:id>
					  <shar:password>{$this->supConfig['alp']['app_password']}</shar:password>
					  <shar:productId>{$params['product_id']}</shar:productId>
					  <shar:Filter>
					  	 <shar:partIdArray>
							<shar:partId>{$params['sku']}</shar:partId>
						 </shar:partIdArray>
					  </shar:Filter>
				   </ns:GetInventoryLevelsRequest>
				</soapenv:Body>
			 </soapenv:Envelope>";
				break;
			case 'price':
				$apiUrl = "{$baseUrl}productConfig/service/index.php";
				$body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns=\"http://www.promostandards.org/WSDL/PricingAndConfiguration/1.0.0/\" xmlns:shar=\"http://www.promostandards.org/WSDL/PricingAndConfiguration/1.0.0/SharedObjects/\">
				<soapenv:Header/>
				<soapenv:Body>
				   <ns:GetConfigurationAndPricingRequest>
					  <shar:wsVersion>1.0.0</shar:wsVersion>
					  <shar:id>{$this->supConfig['alp']['app_id']}</shar:id>
					  <shar:password>{$this->supConfig['alp']['app_password']}</shar:password>
					  <shar:productId>{$params['product_id']}</shar:productId>
					  <shar:partId>{$params['sku']}</shar:partId>
					  <shar:currency>USD</shar:currency>
					  <shar:fobId>CC</shar:fobId>
					  <shar:priceType>Customer</shar:priceType>
					  <shar:localizationCountry>US</shar:localizationCountry>
					  <shar:localizationLanguage>en</shar:localizationLanguage>
					  <shar:configurationType>Blank</shar:configurationType>
				   </ns:GetConfigurationAndPricingRequest>
				</soapenv:Body>
			 </soapenv:Envelope>";
				break;
			default:
				// do not continue if url / body is empty
				return false;
		}
		// begin tag / end tag used to trim the result and get the essential xml
		return [
			'apiUrl' => $apiUrl,
			'body' => $body,
			'header' => [
				"Content-type: text/xml;charset=\"utf-8\"",
				"Accept: text/xml"
			]
		];
	}

	/**
	 * Internal: get the SNS template for soap API call
	 */
	private function getTemplateSns($params)
	{
		$apiUrl = $body = '';
		$baseUrl = SNS_WEBSERVICE_URL;
		if (empty($this->supConfig['sns'])) {
			return false;
		}
		switch ($params['type']) {
			case 'auth':
				// authenication method not provided, by checking available order status - auth checked
				$apiUrl = "{$baseUrl}v2/categories/1";
				break;
			case 'inventory':
				$apiUrl = "{$baseUrl}v2/products/{$params['sku']}";
				break;
			default:
				// do not continue if url / body is empty
				return false;
		}
		// begin tag / end tag used to trim the result and get the essential xml
		return [
			'apiUrl' => $apiUrl,
			'body' => $body,
			'header' => [
				"Authorization: Basic " . base64_encode("{$this->supConfig['sns']['app_id']}:{$this->supConfig['sns']['app_password']}")
			]
		];
	}

	/**
	 * xml to array convert
	 */
	private function xml2Arr($xmlstring, $startTag, $endTag)
	{
		$xmlstring = ' ' . $xmlstring;
		$ini = strpos($xmlstring, $startTag);
		if ($ini == 0) {
			return '';
		}
		$len = strpos($xmlstring, $endTag, $ini) - $ini;
		$xmlPart = substr($xmlstring, $ini, $len) . $endTag;

		try {
			$xml = @simplexml_load_string($xmlPart, 'SimpleXMLElement', LIBXML_NOWARNING);
		} catch (\Exception $e) {
			return false;
		}
		return json_decode(json_encode((array) $xml), true);
	}

	// managing cron jobs
	/**
	 * Get the cron jon list in array format
	 */
	private function getJobs()
	{
		$output = shell_exec('crontab -l');
		return $this->stringToArray($output);
	}

	/**
	 * Add new cron job
	 */
	private function addJob($job = '')
	{
		$job = trim($job);
		if (!$this->doesJobExist($job)) {
			$jobs = $this->getJobs();
			$jobs[] = $job;
			return $this->saveJobs($jobs);
		}
		return false;
	}

	/**
	 * Remove a cron job
	 */
	private function removeJob($job = '')
	{
		$job = trim($job);
		if ($this->doesJobExist($job)) {
			$cronUrl = substr($job, strrpos($job, ' ') + 1);
			$jobs = $this->getJobs();
			foreach ($jobs as $jobIndex => $oldJob) {
				if (strrpos($oldJob, $cronUrl) !== false) {
					unset($jobs[$jobIndex]);
				}
			}
			return $this->saveJobs($jobs);
		}
		return false;
	}

	/**
	 * Save cron jobs
	 */
	private function saveJobs($jobs = array())
	{
		return shell_exec('echo "' . $this->arrayToString($jobs) . '" | crontab -');
	}
	/**
	 * Check if cron job exists
	 */
	private function doesJobExist($job = '')
	{
		$url = substr($job, strrpos($job, ' ') + 1);
		if ($url) {
			$jobs = $this->getJobs();
			foreach ($jobs as $oldJobs) {
				if (strrpos($oldJobs, $url) !== false) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Convert string to array for cronjob
	 */
	private function stringToArray($jobs = '')
	{
		// remove empty array_filter / trim() gets rid of the last \r\n
		return array_filter(explode("\r\n", trim($jobs)));
	}

	/**
	 * Convert array to sting for cronjob
	 */
	private function arrayToString($jobs = array())
	{
		return implode("\r\n", $jobs);
	}

	/**
	 * cron list to check if working properly
	 */
	public function getCronList($request, $response, $args)
	{
		$serverStatusCode = OPERATION_OKAY;

		$jsonResponse = [
			'status' => 1,
			'data' => $this->getJobs()
		];

		return response($response, [
			'data' => $jsonResponse, 'status' => $serverStatusCode
		]);
	}

	/**
	 * GET: Suplier list with auto_sync status
	 *
	 * @author dan@imprintnext.com
	 * @date   06 oct 2022
	 * @return Integer product id
	 */
	public function getSuppliersStatus($request, $response)
    {
		$serverStatusCode = OPERATION_OKAY;
		$jsonResponse = [
			'status' => 0,
			'message' => 'Please update the supplier\'s configuration',
			'auto_sync' => 0
		];
		$supplier = $request->getQueryParam('supplier');
		if (in_array($supplier, $this->providers)) {
			// get the supplier details (with backward compatibility)
			$catalogSupplier = $this->getSuppliersData();
			if (!empty($catalogSupplier)) {
				$slugData = $catalogSupplier->slug_data;
				if (array_key_exists($supplier, $slugData)) {
					$jsonResponse = [
						'status' => 1,
						'auto_sync' => intval($slugData[$supplier]['auto_sync'])
					];
				}
			}
		}
		return response($response, [
			'data' => $jsonResponse, 'status' => $serverStatusCode
		]);
	}
}
