<?php

namespace App\Config;

/**
 * The class is used for managing the list of documents
 * @author Gabriel Grosu
 */
class IssueDocuments extends XmlDynamicRecordsAbstract
{

	/**
	 * Stores the configuration file (relative to the "files/config/" dir). For config files stored
	 * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml).
	 * @var String
	 */
	protected $fileName = CONFIG_FILE_ISSUEDOCUMENTS;



	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlBasicAbstract::getAll_rawData_fileNotExists()
	 */
	protected function getAll_rawData_fileNotExists()
	{
		return [];
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_list()
	 */
	protected function getAll_rawData_extractDataFromFile_list(\SimpleXMLElement $xml)
	{
		// gets the final list of items to be returned
		$return = [];
		foreach ($xml->doc as $docXml) {

			$docTypeCode = (String)$docXml['code'];

			$return[$docTypeCode] = [
				'name'					=> (String)$docXml->name,
				'active'				=> (Int)$docXml['active'],
				'pivot'					=> (String)$docXml['pivot'],
				'mainDocOnPivot'		=> (String)$docXml['mainDocOnPivot'],
				'docNumberType'			=> (String)$docXml->docNumberType,
				'numberReuse'			=> (Int)$docXml->numberReuse,
				'docDate'				=> (String)$docXml->docDate,
				'docCategory'			=> (String)$docXml->docCategory,
				'indicateCopy'			=> (Int)$docXml->flags['indicateCopy'],
				'forceSameTemplate'		=> (Int)$docXml->flags['forceSameTemplate'],
				'printOriginalOnce'		=> (Int)$docXml->flags['printOriginalOnce'],
				'autoGenerateFirstCopy'	=> (Int)$docXml->flags['autoGenerateFirstCopy'],
				'maxGenerationCount'	=> (Int)$docXml->flags['maxGenerationCount'],
			];

			// get list of service codes
			if ($docXml->serviceTypes) {
				$serviceTypes = [];
				foreach($docXml->serviceTypes->service as $serviceXml) {
					$serviceTypes[] = (string)$serviceXml['code'];
				}
				if ($serviceTypes) {
					$return[$docTypeCode]['serviceTypes'] = $serviceTypes;
				}
			}

		}
		return $return;
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_default()
	 */
	protected function getAll_rawData_extractDataFromFile_default(\SimpleXMLElement $xml)
	{
		return '';
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_mandatory()
	 */
	protected function getAll_rawData_extractDataFromFile_mandatory(\SimpleXMLElement $xml)
	{
		return true;
	}

	protected function itemCheck($code, $itemDetails)
	{

		// CHECK CODE -----------------------------------------------------------------------------
		if (!preg_match('/^[a-zA-Z0-9\_]+$/', $code)) {
			$this->addErrorMessage('The doc type code must contain only digits, letters or underscore.');
		} elseif (mb_strlen($code)>50) {
			$this->addErrorMessage('The doc type code must have max 50 chars.');
		}

		// CHECK NAME -----------------------------------------------------------------------------
		if (!$itemDetails['name']) {
			$this->addErrorMessage('The doc type name is not set.');
		} elseif (mb_strlen($itemDetails['name'])>50) {
			$this->addErrorMessage('The doc type name must have 50 chars max.');
		}

		// CHECK PIVOT ----------------------------------------------------------------------------
		$validPivots = ['order', 'service', 'invoice', 'cashing', 'cashBookDoc', 'payment', 'importedService' , 'importedDocument' , 'balance'];
		if (!$itemDetails['pivot']) {
			$this->addErrorMessage('The doc pivot is not selected.');
		} elseif (!in_array($itemDetails['pivot'], $validPivots)) {
			$this->addErrorMessage('The doc pivot is not valid.');
		}
		// pivot must not change
		$prevPivot = self::getInstance()->getDocPivot($code);
		if ($prevPivot and $prevPivot!=$itemDetails['pivot']) {
			$this->addErrorMessage('The doc pivot cannot change once added.');
		}

		// CHECK DOC CATEGORY ---------------------------------------------------------------------
		if (in_array($itemDetails['pivot'], ['cashing', 'payment', 'importedDocument', 'cashBookDoc']) and !$itemDetails['docCategory']) {
			$this->addErrorMessage('You must set the category for this pivot.');
		}
		// validate docCategory based on its pivot
		if ($itemDetails['docCategory']) {
			switch ($itemDetails['pivot']) {
				case 'cashing': {
					if (!\App\Config\CashingDocuments::getInstance()->isActive($itemDetails['docCategory'])) {
						$this->addErrorMessage('The doc category is not valid - this cashing document does not exist or is inactive.');
					}
					break;
				}
				case 'payment': {
					if (!\App\Config\PaymentDocuments::getInstance()->isActive($itemDetails['docCategory'])) {
						$this->addErrorMessage('The doc category is not valid - this payment document does not exist or is inactive.');
					}
					break;
				}
				case 'importedDocument': {
					if (!\App\Config\CashingDocuments::getInstance()->isActive($itemDetails['docCategory']) and !\App\Config\PaymentDocuments::getInstance()->isActive($itemDetails['docCategory'])) {
						$this->addErrorMessage('The doc category is not valid - this document is neither a cashing nor a payment or is associated to an inactive document.');
					}
					break;
				}
				case 'cashBookDoc': {
					$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();
					if (!$CFG['cashBook']['documentTypes']['cashing'][$itemDetails['docCategory']] and !$CFG['cashBook']['documentTypes']['payment'][$itemDetails['docCategory']]) {
						$this->addErrorMessage('The doc category is not valid - this is neither a cashing nor a payment cashbook document.');
					}
					break;
				}
				default: {
					$this->addErrorMessage('You must not set the category for this pivot.');
				}
			}
		}

		// CHECK NUMBER TYPE ----------------------------------------------------------------------
		if (!$itemDetails['docNumberType']) {
			$this->addErrorMessage('The number type is not set.');
		} elseif (!in_array($itemDetails['docNumberType'], ['chooseSeries', 'pivotSeries', 'manualNumber', 'pivotNumber'])) {
			$this->addErrorMessage('The number type is not valid.');
		}
		// validate number type based on pivot - some pivots cannot have series associated
		if ($itemDetails['docNumberType']=='pivotSeries' and in_array($itemDetails['pivot'], ['order', 'service', 'importedService', 'importedDocument', 'cashBookDoc', 'balance'])) {
			$this->addErrorMessage('You cannot select `pivot series` for documents associated with this pivot.');
		}

		if ($itemDetails['docNumberType']=='pivotNumber' and in_array($itemDetails['pivot'], ['order', 'service', 'importedService', 'balance'])) {
			$this->addErrorMessage('You cannot select `pivot number` for documents associated with this pivot.');
		}
		// TODO: [notImportant] if pivotSeries or pivotNumber - it should be validated against the original document settings (eg. cashing, payment, etc) - de ex. daca am pivotSeries trebuie sa am activ campul la nivel de incasare.

		// CHECK DOCUMENT DATE --------------------------------------------------------------------
		if (!$itemDetails['docDate']) {
			$this->addErrorMessage('The document date is not set.');
		} elseif (!in_array($itemDetails['docDate'], ['today', 'pivotDate', 'manual'])) {
			$this->addErrorMessage('The document date is not valid.');
		}
		if ($itemDetails['docDate']=='pivotDate' and in_array($itemDetails['pivot'], ['order', 'service', 'importedService'])) {
			$this->addErrorMessage('The document date cannot be `pivot date` for this pivot.');
		}

		// CHECK SERVICE TYPES --------------------------------------------------------------------
		if ($itemDetails['serviceTypes'] and $itemDetails['pivot']!='service') {
			$this->addErrorMessage('You can set the service types only for `service` pivot.');
		}
		if ($itemDetails['serviceTypes']) {
			foreach($itemDetails['serviceTypes'] as $serviceCode) {
				if (!\App\Config\Services_Basic::getInstance()->checkService($serviceCode)) {
					$this->addErrorMessage(sprintf('Invalid service code selected (%s).', $serviceCode));
				}
			}
		}

		// CHECK FLAGS ----------------------------------------------------------------------------
		if ($itemDetails['maxGenerationCount'] and !\Engine\Validator::set($itemDetails['maxGenerationCount'])->isDigits()) {
			$this->addErrorMessage('The max generation count must be set as a positive integer value.');
		}

	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\BaseAbstract::save_check()
	 */
	protected function save_check()
	{

		$docTypeNames = [];

		// CHECK ITEMS ----------------------------------------------------------------------------
		foreach($this->itemsList as $docTypeDetails) {

			// check doc type name uniqueness (per pivot)
			if ($docTypeNames[$docTypeDetails['pivot']] and in_array($docTypeDetails['name'], $docTypeNames[$docTypeDetails['pivot']])) {
				$this->addErrorMessage(sprintf('The doc type `%s` is added more than once in the list of docs associated to `%s` pivot.', $docTypeDetails['name'], $docTypeDetails['pivot']));
			}
			$docTypeNames[$docTypeDetails['pivot']][] = $docTypeDetails['name'];

		}

		// check that all previous documents still exist
		foreach(self::getInstance()->getAll() as $prevDocTypeCode=>$nada) {
			if (!$this->itemsList[$prevDocTypeCode]) {
				$this->addErrorMessage(sprintf('The doc type `%s` must not be deleted.', $prevDocTypeCode));
			}
		}

	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlBasicAbstract::save_write_do_getXml()
	 */
	protected function save_write_do_getXml()
	{

		$xml=simplexml_load_string('<?xml version="1.0" encoding="UTF-8"?><issueDocuments />');
		foreach ($this->itemsList as $docTypeCode=>$docTypeDet) {
			// add main info
			$thisDocTypeXml						= $xml->addChild('doc', '');
			$thisDocTypeXml['code']				= $docTypeCode;
			$thisDocTypeXml['mainDocOnPivot']	= (bool)$docTypeDet['mainDocOnPivot'];
			$thisDocTypeXml['active']			= (bool)$docTypeDet['active'];
			$thisDocTypeXml['pivot']			= $docTypeDet['pivot'];
			$thisDocTypeXml->name				= $docTypeDet['name'];
			$thisDocTypeXml->docCategory		= $docTypeDet['docCategory'];
			$thisDocTypeXml->docNumberType		= $docTypeDet['docNumberType'];
			$thisDocTypeXml->numberReuse		= (bool)$docTypeDet['numberReuse'];
			$thisDocTypeXml->docDate			= $docTypeDet['docDate'];

			if ($docTypeDet['serviceTypes']) {
				$docServTypesXml = $thisDocTypeXml->addChild('serviceTypes', '');
				foreach ($docTypeDet['serviceTypes'] as $serviceCode) {
					$thisServTypeXml = $docServTypesXml->addChild('service', '');
					$thisServTypeXml['code'] = $serviceCode;
				}
			}

			// add flags
			if ($docTypeDet['forceSameTemplate'] or $docTypeDet['maxGenerationCount'] or $docTypeDet['autoGenerateFirstCopy'] or $docTypeDet['printOriginalOnce'] or $docTypeDet['indicateCopy']) {
				$docFlagsXml = $thisDocTypeXml->addChild('flags', '');
				if ($docTypeDet['forceSameTemplate']) {
					$docFlagsXml['forceSameTemplate']		= (bool)$docTypeDet['forceSameTemplate'];
				}
				if ($docTypeDet['maxGenerationCount']) {
					$docFlagsXml['maxGenerationCount']		= (Int)$docTypeDet['maxGenerationCount'];
				}
				if ($docTypeDet['autoGenerateFirstCopy']) {
					$docFlagsXml['autoGenerateFirstCopy']	= (bool)$docTypeDet['autoGenerateFirstCopy'];
				}
				if ($docTypeDet['printOriginalOnce']) {
					$docFlagsXml['printOriginalOnce']		= (bool)$docTypeDet['printOriginalOnce'];
				}
				if ($docTypeDet['indicateCopy']) {
					$docFlagsXml['indicateCopy']			= (bool)$docTypeDet['indicateCopy'];
				}
			}

		}

		// return the xml content of the current object
		return $xml;

	}


	/**
	 * Returns all active doc types with translation grouped by pivot.
	 * @return Array
	 */
	public function getDocsListByPivot()
	{
		$return = [];
		foreach($this->getAll() as $docType=>$docDet) {
			if ($docDet['active']) {
				$return[$docDet['pivot']][$docType] = $docDet['name'];
			}
		}
		return $return;
	}

	/**
	 * Returns associative array of active doc type code=>name for 'order' pivot.
	 * @return array
	 */
	public function getOrderDocsList()
	{
		return $this->getDocsByPivot('order');
	}

	/**
	 * Returns associative array of active doc type code=>name for 'invoice' pivot.
	 * @return array
	 */
	public function getInvoiceDocsList()
	{
		return $this->getDocsByPivot('invoice');
	}

	/**
	 * Returns associative array of active doc type code=>name for 'balance' pivot.
	 * @return array
	 */
	public function getBalanceDocsList()
	{
		return $this->getDocsByPivot('balance');
	}

	/**
	 * Returns associative array of active doc type code=>name for 'importedService' pivot.
	 * @return array
	 */
	public function getImportedServiceDocsList()
	{
		return $this->getDocsByPivot('importedService');
	}

	/**
	 * Returns associative array of active doc type code=>name for 'service' pivot.
	 * If no service code set - all unioned document types are displayed or if a service code is
	 * set, then only those doc types. Doc types with no service associations are completely ignored.
	 * @return array
	 */
	public function getServiceDocsList($serviceCode=null)
	{
		$return = [];
		foreach($this->getAll() as $docType=>$docDet) {
			if ($docDet['active'] and $docDet['pivot']=='service' and $docDet['serviceTypes'] and (!$serviceCode or in_array($serviceCode, $docDet['serviceTypes'])) ) {
				$return[$docType] = $docDet['name'];
			}
		}
		return $return;
	}

	/**
	 * Returns the list of active cashing documents for a certain category.
	 * @param string $docCategory
	 * @return array
	 */
	public function getCashingDocsList($docCategory=null)
	{
		return $this->getDocsByPivot('cashing', $docCategory);
	}

	/**
	 * Returns the list of active cashing documents for a certain category.
	 * @param string $docCategory
	 * @return array
	 */
	public function getPaymentDocsList($docCategory=null)
	{
		return $this->getDocsByPivot('payment', $docCategory);
	}

	/**
	 * Returns the list of active cashing documents for a certain category.
	 * @param string $docCategory
	 * @return array
	 */
	public function getCashBookDocsList($docCategory)
	{
		return $this->getDocsByPivot('cashBookDoc', $docCategory);
	}

	/**
	 * Returns the list of active cashing documents for a certain category.
	 * @param string $docCategory
	 * @return array
	 */
	public function getImportedDocsList($docCategory)
	{
		return $this->getDocsByPivot('importedDocument', $docCategory);
	}

	/**
	 * Returns the list of document types for a certain pivot. Can also filter by doc category.
	 * @param string $pivot
	 * @param string $docCategory
	 */
	public function getDocsByPivot($pivot, $docCategory = null)
	{
		$return = [];
		foreach ($this->getAll() as $docTypeCode=>$docTypeDet) {
			if ($docTypeDet['active'] and $docTypeDet['pivot']==$pivot and (!$docCategory or $docTypeDet['docCategory']==$docCategory) ) {
				$return[$docTypeCode] = $docTypeDet['name'];
			}
		}
		return $return;
	}

	/**
	 * Returns the first main doc associated to a pivot and (optionally) with a doc category.
	 * @param string $pivot
	 * @param string $docCategory
	 */
	public function getFirstMainDoc($pivot, $docCategory=null)
	{
		foreach ($this->getAll() as $docTypeCode=>$docTypeDet) {
			if ($docTypeDet['active'] and $docTypeDet['mainDocOnPivot'] and $docTypeDet['pivot']==$pivot and (!$docCategory or $docTypeDet['docCategory']==$docCategory) ) {
				return $docTypeCode;
			}
		}
		return null;
	}


	/**
	 * Returns if the doc type exists.
	 * @param string $docType
	 * @return boolean
	 */
	public function exists($docType)
	{
		return $this->get($docType) ? true : false;
	}

	/**
	 * Returns if the doc type is active.
	 * @param string $docType
	 * @return boolean
	 */
	public function isActive($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['active'];
	}

	/**
	 * Returns the doc type name.
	 */
	public function getDocTypeName($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['name'];
	}

	/**
	 * Returns the doc type category.
	 * @param string $docType
	 * @return string
	 */
	public function getDocCategory($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['docCategory'];
	}

	public function isMainDocOnPivot($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['mainDocOnPivot'];
	}

	/**
	 * Returns the doc number type.
	 * @param string $docType
	 * @return string
	 */
	public function getDocNumberType($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['docNumberType'];
	}

	/**
	 * Returns the doc date type.
	 * @param string $docType
	 * @return string
	 */
	public function getDocDate($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['docDate'];
	}

	/**
	 * Returns the doc type pivot.
	 * @param string $docType
	 * @return string
	 */
	public function getDocPivot($docType)
	{
		$docTypeConfigs = $this->get($docType);
		return $docTypeConfigs['pivot'];
	}

	/**
	 * Filters and returns the list of document types based on the provided criteria: pivot, main document status, and active status.
	 *
	 * @param string $pivot The pivot identifier to filter document types.
	 * @param bool|null $mainDoc Specifies whether to filter by main document on the pivot (true, false, or null for no filtering).
	 * @param bool|null $active Specifies whether to filter by active status (true, false, or null for no filtering).
	 *
	 * @return array An array of filtered document types matching the specified criteria.
	 */
	public function getFilteredDocs(string $pivot, ?bool $mainDoc, ?bool $active) :array
	{

		$return = [];

		foreach ($this->getAll() as $docTypeCode=>$docTypeDet) {
			if ($pivot and $docTypeDet['pivot']!=$pivot) { // filter by pivot
				continue;
			}
			if ($mainDoc!==null and $docTypeDet['mainDocOnPivot']!=$mainDoc) { // filter by main doc
				continue;
			}
			if ($active!==null and $docTypeDet['active']!=$active) { // filter by active
				continue;
			}
			$return[$docTypeCode] = $docTypeDet;
		}

		return $return;

	}


}
