<?php

namespace XF\Entity;

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

use function intval, is_array;

/**
 * @property ReactionContent[] Reactions
 */
trait ReactionTrait
{
	/**
	 * @param string|\Stringable|null $error
	 */
	abstract public function canReact(&$error = null);

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

		/** @var ReactionContent $reactionContent */
		$reactionContent = $this->Reactions[$visitor->user_id] ?? null;

		return ($reactionContent && $reactionContent->isReactionActive());
	}

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

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

		return $this->Reactions[$visitor->user_id]->reaction_id;
	}

	/**
	 * @return null|ReactionContent
	 */
	public function getReactionContent()
	{
		if (!$this->isReactedTo())
		{
			return null;
		}

		return $this->Reactions[\XF::visitor()->user_id];
	}

	public function getReactions()
	{
		$sourceValue = $this->getValueSourceEncoded('reactions');

		if (!$sourceValue)
		{
			return [];
		}

		if (!is_array(@json_decode($sourceValue, true)))
		{
			// legacy data, just a number, convert to an array assuming the number is the count of likes (reaction ID 1)
			return [
				'1' => intval($sourceValue),
			];
		}

		$reactions = $this->reactions_;
		arsort($reactions, SORT_NUMERIC);

		$reactionsCache = $this->app()->container('reactions');

		foreach ($reactions AS $reactionId => $reaction)
		{
			if (!isset($reactionsCache[$reactionId]) || !$reactionsCache[$reactionId]['active'])
			{
				unset($reactions[$reactionId]);
				continue;
			}
		}

		return $reactions;
	}

	public function getReactionUsers()
	{
		$reactionUsers = $this->reaction_users_;
		if ($reactionUsers === null)
		{
			return [];
		}

		$reactionsCache = $this->app()->container('reactions');

		foreach ($reactionUsers AS $key => $user)
		{
			if (!isset($user['reaction_id']))
			{
				// likely legacy data with no reaction_id so this relates to a default like so just carry on.
				continue;
			}

			if (!isset($reactionsCache[$user['reaction_id']]) || !$reactionsCache[$user['reaction_id']]['active'])
			{
				unset($reactionUsers[$key]);
				continue;
			}
		}

		return array_values($reactionUsers); // to ensure array is re-indexed from 0
	}

	/**
	 * @param EntityResult $result
	 *
	 * @api-out bool $is_reacted_to True if the viewing user has reacted to this content
	 * @api-out int $visitor_reaction_id If the viewer reacted, the ID of the reaction they used
	 */
	protected function addReactionStateToApiResult(EntityResult $result)
	{
		$visitor = \XF::visitor();

		if ($visitor->user_id)
		{
			$isReactedTo = $this->isReactedTo();
			$result->is_reacted_to = $isReactedTo;
			if ($isReactedTo)
			{
				$result->visitor_reaction_id = $this->getVisitorReactionId();
			}
		}
	}

	public static function addReactableStructureElements(Structure $structure)
	{
		$structure->columns['reaction_score'] = ['type' => self::INT, 'default' => 0, 'api' => true];
		$structure->columns['reactions'] = ['type' => self::JSON_ARRAY, 'default' => []];
		$structure->columns['reaction_users'] = ['type' => self::JSON_ARRAY, 'default' => []];

		$structure->relations['Reactions'] = [
			'entity' => 'XF:ReactionContent',
			'type' => self::TO_MANY,
			'conditions' => [
				['content_type', '=', $structure->contentType],
				['content_id', '=', '$' . $structure->primaryKey],
			],
			'key' => 'reaction_user_id',
			'order' => 'reaction_date',
		];

		$structure->getters['reactions'] = true;
		$structure->getters['reaction_users'] = true;

		$structure->columnAliases['likes'] = 'reaction_score';
		$structure->columnAliases['like_users'] = 'reaction_users';

		if (isset($structure->withAliases['api']))
		{
			$structure->withAliases['api'][] = function ()
			{
				$visitorId = \XF::visitor()->user_id;
				if ($visitorId)
				{
					return 'Reactions|' . $visitorId;
				}
			};
		}
	}
}
