<?php

namespace XF\Entity;

use XF\Api\Result\EntityResult;
use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;
use XF\Repository\ContentVoteRepository;

/**
 * COLUMNS
 * @property int vote_score
 * @property int vote_count
 *
 * GETTERS
 * @property mixed vote_score_short
 *
 * RELATIONS
 * @property ContentVote[] ContentVotes
 */
trait ContentVoteTrait
{
	abstract public function isContentVotingSupported(): bool;

	abstract public function isContentDownvoteSupported(): bool;

	abstract protected function canVoteOnContentInternal(&$error = null): bool;

	public function canVoteOnContent(&$error = null): bool
	{
		if (!isset($this->user_id))
		{
			throw new \LogicException("No user_id column specified on entity, reimplement canVoteOnContent");
		}

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

		$visitor = \XF::visitor();
		if (!$visitor->user_id)
		{
			return false;
		}

		if ($visitor->user_id == $this->user_id)
		{
			return false;
		}

		if (!$this->canVoteOnContentInternal($error))
		{
			return false;
		}

		return true;
	}

	public function canDownvoteContent(&$error = null): bool
	{
		return true;
	}

	public function getVisitorContentVote()
	{
		$visitor = \XF::visitor();
		if (!$visitor->user_id)
		{
			return false;
		}

		if (!isset($this->ContentVotes[$visitor->user_id]))
		{
			return false;
		}

		$vote = $this->ContentVotes[$visitor->user_id];

		if ($vote->score > 0)
		{
			return ContentVote::VOTE_UP;
		}
		else if ($vote->score < 0)
		{
			return ContentVote::VOTE_DOWN;
		}
		else
		{
			return false;
		}
	}

	public function getVoteScoreShort()
	{
		return \XF::language()->shortNumberFormat($this->vote_score, 1);
	}

	/**
	 * @param EntityResult $result
	 *
	 * @api-out int $vote_score <cond> The content's vote score (if supported)
	 * @api-out bool $can_content_vote <cond> True if the viewing user can vote on this content
	 * @api-out str[] $allowed_content_vote_types <cond> List of content vote types allowed on this content
	 * @api-out bool $is_content_voted <cond> True if the viewing user has voted on this content
	 * @api-out str $visitor_content_vote <cond> If the viewer reacted, the vote they case (up/down)
	 */
	protected function addContentVoteToApiResult(EntityResult $result)
	{
		$visitor = \XF::visitor();

		if ($this->isContentVotingSupported())
		{
			$result->vote_score = $this->vote_score;

			$voteTypes = [ContentVote::VOTE_UP];
			if (
				$this->isContentDownvoteSupported()
				&& $this->canDownvoteContent()
			)
			{
				$voteTypes[] = ContentVote::VOTE_DOWN;
			}
			$result->allowed_content_vote_types = $voteTypes;

			if ($result->getResultType() === EntityResult::TYPE_API)
			{
				$result->can_content_vote = $this->canVoteOnContent();

				if ($visitor->user_id)
				{
					$visitorVote = $this->getVisitorContentVote();
					$result->is_content_voted = $visitorVote ? true : false;
					if ($visitorVote)
					{
						$result->visitor_content_vote = $visitorVote;
					}
				}
			}
		}
	}

	public static function addVotableStructureElements(Structure $structure)
	{
		$structure->columns['vote_score'] = ['type' => Entity::INT, 'default' => 0];
		$structure->columns['vote_count'] = ['type' => Entity::UINT, 'forced' => true, 'default' => 0];

		$structure->getters['vote_score_short'] = false;

		$structure->relations['ContentVotes'] = [
			'entity' => 'XF:ContentVote',
			'type' => self::TO_MANY,
			'conditions' => [
				['content_type', '=', $structure->contentType],
				['content_id', '=', '$' . $structure->primaryKey],
			],
			'key' => 'vote_user_id',
			'order' => 'vote_date',
		];
	}

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