<?php

namespace App\AutoProcessRules;

/**
 * Auto-process rule - the definition of the rule (contains the filters and the fields).
 * @author Gabi
 */
abstract class DefinitionAbstract
{

	/**
	 * List with all available fields
	 * @var array
	 */
	protected $fieldsConfig = [];

	/**
	 * The list of associated fields.
	 * @var array
	 */
	protected $fields = [];

	/**
	 * List with fields to be cached
	 * Value of array represents type of variable
	 * @var array
	 */
	protected $fieldsCacheConfig = [];

	/**
	 * List with all available filters
	 * @var array
	 */
	protected $filtersConfig = [];

	/**
	 * The list of associated filters.
	 * @var array
	 */
	protected $filters = [];

	/**
	 * List with filters for cache
	 * @var array
	 */
	protected $filtersCacheConfig = [];


	/**
	 * Adds a new field to the rule.
	 * @param String $name
	 * @param mixed $value
	 * @throws \Exception
	 */
	public function addField($name, $value)
	{
		if (!$this->fieldIsValid($name)) {
			throw new \Exception("Invalid field name `{$name}` for ".get_called_class().'.');
		}
		$this->fields[$name] = $value;
	}

	/**
	 * Indicates if the field is valid.
	 * @param String $field
	 */
	protected function fieldIsValid($field)
	{
		return in_array($field, $this->fieldsConfig);
	}

	/**
	 * Indicates if the filter is valid.
	 * @param String $field
	 */
	protected function filterIsValid($filter)
	{
		return in_array($filter, $this->filtersConfig);
	}


	/**
	 * Checks the fields associated for the current rule.
	 * @param \App\MessagesCollector $msgs
	 */
	abstract protected function checkFields(\App\MessagesCollector $msgs) ;

	/**
	 * Checks the common fields (corporations, customers, suppliers) for all AP rules.
	 */
	protected function checkCommonFilters(\App\MessagesCollector $msgs) :void
	{

		// check customers
		if ($this->filters['customers']) {
			if (!is_array($this->filters['customers'])) {
				$msgs->addError("The customers must be set as a list.");
			}
			else {
				foreach ($this->filters['customers'] as $idCustomer) {
					$customerEntity = \Entity\Customer::select($idCustomer);
					if (!$customerEntity->recordExists() or $customerEntity->deleted) {
						$msgs->addError(sprintf('Invalid customer id - %s.', $idCustomer));
					}
				}

				// check for duplicated customers
				if (count($this->filters['customers']) != count(array_unique($this->filters['customers']))) {
					$msgs->addWarning('Found some duplicated customers in the list.');
				}
			}
		}

		// check corporates
		if ($this->filters['corporates']) {
			if (!is_array($this->filters['corporates'])) {
				$msgs->addError("The corporations must be set as a list.");
			}
			else {
				foreach ($this->filters['corporates'] as $idCorporate) {
					$corporateEntity = \Entity\Corporate::select($idCorporate);
					if (!$corporateEntity->recordExists() or $corporateEntity->deleted) {
						$msgs->addError(sprintf('Invalid corporate id - %s.', $idCorporate));
					}
				}

				// check for duplicated corporates
				if (count($this->filters['corporates']) != count(array_unique($this->filters['corporates']))) {
					$msgs->addWarning('Found some duplicated corporations in the list.');
				}
			}
		}

		// check corporate groups
		if ($this->filters['corporateGroups']) {
			if (!is_array($this->filters['corporateGroups'])) {
				$msgs->addError("The corporation group elements must be set as a list.");
			}
			else {
				foreach ($this->filters['corporateGroups'] as $idCorpGroupElement) {
					$groupElementDetails = \App\Config\CorporateGroups::getInstance()->getGroupElementDetailsById($idCorpGroupElement);
					if (!$groupElementDetails) {
						$msgs->addError(sprintf('Invalid group element id - %s.', $idCorpGroupElement));
					}
				}

				// check for duplicated corporates
				if (count($this->filters['corporateGroups']) != count(array_unique($this->filters['corporateGroups']))) {
					$msgs->addWarning('Found some duplicated corporation group elements in the list.');
				}
			}
		}

		// check suppliers
		if ($this->filters['suppliers']) {
			if (!is_array($this->filters['suppliers'])) {
				$msgs->addError("The suppliers must be set as a list.");
			}
			else {
				foreach ($this->filters['suppliers'] as $idSupplier) {
					$supplierEntity = \Entity\Supplier::select($idSupplier);
					if (!$supplierEntity->recordExists() or $supplierEntity->deleted) {
						$msgs->addError(sprintf('Invalid supplier id - %s.', $idSupplier));
					}
				}

				// check for duplicated suppliers
				if (count($this->filters['suppliers']) != count(array_unique($this->filters['suppliers']))) {
					$msgs->addWarning('Found some duplicated suppliers in the list.');
				}
			}
		}

	}

