<?php

namespace XF\Notifier\Post;

use XF\App;
use XF\Entity\Post;
use XF\Entity\User;
use XF\Finder\PostFinder;
use XF\Notifier\AbstractNotifier;

use function in_array;

abstract class AbstractWatch extends AbstractNotifier
{
	/**
	 * @var Post
	 */
	protected $post;

	protected $actionType;
	protected $isApplicable;

	protected $userReadDates = [];
	protected $previousPosts = null;

	abstract protected function getDefaultWatchNotifyData();
	abstract protected function getApplicableActionTypes();
	abstract protected function getWatchEmailTemplateName();

	public function __construct(App $app, Post $post, $actionType)
	{
		parent::__construct($app);

		$this->post = $post;
		$this->actionType = $actionType;
		$this->isApplicable = $this->isApplicable();
	}

	protected function isApplicable()
	{
		if (!in_array($this->actionType, $this->getApplicableActionTypes()))
		{
			return false;
		}

		if (!$this->post->isVisible())
		{
			return false;
		}

		return true;
	}

	public function canNotify(User $user)
	{
		if (!$this->isApplicable)
		{
			return false;
		}

		if (!isset($this->userReadDates[$user->user_id]))
		{
			// this should have a record for every user, so generally shouldn't happen
			return false;
		}

		$userReadDate = $this->userReadDates[$user->user_id];
		$post = $this->post;

		if ($user->user_id == $post->user_id || $user->isIgnoring($post->user_id))
		{
			return false;
		}

		if ($userReadDate > $post->Thread->last_post_date)
		{
			return false;
		}

		if ($this->actionType == 'reply')
		{
			$previousVisiblePost = null;
			foreach ($this->getPreviousPosts() AS $previousPost)
			{
				if (!$user->isIgnoring($previousPost->user_id))
				{
					$previousVisiblePost = $previousPost;
					break;
				}
			}

			$autoReadDate = \XF::$time - $this->app()->options()->readMarkingDataLifetime * 86400;
			if (!$previousVisiblePost || $previousVisiblePost->post_date < $autoReadDate)
			{
				// always alert
			}
			else if ($previousVisiblePost->post_date > $userReadDate)
			{
				return false;
			}
		}

		return true;
	}

	public function sendAlert(User $user)
	{
		$post = $this->post;

		return $this->basicAlert(
			$user,
			$post->user_id,
			$post->username,
			'post',
			$post->post_id,
			'insert'
		);
	}

	public function sendEmail(User $user)
	{
		if (!$user->email || $user->user_state != 'valid')
		{
			return false;
		}

		$post = $this->post;

		$params = [
			'post' => $post,
			'thread' => $post->Thread,
			'forum' => $post->Thread->Forum,
			'receiver' => $user,
		];

		$template = $this->getWatchEmailTemplateName();

		$this->app()->mailer()->newMail()
			->setToUser($user)
			->setTemplate($template, $params)
			->queue();

		return true;
	}

	public function getDefaultNotifyData()
	{
		if (!$this->isApplicable)
		{
			return [];
		}

		return $this->getDefaultWatchNotifyData();
	}

	public function getUserData(array $userIds)
	{
		$users = parent::getUserData($userIds);
		$this->userReadDates = $this->getUserReadDates($userIds);

		return $users;
	}

	protected function getUserReadDates(array $userIds)
	{
		if (!$userIds)
		{
			return [];
		}

		$autoReadDate = \XF::$time - $this->app()->options()->readMarkingDataLifetime * 86400;
		$post = $this->post;

		$db = $this->app()->db();
		$readDates = $db->fetchPairs("
			SELECT user.user_id,
				GREATEST(
					COALESCE(thread_read.thread_read_date, 0),
					COALESCE(forum_read.forum_read_date, 0),
					?
				)
			FROM xf_user AS user
			LEFT JOIN xf_thread_read AS thread_read ON
				(thread_read.user_id = user.user_id AND thread_read.thread_id = ?)
			LEFT JOIN xf_forum_read AS forum_read ON
				(forum_read.user_id = user.user_id AND forum_read.node_id = ?)
			WHERE user.user_id IN (" . $db->quote($userIds) . ")
		", [$autoReadDate, $post->thread_id, $post->Thread->node_id]);

		foreach ($userIds AS $userId)
		{
			if (!isset($readDates[$userId]))
			{
				$readDates[$userId] = $autoReadDate;
			}
		}

		return $readDates;
	}

	protected function getPreviousPosts()
	{
		if ($this->previousPosts === null)
		{
			$autoReadDate = \XF::$time - $this->app()->options()->readMarkingDataLifetime * 86400;

			$finder = $this->app()->finder(PostFinder::class)
				->where('thread_id', $this->post->thread_id)
				->where('message_state', 'visible')
				->where('post_date', '<', $this->post->post_date)
				->where('post_date', '>=', $autoReadDate)
				->order('post_date', 'desc');

			$this->previousPosts = $finder->fetch(15);
		}

		return $this->previousPosts;
	}
}
