<?php

namespace XF\Searcher;

use XF\Finder\UserFinder;
use XF\Mvc\Entity\Finder;
use XF\Repository\ConnectedAccountRepository;
use XF\Repository\UserGroupRepository;

use function count, in_array, is_array;

/**
 * @method UserFinder getFinder()
 */
class User extends AbstractSearcher
{
	protected $allowedRelations = ['Option', 'Profile', 'Privacy'];

	protected $formats = [
		'email' => 'like',
		'register_date' => 'date',
		'last_activity' => 'date',
	];

	protected $arrayValueKeys = [
		'user_field',
	];

	protected $order = [['username', 'asc']];

	protected $stringSortOrders = ['username'];

	protected function getEntityType()
	{
		return 'XF:User';
	}

	protected function getDefaultOrderOptions()
	{
		$orders = [
			'username' => \XF::phrase('user_name'),
			'register_date' => \XF::phrase('join_date'),
			'last_activity' => \XF::phrase('last_activity'),
			'message_count' => \XF::phrase('messages'),
			'question_solution_count' => \XF::phrase('solutions'),
			'trophy_points' => \XF::phrase('trophy_points'),
			'reaction_score' => \XF::phrase('reaction_score'),
		];

		$stringSortOrders = $this->stringSortOrders;

		\XF::fire('user_searcher_orders', [$this, &$orders, &$stringSortOrders]);

		$this->stringSortOrders = $stringSortOrders;

		return $orders;
	}

	public function isSortOrderNumeric($sortOrder)
	{
		return !in_array($sortOrder, $this->stringSortOrders);
	}

	protected function validateSpecialCriteriaValue($key, &$value, $column, $format, $relation)
	{
		if ($key == 'no_secondary_group_ids' && !$value)
		{
			return false;
		}

		if ($key == 'user_field')
		{
			$exactMatchFields = !empty($value['exact']) ? $value['exact'] : []; // used for multi-choice field searches
			$customFields = $value;
			unset($customFields['exact']);

			foreach ($customFields AS $fieldId => $fieldValue)
			{
				if ($fieldValue === '' || (is_array($fieldValue) && !$fieldValue))
				{
					unset($customFields[$fieldId]);
				}
			}
			foreach ($exactMatchFields AS $fieldId => $fieldValue)
			{
				if ($fieldValue === '' || (is_array($fieldValue) && !$fieldValue))
				{
					unset($exactMatchFields[$fieldId]);
				}
			}
			if ($exactMatchFields)
			{
				$customFields['exact'] = $exactMatchFields;
			}

			$value = $customFields;
			if (!$customFields)
			{
				return false;
			}
		}

		return null;
	}

