<?php

namespace XF\Job;

use XF\Entity\Thread;
use XF\Service\FeaturedContent\CreatorService;
use XF\Service\FeaturedContent\DeleterService;

use function intval, is_array;

class ThreadAction extends AbstractJob
{
	protected $defaultData = [
		'start' => 0,
		'count' => 0,
		'total' => null,
		'criteria' => null,
		'threadIds' => null,
		'actions' => [],
	];

	public function run($maxRunTime)
	{
		if (is_array($this->data['criteria']) && is_array($this->data['threadIds']))
		{
			throw new \LogicException("Cannot have both criteria and threadIds values; one must be null");
		}

		$startTime = microtime(true);
		$em = $this->app->em();

		$ids = $this->prepareThreadIds();
		if (!$ids)
		{
			return $this->complete();
		}

		$db = $this->app->db();
		$db->beginTransaction();

		$limitTime = ($maxRunTime > 0);
		foreach ($ids AS $key => $id)
		{
			$this->data['count']++;
			$this->data['start'] = $id;
			unset($ids[$key]);

			/** @var Thread $thread */
			$thread = $em->find(Thread::class, $id);
			if ($thread)
			{
				if ($this->getActionValue('delete'))
				{
					$thread->delete(false, false);
					continue; // no further action required
				}

				$this->applyInternalThreadChange($thread);
				$thread->save(false, false);

				$this->applyExternalThreadChange($thread);
			}

			if ($limitTime && microtime(true) - $startTime > $maxRunTime)
			{
				break;
			}
		}

		if (is_array($this->data['threadIds']))
		{
			$this->data['threadIds'] = $ids;
		}

		$db->commit();

		return $this->resume();
	}

	protected function getActionValue($action)
	{
		$value = null;
		if (!empty($this->data['actions'][$action]))
		{
			$value = $this->data['actions'][$action];
		}
		return $value;
	}

	protected function prepareThreadIds()
	{
		if (is_array($this->data['criteria']))
		{
			$searcher = $this->app->searcher(\XF\Searcher\Thread::class, $this->data['criteria']);
			$results = $searcher->getFinder()
				->where('thread_id', '>', $this->data['start'])
				->order('thread_id')
				->limit(1000)
				->fetchColumns('thread_id');
			$ids = array_column($results, 'thread_id');
		}
		else if (is_array($this->data['threadIds']))
		{
			$ids = $this->data['threadIds'];
		}
		else
		{
			$ids = [];
		}
		sort($ids, SORT_NUMERIC);
		return $ids;
	}

	protected function applyInternalThreadChange(Thread $thread)
	{
		$isRedirect = ($thread->discussion_type == 'redirect');

		if ($nodeId = $this->getActionValue('node_id'))
		{
			$thread->node_id = $nodeId;
		}

		$newThreadType = $this->getActionValue('discussion_type');
		if ($newThreadType && !$isRedirect)
		{
			$forum = $thread->Forum;
			if (
				$forum
				&& $forum->TypeHandler->isThreadTypeAllowed($newThreadType, $forum)
				&& $thread->TypeHandler->canThreadTypeBeChanged($thread)
			)
			{
				$thread->discussion_type = $newThreadType;
			}
		}

		if ($this->getActionValue('apply_thread_prefix') && !$isRedirect)
		{
			$thread->prefix_id = intval($this->getActionValue('prefix_id'));
		}

		if ($this->getActionValue('stick'))
		{
			$thread->sticky = true;
		}
		if ($this->getActionValue('unstick'))
		{
			$thread->sticky = false;
		}

		if ($this->getActionValue('lock') && !$isRedirect)
		{
			$thread->discussion_open = false;
		}
		if ($this->getActionValue('unlock') && !$isRedirect)
		{
			$thread->discussion_open = true;
		}

		if ($this->getActionValue('approve') && !$isRedirect)
		{
			$thread->discussion_state = 'visible';
		}
		if ($this->getActionValue('unapprove') && !$isRedirect)
		{
			$thread->discussion_state = 'moderated';
		}

		if ($this->getActionValue('feature') && !$isRedirect && !$thread->isFeatured())
		{
			$creator = $this->app->service(
				CreatorService::class,
				$thread
			);
			$creator->save();
		}
		else if ($this->getActionValue('unfeature') && !$isRedirect && $thread->isFeatured())
		{
			$deleter = $this->app->service(
				DeleterService::class,
				$thread->Feature
			);
			$deleter->delete();
		}

		if ($this->getActionValue('soft_delete'))
		{
			$thread->discussion_state = 'deleted';
		}
	}

	protected function applyExternalThreadChange(Thread $thread)
	{
	}

	public function getStatusMessage()
	{
		$actionPhrase = \XF::phrase('updating');
		$typePhrase = \XF::phrase('threads');

		if ($this->data['total'] !== null)
		{
			return sprintf('%s... %s (%d/%d)', $actionPhrase, $typePhrase, $this->data['count'], $this->data['total']);
		}
		else
		{
			return sprintf('%s... %s (%d)', $actionPhrase, $typePhrase, $this->data['start']);
		}
	}

	public function canCancel()
	{
		return true;
	}

	public function canTriggerByChoice()
	{
		return false;
	}
}
