<?php

namespace XF\Repository;

use XF\Db\DeadlockException;
use XF\Entity\UserRemember;
use XF\Finder\UserRememberFinder;
use XF\Mvc\Entity\Repository;

use function intval, is_string;

class UserRememberRepository extends Repository
{
	public function createRememberRecord($userId, &$remember = null)
	{
		/** @var UserRemember $remember */
		$remember = $this->em->create(UserRemember::class);
		$key = $remember->generateForUserId($userId);
		$remember->extendExpiryDate();
		$remember->save();

		return $key;
	}

	public function getCookieValue($userId, $cookieKey)
	{
		return $userId . ',' . $cookieKey;
	}

	/**
	 * @param string $cookie
	 * @param null|UserRemember $matched If matched, returns remember record
	 * @return bool
	 */
	public function validateByCookieValue($cookie, &$matched = null)
	{
		$matched = null;

		if (!$cookie || !is_string($cookie) || !strpos($cookie, ','))
		{
			return false;
		}

		[$userId, $key] = explode(',', $cookie, 2);

		$userId = intval($userId);
		if (!$userId)
		{
			return false;
		}

		return $this->validateRememberRecord($userId, $key, $matched);
	}

	public function validateRememberRecord($userId, $key, &$matched = null)
	{
		$matched = null;

		$records = $this->finder(UserRememberFinder::class)->where('user_id', $userId)->fetch();
		foreach ($records AS $record)
		{
			/** @var $record \XF\Entity\UserRemember */
			if ($record->isKeyCorrect($key))
			{
				if ($record->isValid())
				{
					$matched = $record;
					return true;
				}
				else
				{
					return false;
				}
			}
		}

		return false;
	}

	public function clearUserRememberRecords($userId)
	{
		$this->db()->delete('xf_user_remember', 'user_id = ?', $userId);
	}

	public function pruneExpiredRememberRecords()
	{
		$this->db()->delete('xf_user_remember', 'expiry_date < ?', \XF::$time);
	}

	/**
	 * Only allows a user to have so many remember keys at a given time, deleting soonest expiring ones first.
	 *
	 * @param int $userId
	 *
	 * @return int Number of rows deleted
	 */
	public function applyUserRememberRecordLimit($userId)
	{
		$expiryDate = $this->db()->fetchOne("
			SELECT expiry_date
			FROM xf_user_remember
			WHERE user_id = ?
			ORDER BY expiry_date DESC
			LIMIT 1 OFFSET 500
		", $userId);
		if ($expiryDate)
		{
			try
			{
				return $this->db()->delete(
					'xf_user_remember',
					'user_id = ? AND expiry_date <= ?',
					[$userId, $expiryDate]
				);
			}
			catch (DeadlockException $e)
			{
				// ignore deadlocks - we'll catch this next time
				return 0;
			}
		}

		return 0;
	}
}
