<?php

namespace XF\ForumType;

use XF\Api\Result\EntityResult;
use XF\Entity\Forum;
use XF\Entity\Node;
use XF\Finder\ThreadFinder;
use XF\Http\Request;
use XF\InputFiltererArray;
use XF\Mvc\Entity\Entity;
use XF\Mvc\FormAction;
use XF\Mvc\Reply\AbstractReply;
use XF\Mvc\Reply\View;

use function intval, is_array;

class SuggestionHandler extends AbstractHandler
{
	public function getDefaultThreadType(Forum $forum): string
	{
		return 'suggestion';
	}

	public function getDisplayOrder(): int
	{
		return 30;
	}

	public function getTypeIconClass(): string
	{
		return 'fa-lightbulb-on';
	}

	public function getTypeActionPhrase(string $context)
	{
		return \XF::phrase('forum_type_action.suggestion');
	}

	public function getDefaultTypeConfig(): array
	{
		return [
			'allow_downvote' => true,
			'implemented_prefix_ids' => [], // key and value both the prefix ID
			'closed_prefix_ids' => [], // key and value both the prefix ID
		];
	}

	protected function getTypeConfigColumnDefinitions(): array
	{
		$prefixIdsVerifier = function (&$ids)
		{
			$ids = is_array($ids) ? $this->getPrefixIdsForTypeConfig($ids) : [];
			return true;
		};

		return [
			'allow_downvote' => ['type' => Entity::BOOL],
			'implemented_prefix_ids' => [
				'type' => Entity::LIST_ARRAY,
				'verify' => $prefixIdsVerifier,
			],
			'closed_prefix_ids' => [
				'type' => Entity::LIST_ARRAY,
				'verify' => $prefixIdsVerifier,
			],
		];
	}

	public function getForumViewAndTemplate(Forum $forum): array
	{
		return ['XF:Forum\ViewTypeSuggestion', 'forum_view_type_suggestion'];
	}

	public function adjustForumViewParams(Forum $forum, array $viewParams, Request $request): array
	{
		$effectiveOrder = $viewParams['sortInfo']['order'];
		$filters = $viewParams['filters'];

		$suggestionTabs = [
			'latest' => [
				'selected' => ($effectiveOrder == 'last_post_date'),
				'filters' => array_replace($filters, [
					'order' => 'last_post_date',
					'direction' => 'desc',
					'your_suggestions' => null,
					'your_votes' => null,
					'suggestion_state' => null,
				]),
				'priority' => 1,
			],
			'popular' => [
				'selected' => ($effectiveOrder == 'vote_score'),
				'filters' => array_replace($filters, [
					'order' => 'vote_score',
					'direction' => 'desc',
					'your_suggestions' => null,
					'your_votes' => null,
					'suggestion_state' => null,
				]),
				'priority' => 1,
			],
			'newest' => [
				'selected' => ($effectiveOrder == 'post_date'),
				'filters' => array_replace($filters, [
					'order' => 'post_date',
					'direction' => 'desc',
					'your_suggestions' => null,
					'your_votes' => null,
					'suggestion_state' => null,
				]),
				'priority' => 1,
			],
			'implemented' => [
				'selected' => !empty($filters['suggestion_state']) && $filters['suggestion_state'] == 'implemented',
				'filters' => array_replace($filters, [
					'order' => null,
					'direction' => null,
					'your_suggestions' => null,
					'your_votes' => null,
					'suggestion_state' => 'implemented',
				]),
				'priority' => 5,
			],
			'yourSuggestions' => [
				'selected' => !empty($filters['your_suggestions']),
				'filters' => array_replace($filters, [
					'order' => null,
					'direction' => null,
					'starter_id' => null,
					'your_suggestions' => 1,
					'your_votes' => null,
					'suggestion_state' => null,
				]),
				'priority' => 10,
			],
			'yourVotes' => [
				'selected' => !empty($filters['your_votes']),
				'filters' => array_replace($filters, [
					'order' => null,
					'direction' => null,
					'your_suggestions' => null,
					'your_votes' => 1,
					'suggestion_state' => null,
				]),
				'priority' => 10,
			],
		];

		if (!$forum->type_config['implemented_prefix_ids'])
		{
			unset($suggestionTabs['implemented']);
		}

		if (!\XF::visitor()->user_id)
		{
			unset($suggestionTabs['yourSuggestions'], $suggestionTabs['yourVotes']);
		}

		$selectedId = null;
		$selectedPriority = 0;

		foreach ($suggestionTabs AS $tabId => &$tab)
		{
			if (
				isset($tab['filters']['order'])
				&& $tab['filters']['order'] == $forum->default_sort_order)
			{
				$tab['filters']['order'] = null;
			}

			if (
				isset($tab['filters']['direction'])
				&& $tab['filters']['direction'] == $forum->default_sort_direction)
			{
				$tab['filters']['direction'] = null;
			}

			// get the highest priority selected tab
			if ($tab['selected'] && $tab['priority'] >= $selectedPriority)
			{
				$selectedId = $tabId;
				$selectedPriority = $tab['priority'];
			}
		}

		// second loop to ensure only one tab is selected
		if ($selectedId)
		{
			foreach ($suggestionTabs AS $tabId => &$tab)
			{
				if ($tab['selected'] && $tabId !== $selectedId)
				{
					$tab['selected'] = false;
				}
			}
		}

		unset($tab);

		$viewParams['suggestionTabs'] = $suggestionTabs;

		return $viewParams;
	}

	public function getForumViewTemplateOverrides(Forum $forum, array $extra = []): array
	{
		return [
			'thread_list_macro' => 'thread_list_suggestion_macros::item',
			'quick_thread_macro' => 'thread_list_suggestion_macros::quick_thread',
		];
	}

