<?php

/**
 *
 *    Copyright (C) 2017 onOffice GmbH
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

declare(strict_types=1);

namespace onOffice\WPlugin\Filter;

use DI\DependencyException;
use DI\NotFoundException;
use onOffice\SDK\onOfficeSDK;
use onOffice\WPlugin\DataView\DataListView;
use onOffice\WPlugin\Favorites;
use onOffice\WPlugin\Field\Collection\FieldsCollectionBuilderShort;
use onOffice\WPlugin\GeoPosition;
use onOffice\WPlugin\Types\FieldsCollection;

class DefaultFilterBuilderListView
	implements DefaultFilterBuilder
{
	/** @var DataListView */
	private $_pDataListView = null;

	/** @var DefaultFilterBuilderListViewEnvironment */
	private $_pEnvironment = null;

	/** @var FieldsCollectionBuilderShort  */
	private $_pFieldsCollectionBuilderShort;

	private $_geofilter = null;

	/** @var array */
	private $_defaultFilter = [
		'veroeffentlichen' => [
			['op' => '=', 'val' => 1],
		],
	];

	/**
	 * @param DataListView $pDataListView
	 * @param FieldsCollectionBuilderShort $pFieldsCollectionBuilderShort
	 * @param DefaultFilterBuilderListViewEnvironment|null $pEnvironment
	 */
	public function __construct(
		DataListView $pDataListView,
		FieldsCollectionBuilderShort $pFieldsCollectionBuilderShort,
		DefaultFilterBuilderListViewEnvironment $pEnvironment = null)
	{
		$this->_pDataListView = $pDataListView;
		$this->_pEnvironment = $pEnvironment ?? new DefaultFilterBuilderListViewEnvironmentDefault();
		$this->_pFieldsCollectionBuilderShort = $pFieldsCollectionBuilderShort;
	}

	/**
     * Resolves the DataListView to be used.
     * * Returns the provided override if it is not null; otherwise, falls back 
     * to the internal default DataListView instance.
     *
     * @param DataListView|null $pDataListViewOverride Optional override instance.
     * @return DataListView The resolved DataListView instance.
     * @throws \TypeError If no override is provided and the internal instance is not initialized.
     */
	public function getDataListView(?DataListView $pDataListViewOverride = null): DataListView
	{
		return $pDataListViewOverride ?? $this->_pDataListView;
	}

	/**
	 * @param DataListView|null $pDataListViewOverride optional override for DataListView
	 * @return array
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	public function buildFilter(): array
	{
		$useDataListView = $this->getDataListView();
		$filterableFields = $useDataListView->getFilterableFields();

		// Geo position will be done later
		if (in_array(GeoPosition::FIELD_GEO_POSITION, $filterableFields)) {
			$position = array_search(GeoPosition::FIELD_GEO_POSITION, $filterableFields, true);
			unset($filterableFields[$position]);
		}

		$filterableFields = $this->filterActiveFilterableFields($filterableFields);
		$fieldFilter = $this->_pEnvironment->getFilterBuilderInputVariables()->getPostFieldsFilter($filterableFields);
		$filter = array_merge($this->_defaultFilter, $fieldFilter, $this->_geofilter ?? []);

		switch ($useDataListView->getListType()) {
			case DataListView::LISTVIEW_TYPE_FAVORITES:
				$filter = $this->getFavoritesFilter();
				break;
		}

		$priceFields = $useDataListView->getListFieldsShowPriceOnRequest();

		if (count(array_intersect($priceFields, array_keys($filter))) > 0) {
			$filter['preisAufAnfrage'][] = ['op' => '!=', 'val' => 1];
		}

		$filter = $this->addEstateCityFilterWhenConvertTextToSelect($filter, $filterableFields);
		$filterWithRegion = $this->addSubRegionFilter($filter, $filterableFields);
		$filterWithRegion = $this->addRentalPurchaseFilter($filterWithRegion);

		return $filterWithRegion;
	}

	/**
	 * @param array $filterableFields
	 * @return array
	 * @throws DependencyException
	 * @throws NotFoundException
	 */
	private function filterActiveFilterableFields(array $filterableFields): array
	{
		$pFieldsCollection = new FieldsCollection;
		$this->_pFieldsCollectionBuilderShort->addFieldsAddressEstate($pFieldsCollection);
		$activeFilterableFields = [];

		foreach ($filterableFields as $field) {
			if ($pFieldsCollection->containsFieldByModule(onOfficeSDK::MODULE_ESTATE, $field)) {
				$activeFilterableFields []= $field;
			}
		}

		return $activeFilterableFields;
	}

	/**
	 * @param array $baseFilter
	 * @param array $filterableFields
	 * @return array
	 */
	private function addSubRegionFilter(array $baseFilter, array $filterableFields): array
	{
		if (in_array('regionaler_zusatz', $filterableFields)) {
			$additionalRegions = [];
			$pRegionController = $this->_pEnvironment->getRegionController();
			$pRegionController->fetchRegions();
			$regionValue = (array)$this->_pEnvironment
				->getInputVariableReader()->getFieldValue('regionaler_zusatz');
			foreach ($regionValue as $region) {
				$additionalRegions = array_merge($additionalRegions,
					$pRegionController->getSubRegionsByParentRegion($region));
				$additionalRegions []= $region;
			}

			if ($additionalRegions !== []) {
				$baseFilter['regionaler_zusatz'] = [
					['op' => 'in', 'val' => $additionalRegions],
				];
			}
		}
		return $baseFilter;
	}

	/**
	 * @param array $baseFilter
	 * @param array $filterableFields
	 * @return array
	 */
	private function addEstateCityFilterWhenConvertTextToSelect(array $baseFilter, array $filterableFields): array
	{
		if (in_array('ort', $filterableFields) && !empty($this->_pDataListView->getConvertTextToSelectForCityField())) {
			$additionalEstateCities = [];
			$estateCityValue = $this->_pEnvironment->getInputVariableReader()->getFieldValue('ort');

			if (!is_array($estateCityValue) || empty($estateCityValue)) {
				return $baseFilter;
			}
			foreach ($estateCityValue as $value) {
				$additionalEstateCities []= $value;
			}

			if ($additionalEstateCities !== []) {
				$baseFilter['ort'] = [
					['op' => 'in', 'val' => $additionalEstateCities],
				];
			}
		}

		return $baseFilter;
	}

	/**
	 * @return array
	 */
	public function getDefaultFilter(): array
	{
		return $this->_defaultFilter;
	}
	/**
	 * @return array
	 */
	private function getFavoritesFilter(): array
	{
		$ids = Favorites::getAllFavorizedIds();

		$filter = $this->_defaultFilter;
		$filter['Id'] = [
			['op' => 'in', 'val' => $ids],
		];

		return $filter;
	}
	public function setFilterGeoSearch(object $geo)
	{
		$this->_geofilter = [
			'geo' => [
				['op' => 'geo', 'val' => $geo->km, 'min' => $geo->min ?? null, 'max' => $geo->max ?? null, 'country' => $geo->country ?? null]
			]
		];
	}

	/**
	 * @param array $filter
	 * @return array
	 */
	private function addRentalPurchaseFilter(array $filter): array
	{
		$purchaseFields = ['kaufpreis', 'kaufpreis_pro_qm', 'erbpacht', 'pacht'];
		$rentalFields = ['nettokaltmiete', 'warmmiete', 'kaltmiete', 'miete_pauschal', 'saisonmiete', 'wochmietbto', 'mietpreis_pro_qm'];

		$hasRentalInput = false;
		$hasPurchaseInput = false;

		foreach ($_GET as $key => $value) {
			if (empty($value)) continue;

			foreach ($rentalFields as $field) {
				if ($key === $field || strpos($key, $field) === 0 || strpos($key, $field . '__') === 0) {
					$hasRentalInput = true;
					break;
				}
			}

			foreach ($purchaseFields as $field) {
				if ($key === $field || strpos($key, $field) === 0 || strpos($key, $field . '__') === 0) {
					$hasPurchaseInput = true;
					break;
				}
			}
		}

		if ($hasRentalInput && !$hasPurchaseInput) {
			$filter['kaufpreis'][] = ['op' => '=', 'val' => '0.00'];
			$filter['kaltmiete'][] = ['op' => '!=', 'val' => '0.00'];
		}

		if ($hasPurchaseInput && !$hasRentalInput) {
			$filter['kaltmiete'][] = ['op' => '=', 'val' => '0.00'];
			$filter['kaufpreis'][] = ['op' => '!=', 'val' => '0.00'];
		}

		return $filter;
	}
}
