<?php

namespace XF\Repository;

use XF\Entity\User;
use XF\Finder\UserGroupFinder;
use XF\Mvc\Entity\Repository;

class UserGroupRepository extends Repository
{
	protected $displayStyleMapCache = [];

	/**
	 * @return UserGroupFinder
	 */
	public function findUserGroupsForList()
	{
		return $this->finder(UserGroupFinder::class)->order('title');
	}

	public function getUserGroupTitlePairs()
	{
		return $this->findUserGroupsForList()->fetch()->pluckNamed('title', 'user_group_id');
	}

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

		$userGroups = $this->getUserGroupTitlePairs();

		foreach ($userGroups AS $userGroupId => $label)
		{
			$choices[$userGroupId] = [
				'value' => $userGroupId,
				'label' => $label,
			];
			if ($type !== null)
			{
				$choices[$userGroupId]['_type'] = $type;
			}
		}

		return $choices;
	}

	public function getDisplayGroupIdForUser(User $user)
	{
		$groups = $user->secondary_group_ids;
		$groups[] = $user->user_group_id;

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

		$cacheId = implode(',', $groups);
		if (isset($this->displayStyleMapCache[$cacheId]))
		{
			return $this->displayStyleMapCache[$cacheId];
		}

		$result = $this->db()->fetchOne("
			SELECT user_group_id
			FROM xf_user_group
			WHERE user_group_id IN (" . $this->db()->quote($groups) . ")
			ORDER BY display_style_priority DESC
			LIMIT 1
		");
		if (!$result)
		{
			$result = end($groups);
		}

		$this->displayStyleMapCache[$cacheId] = $result;

		return $result;
	}

	public function rebuildDisplayPriority($userGroupId, $oldPriority, $newPriority)
	{
		if ($oldPriority == $newPriority)
		{
			return;
		}

		$userGroups = $this->finder(UserGroupFinder::class)->order('title')->fetch();

		$betweenGroupIds = [];

		$lowerBound = min($oldPriority, $newPriority);
		$upperBound = max($oldPriority, $newPriority);

		foreach ($userGroups AS $userGroup)
		{
			if ($userGroup->display_style_priority >= $lowerBound
				&& $userGroup->display_style_priority < $upperBound
				&& $userGroup->user_group_id != $userGroupId
			)
			{
				$betweenGroupIds[] = $userGroup->user_group_id;
			}
		}

		if (!$betweenGroupIds)
		{
			return;
		}

		$db = $this->db();

		if ($newPriority > $oldPriority)
		{
			// moving up: all who have this group as highest stay; switch to this if highest
			// was between old and new priorities and member of this group
			$updateUserIds = $db->fetchAllColumn('
				SELECT user.user_id
				FROM xf_user AS user
				INNER JOIN xf_user_group_relation AS user_group_relation ON
					(user_group_relation.user_id = user.user_id AND user_group_relation.user_group_id = ?)
				WHERE user.display_style_group_id IN (' . $db->quote($betweenGroupIds) . ')
			', $userGroupId);
			if ($updateUserIds)
			{
				$db->update(
					'xf_user',
					['display_style_group_id' => $userGroupId],
					'user_id IN (' . $db->quote($updateUserIds) . ')'
				);
			}
		}
		else
		{
			// moving down: need to recalculate for users that have this group as highest
			// but only need to for users that have more than one group
			$updateRes = $db->query('
				SELECT user.user_id, user_group.display_style_priority, user_group.user_group_id
				FROM xf_user AS user
				INNER JOIN xf_user_group_relation AS user_group_relation ON
					(user.user_id = user_group_relation.user_id
					AND user_group_relation.user_group_id <> user.display_style_group_id)
				INNER JOIN xf_user_group AS user_group ON
					(user_group.user_group_id = user_group_relation.user_group_id)
				WHERE user.display_style_group_id = ?
				GROUP BY user.user_id
			', $userGroupId);

			$updateGroups = [];
			$updatePriorities = [];
			while ($update = $updateRes->fetch())
			{
				// seed with current data if not set
				if (!isset($updateGroups[$update['user_id']]))
				{
					$updateGroups[$update['user_id']] = $userGroupId;
					$updatePriorities[$update['user_id']] = $newPriority;
				}

				// if higher, update
				if ($update['display_style_priority'] > $updatePriorities[$update['user_id']])
				{
					$updateGroups[$update['user_id']] = $update['user_group_id'];
					$updatePriorities[$update['user_id']] = $update['display_style_priority'];
				}
			}

			foreach ($updateGroups AS $userId => $displayGroupId)
			{
				// if ==, then no change from the current display group id
				if ($displayGroupId != $userGroupId)
				{
					$db->update(
						'xf_user',
						['display_style_group_id' => $displayGroupId],
						'user_id = ?',
						$userId
					);
				}
			}
		}
	}

	public function getDisplayStyleCacheData()
	{
		$data = [];
		foreach ($this->finder(UserGroupFinder::class)->fetch() AS $userGroup)
		{
			$data[$userGroup->user_group_id] = [
				'username_css' => $userGroup->username_css,
				'user_title' => $userGroup->user_title,
			];
		}

		return $data;
	}

	public function rebuildDisplayStyleCache()
	{
		$cache = $this->getDisplayStyleCacheData();
		\XF::registry()->set('displayStyles', $cache);

		$styleRepo = $this->repository(StyleRepository::class);
		$styleRepo->updateAllStylesLastModifiedDateLater();

		return $cache;
	}

	public function getUserBannerCacheData()
	{
		$cache = [];
		$userGroups = $this->finder(UserGroupFinder::class)
			->where('banner_text', '<>', '')
			->order('display_style_priority', 'desc')
			->fetch();

		foreach ($userGroups AS $userGroup)
		{
			$cache[$userGroup->user_group_id] = [
				'class' => $userGroup->banner_css_class,
				'text' => $userGroup->banner_text,
			];
		}

		return $cache;
	}

	public function rebuildUserBannerCache()
	{
		$cache = $this->getUserBannerCacheData();
		\XF::registry()->set('userBanners', $cache);

		return $cache;
	}
}