	public function getForumFilterInput(Forum $forum, Request $request, array $filters): array
	{
		$userId = \XF::visitor()->user_id;

		$yourSuggestions = $request->filter('your_suggestions', 'bool');
		if ($yourSuggestions && $userId)
		{
			$filters['your_suggestions'] = 1;
			unset($filters['starter_id']);
		}
		else if ($userId && !empty($filters['starter_id']) && $filters['starter_id'] == $userId)
		{
			$filters['your_suggestions'] = 1;
			unset($filters['starter_id']);
		}

		$yourVotes = $request->filter('your_votes', 'bool');
		if ($yourVotes && $userId)
		{
			$filters['your_votes'] = 1;
		}

		$suggestionState = $request->filter('suggestion_state', 'string');
		if ($suggestionState == 'implemented' && $forum->type_config['implemented_prefix_ids'])
		{
			$filters['suggestion_state'] = 'implemented';
		}
		else if ($suggestionState == 'closed' && $forum->type_config['closed_prefix_ids'])
		{
			$filters['suggestion_state'] = 'closed';
		}
		else if (
			$suggestionState == 'open'
			&& ($forum->type_config['closed_prefix_ids'] || $forum->type_config['implemented_prefix_ids'])
		)
		{
			$filters['suggestion_state'] = 'open';
		}

		return $filters;
	}

	public function applyForumFilters(Forum $forum, ThreadFinder $threadFinder, array $filters): ThreadFinder
	{
		$userId = \XF::visitor()->user_id;

		if (!empty($filters['your_suggestions']))
		{
			$threadFinder->where('user_id', $userId);
		}

		if (!empty($filters['your_votes']))
		{
			$threadFinder->exists('ContentVotes|' . $userId);
		}

		if (!empty($filters['suggestion_state']))
		{
			$typeConfig = $forum->type_config;

			if ($filters['suggestion_state'] == 'implemented')
			{
				$threadFinder->where('prefix_id', array_values($typeConfig['implemented_prefix_ids']));
			}
			else if ($filters['suggestion_state'] == 'closed')
			{
				$threadFinder->where('prefix_id', array_values($typeConfig['closed_prefix_ids']));
			}
			else if ($filters['suggestion_state'] == 'open')
			{
				$specialPrefixIds = $typeConfig['implemented_prefix_ids'] + $typeConfig['closed_prefix_ids'];
				$threadFinder->where('prefix_id', '<>', array_values($specialPrefixIds));
			}
		}

		return $threadFinder;
	}

	public function adjustForumFiltersPopup(Forum $forum, View $filtersView): AbstractReply
	{
		$filtersView->setTemplateName('forum_filters_type_suggestion');

		$filters = $filtersView->getParam('filters');
		$visitor = \XF::visitor();

		if (!empty($filters['your_suggestions']) && $visitor->user_id)
		{
			$filtersView->setParam('starterFilter', $visitor);
		}

		return $filtersView;
	}

	public function getThreadListSortOptions(Forum $forum, bool $forAdminConfig = false): array
	{
		$options = parent::getThreadListSortOptions($forum, $forAdminConfig);
		$options['vote_score'] = 'vote_score';

		return $options;
	}

	public function adjustForumThreadListFinder(
		Forum $forum,
		ThreadFinder $threadFinder,
		int $page,
		Request $request
	)
	{
		$visitor = \XF::visitor();
		if ($visitor->user_id)
		{
			$threadFinder->with('ContentVotes|' . $visitor->user_id);
		}
	}

	public function setupTypeConfigEdit(
		View $reply,
		Node $node,
		Forum $forum,
		array &$typeConfig
	)
	{
		return 'forum_type_config_suggestion';
	}

	public function setupTypeConfigSave(FormAction $form, Node $node, Forum $forum, Request $request)
	{
		$validator = $this->getTypeConfigValidator($forum);

		$validator->bulkSet([
			'allow_downvote' => $request->filter('type_config.allow_downvote', 'bool'),
			'implemented_prefix_ids' => $request->filter('type_config.implemented_prefix_ids', 'array-uint'),
			'closed_prefix_ids' => $request->filter('type_config.closed_prefix_ids', 'array-uint'),
		]);

		return $validator;
	}

	public function setupTypeConfigApiSave(
		FormAction $form,
		Node $node,
		Forum $forum,
		InputFiltererArray $typeInputFilterer
	)
	{
		$validator = $this->getTypeConfigValidator($forum);

		$allowDownote = $typeInputFilterer->filter('type_config.allow_downvote', '?bool');
		if ($allowDownote !== null)
		{
			$validator->allow_downvote = $allowDownote;
		}

		$implementedIds = $typeInputFilterer->filter('type_config.implemented_prefix_ids', '?array-uint');
		if (is_array($implementedIds))
		{
			$validator->implemented_prefix_ids = $implementedIds;
		}

		$closedIds = $typeInputFilterer->filter('type_config.closed_prefix_ids', '?array-uint');
		if (is_array($closedIds))
		{
			$validator->closed_prefix_ids = $closedIds;
		}

		return $validator;
	}

	protected function getPrefixIdsForTypeConfig(array $ids)
	{
		foreach ($ids AS $k => &$v)
		{
			$v = intval($v);
			if ($v <= 0)
			{
				unset($ids[$k]);
			}
		}

		$ids = array_unique($ids);
		sort($ids, SORT_NUMERIC);

		return array_combine($ids, $ids);
	}

	public function addTypeConfigToApiResult(
		Forum $forum,
		EntityResult $result,
		int $verbosity = Entity::VERBOSITY_NORMAL,
		array $options = []
	)
	{
		$result->suggestion = [
			'allow_downvote' => $forum->type_config['allow_downvote'],
		];
	}
}