	protected function applySpecialCriteriaValue(Finder $finder, $key, $value, $column, $format, $relation)
	{
		if ($key == 'username')
		{
			if (is_array($value))
			{
				$username = $value['name'] ?? '';
				$exactMatch = !empty($value['exact_match']);
			}
			else
			{
				$username = $value;
				$exactMatch = false;
			}

			if ($exactMatch)
			{
				$finder->where('username', '=', $username);
			}
			else
			{
				$finder->where('username', 'LIKE', $finder->escapeLike($username, '%?%'));
			}

			return true;
		}

		if ($key == 'email')
		{
			if (is_array($value))
			{
				$email = $value['address'] ?? '';
				$exactMatch = !empty($value['exact_match']);
			}
			else
			{
				$email = $value;
				$exactMatch = false;
			}

			if ($exactMatch)
			{
				$finder->where('email', '=', $email);
			}
			else
			{
				$finder->where('email', 'LIKE', $finder->escapeLike($email, '%?%'));
			}

			return true;
		}

		if ($key == 'secondary_group_ids' || $key == 'not_secondary_group_ids')
		{
			if (!is_array($value))
			{
				$value = [$value];
			}

			$columnName = $finder->columnSqlName('secondary_group_ids');
			$positiveMatch = ($key == 'secondary_group_ids');
			$parts = [];
			foreach ($value AS $part)
			{
				if ($positiveMatch)
				{
					$parts[] = 'FIND_IN_SET(' . $finder->quote($part) . ', ' . $columnName . ')';
				}
				else
				{
					$parts[] = 'FIND_IN_SET(' . $finder->quote($part) . ', ' . $columnName . ') = 0';
				}
			}
			if ($parts)
			{
				$joiner = $positiveMatch ? ' OR ' : ' AND ';
				$finder->whereSql(implode($joiner, $parts));
			}
			return true;
		}

		if ($key == 'no_secondary_group_ids' && $value)
		{
			$finder->where('secondary_group_ids', '=', '');
			return true;
		}

		if ($key == 'no_empty_email')
		{
			$finder->where('email', '<>', '');
			return true;
		}

		if ($key == 'not_user_id')
		{
			$finder->where('user_id', '<>', $value);
			return true;
		}

		if ($key == 'user_field')
		{
			$exactMatchFields = !empty($value['exact']) ? $value['exact'] : []; // used for multi-choice field searches
			$customFields = $value + $exactMatchFields;
			unset($customFields['exact']);

			foreach ($customFields AS $fieldId => $value)
			{
				if ($value === '' || (is_array($value) && !$value))
				{
					continue;
				}

				$finder->with('Profile.CustomFields|' . $fieldId);
				$isExact = !empty($exactMatchFields[$fieldId]);
				$conditions = [];

				foreach ((array) $value AS $possible)
				{
					$columnName = 'Profile.CustomFields|' . $fieldId . '.field_value';
					if ($isExact)
					{
						$conditions[] = [$columnName, '=', $possible];
					}
					else
					{
						$conditions[] = [$columnName, 'LIKE', $finder->escapeLike($possible, '%?%')];
					}
				}

				if ($conditions)
				{
					$finder->whereOr($conditions);
				}
			}
		}

		if ($key == 'security_lock')
		{
			if (!is_array($value))
			{
				$value = [$value];
			}

			// "none" really means search for value ""
			if (($k = array_search('none', $value)) !== false)
			{
				unset($value[$k]);
				$value[] = '';
			}

			$totalValues = isset($column['allowedValues']) ? count($column['allowedValues']) : PHP_INT_MAX;

			if (count($value) < $totalValues)
			{
				$finder->where('security_lock', $value);
			}
			// otherwise all selected, so we don't need a condition

			return true;
		}

		if ($key == 'activity_summary')
		{
			if (!is_array($value))
			{
				$value = [$value];
			}

			$conditions = [];

			if (in_array('0', $value))
			{
				$conditions[] = ['last_summary_email_date', '=', null];
			}
			if (in_array('1', $value))
			{
				$conditions[] = ['last_summary_email_date', '>=', 0];
			}

			if (count($conditions) < 2)
			{
				$finder->whereOr($conditions);
			}
			// otherwise both selected, so we don't need a condition

			return true;
		}

		if ($key == 'connected_account')
		{
			if (!is_array($value))
			{
				$value = [$value];
			}

			foreach ($value AS $providerId)
			{
				$finder->with('ConnectedAccounts|' . $providerId, true);
			}

			return true;
		}

		return false;
	}

	public function getFormData()
	{
		return [
			'userGroups' => $this->em->getRepository(UserGroupRepository::class)->findUserGroupsForList()->fetch(),
			'connectedAccProviders' => $this->em->getRepository(ConnectedAccountRepository::class)->getConnectedAccountProviderTitlePairs(),
		];
	}

	/**
	 * @return array<string, array<mixed>>
	 */
	public function getFormDefaults()
	{
		return [
			'user_state' => [
				'valid', 'email_confirm', 'email_confirm_edit', 'email_bounce', 'moderated', 'rejected', 'disabled',
			],
			'security_lock' => [
				'none', 'change', 'reset',
			],
			'is_banned' => [0, 1],
			'is_staff' => [0, 1],
			'activity_summary' => [0, 1],
			'no_secondary_group_ids' => 0,
			'message_count' => ['end' => -1],
			'question_solution_count' => ['end' => -1],
			'trophy_points' => ['end' => -1],
			'Option' => [
				'is_discouraged' => [0, 1],
			],
		];
	}
}