	/**
	 * Adds a new filter to the current rule.
	 * @param String $field
	 * @param Mixed $value
	 * @throws \Exception
	 */
	public function addFilter($field, $value)
	{
		if (!$this->filterIsValid($field)) {
			throw new \Exception("Invalid filter field `{$field}` for ".get_called_class().'.');
		}
		$this->filters[$field] = $value;
	}


	/**
	 * Checks the filters associated for the current rule.
	 * @param \App\MessagesCollector $msgs
	 */
	abstract protected function checkFilters(\App\MessagesCollector $msgs) ;

	/**
	 * Checks the whole rule (fields and filters).
	 * @throws \App\MessagesException
	 */
	public function check()
	{

		$msgs = new \App\MessagesCollector();

		// check partner ids (customers, corporates, suppliers)
		$this->checkCommonFilters($msgs);

		// check custom filters
		$this->checkFilters($msgs);

		// check custom fields
		$this->checkFields($msgs);

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

	}


	/**
	 * Returns the full list of associated fields and their values.
	 * @return Array
	 */
	public function getFields()
	{
		return $this->fields;
	}

	public function getField($field)
	{
		return $this->fields[$field];
	}

	/**
	 * Returns the full list of associated filters and their values.
	 * @return Array
	 */
	public function getFilters()
	{
		return $this->filters;
	}

	public function getFilter($filter)
	{
		return $this->filters[$filter];
	}

	/**
	 * Gets the rule type (strips the ns and the "Rule_").
	 * @return String
	 */
	public function getType()
	{
		return substr( stripClassNamespace(get_called_class()), 0, -11); // format: \App\AutoProcessRules\ImportedServiceProcess_Definition
	}

	/**
	 * Returns list with fields values in a standard format. This array will be used by the entity in order to cache values
	 * @return Array
	 */
	public function getFieldsValuesBulk()
	{
		$fieldsValues = [];

		foreach ($this->fields as $var => $value) {
			if ($value and array_key_exists($var, $this->fieldsCacheConfig)) {
				$fieldsValues[$var] = [
					'value' => $value,
					'type' =>	$this->fieldsCacheConfig[$var],
				];
			}
		}

		return $fieldsValues;
	}

	/**
	 * Returns list with filters values in a standard format. This array will be used by the entity in order to cache values
	 * @return Array
	 */
	public function getFiltersValuesBulk()
	{
		$filtersValues = [];

		foreach ($this->filters as $var => $value) {
			if ($value and array_key_exists($var, $this->filtersCacheConfig)) {
				if ($this->filtersCacheConfig[$var] == 'remarks') {
					$cacheValue = [];
					foreach ($value as $val) {
						$cacheValue[] = $val['var'];
					}
				}
				else {
					$cacheValue = $value;
				}
				$filtersValues[$var] = [
					'value' => 	$cacheValue,
					'type' =>	$this->filtersCacheConfig[$var],
				];
			}
		}

		return $filtersValues;
	}

	/**
	 * Get the fields description for the rule.
	 */
	abstract public function getDescriptionFields();

	/**
	 * Get the filters description for the rule.
	 */
	abstract public function getDescriptionFilters();

	/**
	 * Populate the description for some common filters (partner details).
	 * @param array $descrFilters
	 * @param string $filterName
	 */
	protected function populateDescriptionCommonFilters(array &$descrFilters, string $filterName) :void
	{

		$filters = $this->getFilters();

		switch ($filterName) {
			case 'corporates':
				if ($filters['corporates']) {
					$corporates = [];
					foreach ($filters['corporates'] as $idCorp) {
						$corporates[] = \Entity\Corporate::select($idCorp)->alias;
					}
					$descrFilters[] = sprintf('corporations - %s', implode(', ', $corporates));
				}
			break;

			case 'corporateGroups':
				if ($filters['corporateGroups']) {
					$groupElements = [];
					foreach ($filters['corporateGroups'] as $idGroupElem) {
						$grElemDet = \App\Config\CorporateGroups::getInstance()->getGroupElementDetailsById($idGroupElem);
						$groupElements[] = $grElemDet['groupName'].': '.$grElemDet['groupElementName'];
					}
					$descrFilters[] = sprintf('corporations groups - %s', implode(', ', $groupElements));
				}
			break;

			case 'customers':
				if ($filters['customers']) {
					$customer = [];
					foreach ($filters['customers'] as $idCust) {
						$customer[] = \Entity\Customer::select($idCust)->name;
					}
					$descrFilters[] = sprintf('customers - %s', implode(', ', $customer));
				}
			break;

			case 'suppliers':
				if ($filters['suppliers']) {
					$suppliers = [];
					foreach ($filters['suppliers'] as $idSupp) {
						$suppliers[] = \Entity\Supplier::select($idSupp)->alias;
					}
					$descrFilters[] = sprintf("suppliers - %s", implode(',', $suppliers));
				}
			break;
		}

	}


}
