<?php

namespace XF\Repository;

use XF\Db\AbstractAdapter;
use XF\Entity\AbstractNode;
use XF\Entity\Forum;
use XF\Entity\Node;
use XF\Finder\ForumFinder;
use XF\Mvc\Entity\Repository;

use function in_array, is_array;

class ForumRepository extends Repository
{
	public function getViewableForums(array $where = [], $with = null)
	{
		$visitor = \XF::visitor();

		$forumFinder = $this->finder(ForumFinder::class)
			->with('Node.Permissions|' . $visitor->permission_combination_id);

		if ($where)
		{
			$forumFinder->where($where);
		}
		if ($with)
		{
			$forumFinder->with($with);
		}

		return $forumFinder->fetch()->filterViewable();
	}

	public function markForumReadByUser(Forum $forum, $userId, $newRead = null)
	{
		if (!$userId)
		{
			return false;
		}

		if ($newRead === null)
		{
			$newRead = max(\XF::$time, $forum->last_post_date);
		}

		$cutOff = \XF::$time - $this->options()->readMarkingDataLifetime * 86400;
		if ($newRead <= $cutOff)
		{
			return false;
		}

		$read = $forum->Read[$userId];
		if ($read && $newRead <= $read->forum_read_date)
		{
			return false;
		}

		\XF::db()->executeTransaction(function (AbstractAdapter $db) use ($forum, $userId, $newRead)
		{
			$db->insert('xf_forum_read', [
				'node_id' => $forum->node_id,
				'user_id' => $userId,
				'forum_read_date' => $newRead,
			], false, 'forum_read_date = VALUES(forum_read_date)');
		}, AbstractAdapter::ALLOW_DEADLOCK_RERUN);

		return true;
	}

	public function markForumReadByVisitor(Forum $forum, $newRead = null)
	{
		$userId = \XF::visitor()->user_id;
		return $this->markForumReadByUser($forum, $userId, $newRead);
	}

	public function markForumReadIfPossible(Forum $forum, $newRead = null)
	{
		$userId = \XF::visitor()->user_id;
		if (!$userId)
		{
			return false;
		}

		$threadRepo = $this->repository(ThreadRepository::class);
		$unread = $threadRepo->countUnreadThreadsInForum($forum);
		if ($unread)
		{
			return false;
		}

		return $this->markForumReadByVisitor($forum, $newRead);
	}

	public function markForumTreeReadByVisitor(?AbstractNode $baseNode = null, $newRead = null)
	{
		$userId = \XF::visitor()->user_id;
		if (!$userId)
		{
			return [];
		}

		if ($newRead === null)
		{
			$newRead = \XF::$time;
		}

		$cutOff = \XF::$time - $this->options()->readMarkingDataLifetime * 86400;
		if ($newRead <= $cutOff)
		{
			return [];
		}

		/** @var NodeRepository $nodeRepo */
		$nodeRepo = $this->repository(NodeRepository::class);

		if ($baseNode)
		{
			$childNodes = $nodeRepo->findDescendants($baseNode->Node, false)->fetch();
			$nodes = $childNodes->unshift($baseNode->Node);
		}
		else
		{
			$nodes = $nodeRepo->getFullNodeList();
		}

		$forumIds = [];
		foreach ($nodes AS $nodeId => $node)
		{
			/** @var Node $node */
			if ($node->node_type_id == 'Forum')
			{
				$forumIds[] = $node->node_id;
			}
		}

		$forums = $this->em->findByIds(
			ForumFinder::class,
			$forumIds,
			[
				'Node.Permissions|' . \XF::visitor()->permission_combination_id,
				'Read|' . $userId,
			]
		);

		$markedRead = [];
		$inserts = [];

		foreach ($forums AS $forum)
		{
			/** @var Forum $forum */
			if (!$forum->canView())
			{
				continue;
			}

			$read = $forum->Read[$userId];
			if ($read && $newRead <= $read->forum_read_date)
			{
				continue;
			}

			$inserts[] = [
				'node_id' => $forum->node_id,
				'user_id' => $userId,
				'forum_read_date' => $newRead,
			];
			$markedRead[] = $forum->node_id;
		}

		if ($inserts)
		{
			\XF::db()->executeTransaction(function (AbstractAdapter $db) use ($inserts)
			{
				$this->db()->insertBulk(
					'xf_forum_read',
					$inserts,
					false,
					'forum_read_date = VALUES(forum_read_date)'
				);
			}, AbstractAdapter::ALLOW_DEADLOCK_RERUN);
		}

		return $markedRead;
	}

	public function pruneForumReadLogs($cutOff = null)
	{
		if ($cutOff === null)
		{
			$cutOff = \XF::$time - ($this->options()->readMarkingDataLifetime * 86400);
		}

		$this->db()->delete('xf_forum_read', 'forum_read_date < ?', $cutOff);
	}

	public function getForumCounterTotals()
	{
		return $this->db()->fetchRow("
			SELECT SUM(discussion_count) AS threads,
				SUM(message_count) AS messages
			FROM xf_forum
		");
	}

	/**
	 * @param bool $includeEmpty
	 * @param string[]|string|null $enableTypes
	 * @param string|null $type
	 * @param bool $checkPerms
	 *
	 * @return array
	 */
	public function getForumOptionsData(
		bool $includeEmpty = true,
		$enableTypes = null,
		?string $type = null,
		bool $checkPerms = false
	)
	{
		$choices = [];

		if ($includeEmpty)
		{
			$choices[0] = [
				'_type' => 'option',
				'value' => 0,
				'label' => \XF::phrase('(none)'),
			];
		}

		/** @var NodeRepository $nodeRepo */
		$nodeRepo = $this->repository(NodeRepository::class);
		$nodeList = $nodeRepo->getFullNodeList();
		$nodeRepo->loadNodeTypeDataForNodes($nodeList);

		if ($checkPerms)
		{
			$nodeList = $nodeList->filterViewable();
		}

		foreach ($nodeRepo->createNodeTree($nodeList)->getFlattened() AS $entry)
		{
			/** @var Node $node */
			$node = $entry['record'];

			if ($node['depth'])
			{
				$prefix = str_repeat('--', $entry['depth']) . ' ';
			}
			else
			{
				$prefix = '';
			}

			$choice = [
				'value' => $node->node_id,
				'label' => $prefix . $node->title,
			];

			if ($node->node_type_id == 'Forum' && $node->Data)
			{
				if ($enableTypes !== null)
				{
					if (!is_array($enableTypes))
					{
						$enableTypes = [$enableTypes];
					}

					/** @var Forum $forum */
					$forum = $node->Data;
					$choice['disabled'] = in_array($forum->forum_type_id, $enableTypes)
						? false
						: 'disabled';
				}
				else
				{
					$choice['disabled'] = false;
				}
			}
			else
			{
				$choice['disabled'] = 'disabled';
			}

			if ($type !== null)
			{
				$choice['_type'] = $type;
			}

			$choices[$node->node_id] = $choice;
		}

		return $choices;
	}
}
