<?php

namespace XF\Spam;

use XF\App;
use XF\Entity\User;
use XF\Finder\ReportFinder;
use XF\Repository\BanningRepository;
use XF\Repository\SpamRepository;
use XF\Service\User\AvatarService;
use XF\Service\User\ProfileBannerService;

use function boolval, count, strval;

class Cleaner
{
	/**
	 * @var App
	 */
	protected $app;

	protected $db;

	/** @var User */
	protected $user;

	protected $log = [];
	protected $errors = [];

	protected $inTransaction = false;

	public function __construct(App $app, User $user)
	{
		$this->app = $app;

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

		$this->setUser($user);
	}

	public function setUser(User $user)
	{
		$this->user = $user;
	}

	public function isAlreadyCleaned()
	{
		$db = $this->db;

		return boolval($db->fetchOne('
			SELECT COUNT(*)
			FROM xf_spam_cleaner_log
			WHERE user_id = ?
			AND restored_date = 0
		', $this->user->user_id));
	}

	public function isRecentlyCleaned(?int $recentSeconds = null): bool
	{
		$db = $this->db;

		// done this way to give us future flexibility without requiring a method signature change
		if ($recentSeconds === null)
		{
			$recentSeconds = 120;
		}

		return boolval($db->fetchOne('
			SELECT COUNT(*)
			FROM xf_spam_cleaner_log
			WHERE user_id = ?
			AND restored_date = 0
			AND application_date >= ?
		', [$this->user->user_id, time() - $recentSeconds]));
	}

	public function banUser()
	{
		$this->forceTransaction();

		$this->log('user', 'banned');

		$user = $this->user;

		$reason = strval(\XF::phrase('spam_cleaner_ban_reason'));

		$banningRepo = $this->app->repository(BanningRepository::class);

		$success = $banningRepo->banUser($user, 0, $reason, $error);
		if (!$success)
		{
			$this->logError('ban', $error);
			return;
		}

		if ($user->avatar_date > 0 || $user->gravatar)
		{
			$avatarService = $this->app->service(AvatarService::class, $user);
			$avatarService->logIp(false);
			$avatarService->deleteAvatar();
		}

		if ($user->Profile && $user->Profile->banner_date > 0)
		{
			$bannerService = $this->app->service(ProfileBannerService::class, $user);
			$bannerService->logIp(false);
			$bannerService->deleteBanner();
		}

		$this->submitData();
	}

	public function submitData()
	{
		$submitter = $this->app->container('spam.userSubmitter');
		$submitter->submit($this->user);
	}

	protected function getDefaultActions()
	{
		return [
			'action_threads' => false,
			'delete_messages' => false,
			'delete_conversations' => false,
			'ban_user' => false,
			'check_ips' => false,
		];
	}

	protected function prepareActions(array $actions)
	{
		return array_replace($this->getDefaultActions(), $actions);
	}

	public function cleanUp(array $actions)
	{
		$actions = $this->prepareActions($actions);

		$this->forceTransaction();

		if ($actions['ban_user'])
		{
			$this->banUser();
		}

		$this->cleanUpContent($actions);
	}

	public function cleanUpContent(array $actions)
	{
		$actions = $this->prepareActions($actions);

		$this->forceTransaction();

		$spamRepo = $this->app->repository(SpamRepository::class);

		$spamHandlers = $spamRepo->getSpamHandlers($this->user);
		foreach ($spamHandlers AS $contentType => $spamHandler)
		{
			if ($spamHandler->canCleanUp($actions))
			{
				if (!$spamHandler->cleanUp($this->log, $error))
				{
					$this->logError($contentType, $error);

					return;
				}
			}
		}

		if ($actions['delete_messages'])
		{
			$reports = $this->app->finder(ReportFinder::class)->where('content_user_id', $this->user->user_id);
			foreach ($reports->fetch() AS $report)
			{
				$report->report_state = 'resolved';
				$report->save();
			}
		}
	}

	public function finalize()
	{
		$db = $this->db;

		if (count($this->errors))
		{
			if ($this->inTransaction)
			{
				$db->rollback();
				$this->inTransaction = false;
			}

			return false;
		}

		$this->user->save();
		$this->writeLog();

		if ($this->inTransaction)
		{
			$db->commit();
			$this->inTransaction = false;
		}

		return true;
	}

	protected function writeLog()
	{
		$db = $this->db;

		$user = $this->user;
		$visitor = \XF::visitor();

		// log progress
		$db->insert('xf_spam_cleaner_log', [
			'user_id' => $user->user_id,
			'username' => $user->username,
			'applying_user_id' => $visitor->user_id,
			'applying_username' => $visitor->username,
			'application_date' => time(),
			'data' => (count($this->log) ? json_encode($this->log) : ''),
		]);

		$this->app->logger()->logModeratorAction('user', $user, 'spam_clean');
	}

	protected function forceTransaction()
	{
		if (!$this->inTransaction)
		{
			$this->db->beginTransaction();
			$this->inTransaction = true;
		}
	}

	public function getErrors()
	{
		return $this->errors;
	}

	protected function log($logKey, $value)
	{
		$this->log[$logKey] = $value;
	}

	protected function logError($logKey, $value)
	{
		$this->errors[$logKey] = $value;
	}
}
