<?php

namespace XF\Api\Controller;

use XF\Entity\User;
use XF\Mvc\ParameterBag;
use XF\Repository\UserAlertRepository;
use XF\Repository\UserRepository;

/**
 * @api-group Alerts
 */
class AlertsController extends AbstractController
{
	protected function preDispatchController($action, ParameterBag $params)
	{
		if (strtolower($action) == 'postmarkall')
		{
			$this->assertApiScope('alert:read');
		}
		else
		{
			$this->assertApiScopeByRequestMethod('alert');
		}

		if (strtolower($action) != 'post')
		{
			$this->assertRegisteredUser();
		}
	}

	/**
	 * @api-desc Gets the API user's list of alerts
	 *
	 * @api-in int $page
	 * @api-in int $cutoff Unix timestamp of oldest alert to include. Note that unread or unviewed alerts are always included.
	 * @api-in bool $unviewed If true, gets only unviewed alerts. Unviewed alerts have not been seen (in the standard UI).
	 * @api-in bool $unread If true, gets only unread alerts. Unread alerts may have been seen but the content they relate to has not been viewed.
	 *
	 * @api-out UserAlert[] $alerts
	 * @api-out pagination $pagination
	 */
	public function actionGet()
	{
		$page = $this->filterPage();
		$perPage = $this->options()->alertsPerPage;

		$alertsFinder = $this->setupAlertsFinder();
		$alerts = $alertsFinder->limitByPage($page, $perPage)->fetch();

		$totalAlerts = $alertsFinder->total();

		$this->assertValidApiPage($page, $perPage, $totalAlerts);

		$this->getAlertRepo()->addContentToAlerts($alerts);
		$alerts = $alerts->filterViewable();

		$alertResults = $alerts->toApiResults();

		$return = [
			'alerts' => $alertResults,
			'pagination' => $this->getPaginationData($alertResults, $page, $perPage, $totalAlerts),
		];
		return $this->apiResult($return);
	}

	/**
	 * @return UserAlertFinder
	 */
	protected function setupAlertsFinder()
	{
		$cutOff = $this->filter('cutoff', 'uint');

		$alertsFinder = $this->getAlertRepo()->findAlertsForUser(\XF::visitor()->user_id, $cutOff)
			->with('api');

		$unviewed = $this->filter('unviewed', 'bool');
		if ($unviewed)
		{
			$alertsFinder->where('view_date', 0);
		}

		$unread = $this->filter('unread', 'bool');
		if ($unread)
		{
			$alertsFinder->where('read_date', 0);
		}

		return $alertsFinder;
	}

	/**
	 * @api-desc Sends an alert to the specified user. Only available to super user keys.
	 *
	 * @api-in int $to_user_id <req> ID of the user to receive the alert
	 * @api-in str $alert <req> Text of the alert. May use the placeholder "{link}" to have the link automatically inserted.
	 * @api-in int $from_user_id If provided, the user to send the alert from. Otherwise, uses the current API user. May be 0 for an anonymous alert.
	 * @api-in str $link_url URL user will be taken to when the alert is clicked.
	 * @api-in str $link_title Text of the link URL that will be displayed. If no placeholder is present in the alert, will be automatically appended.
	 *
	 * @api-out true $success
	 */
	public function actionPost()
	{
		$this->assertSuperUserKey();
		$this->assertRequiredApiInput(['to_user_id', 'alert']);

		$input = $this->filter([
			'from_user_id' => '?uint',
			'to_user_id' => 'uint',
			'alert' => 'str',
			'link_url' => 'str',
			'link_title' => 'str',
		]);

		$toUser = $this->assertRecordExists(User::class, $input['to_user_id']);

		if ($input['from_user_id'] !== null)
		{
			if (!$input['from_user_id'])
			{
				$fromUser = $this->repository(UserRepository::class)->getGuestUser();
			}
			else
			{
				$fromUser = $this->assertRecordExists(User::class, $input['from_user_id']);
			}
		}
		else
		{
			$fromUser = \XF::visitor();
		}

		$alert = $this->setupAlertFromInput($input);

		$this->getAlertRepo()->alert(
			$toUser,
			$fromUser->user_id,
			$fromUser->username,
			'user',
			$toUser->user_id,
			'from_admin',
			$alert
		);

		return $this->apiSuccess();
	}

	protected function setupAlertFromInput(array $input): array
	{
		$alertText = $input['alert'];

		if ($input['link_url'])
		{
			if (strpos($alertText, '{link}') === false)
			{
				$alertText .= ' {link}';
			}

			$link = '<a href="' . $input['link_url'] . '" class="fauxBlockLink-blockLink">'
				. ($input['link_title'] ?: $input['link_url'])
				. '</a>';
			$alertText = str_replace('{link}', $link, $alertText);
		}

		return [
			'alert_text' => $alertText,
			'link_url' => $input['link_url'],
			'link_title' => $input['link_title'],
		];
	}

	/**
	 * @api-desc Marks all of the API user's alerts as read or viewed. Must specify "read" or "viewed" parameters.
	 *
	 * @api-in bool $read If specified, marks all alerts as read.
	 * @api-in bool $viewed If specified, marks all alerts as viewed. This will remove the alert counter but keep unactioned alerts highlighted.
	 *
	 * @api-out true $success
	 */
	public function actionPostMarkAll()
	{
		$visitor = \XF::visitor();

		if ($this->filter('viewed', 'bool'))
		{
			$this->getAlertRepo()->markUserAlertsViewed($visitor);
		}
		else if ($this->filter('read', 'bool'))
		{
			$this->getAlertRepo()->markUserAlertsRead($visitor);
		}
		else
		{
			$this->assertRequiredApiInput(['viewed', 'read']);
		}

		return $this->apiSuccess();
	}

	/**
	 * @return UserAlertRepository
	 */
	protected function getAlertRepo()
	{
		return $this->repository(UserAlertRepository::class);
	}
}
