<?php

namespace App\Controller\Helper;

use App\Model\DataObject\User;
use App\Tool\Utils;
use Pimcore\Translation\Translator;
use Pimcore\Model\DataObject;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Pimcore\Cache\RuntimeCache;
use Pimcore\Twig\Extension\Templating\HeadLink;
use Pimcore\Twig\Extension\Templating\HeadMeta;
use Pimcore\Twig\Extension\Templating\HeadScript;
use Pimcore\Twig\Extension\Templating\HeadTitle;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Twig\Environment;

trait ControllerTrait
{
	/** @var Request */
	private $request;

	/** @var string */
	protected $language;
	/** @var string */
	protected $defaultLanguage;
	/** @var string */
	protected $currentUrl;
	/** @var string */
	protected $cacheKeySuffix;

	/**
	 * container for breadcrumbs extension on custom routed pages.
	 *
	 * @var array
	 */
	protected $addToBreadcrumbs = [];

	/**
	 * container for related pages in other system registered languages.
	 *
	 * @var type
	 */
	public $languageSwitch = [];

	/** @var Translator */
	protected $translator;

	/** @var Environment */
	private $twig;

	/** @var HeadScript */
	public $headScript;

	/** @var HeadLink */
	public $headLink;

	/** @var HeadMeta */
	public $headMeta;

	/** @var HeadTitle */
	public $headTitle;

	/** @var AjaxResponseBuilder */
	public $ajaxResponseBuilder;

	/** @var ApplicationLogger */
	protected $applicationLogger;

	/**
	 * @return Request
	 */
	protected function getRequest()
	{
		return $this->request;
	}

	/**
	 * @return string
	 */
	protected function getLanguage()
	{
		return $this->language;
	}

	/**
	 * @param string $realFullPath
	 * @param string $language
	 *
	 * @return string
	 */
	protected function docUrl($realFullPath, $language = null)
	{
		if (null === $language) {
			$language = $this->getLanguage();
		}

		return Utils::docUrl($realFullPath, $language);
	}

	/**
	 * @param string $documentId
	 * @param string $language
	 *
	 * @return string
	 */
	protected function docUrlById($documentId, $language = null)
	{
		if (null === $language) {
			$language = $this->getLanguage();
		}

		return Utils::docUrlById($documentId, $language);
	}

	/**
	 * adds a success flash message.
	 *
	 * @param string $msg               the message
	 * @param string $forCurrentRequest shows the message on current request (without redirect)
	 */
	public function addSuccessMsg($msg, $forCurrentRequest = false)
	{
		if ($forCurrentRequest) {
			$this->addFlashForCurrentRequest('success', $msg);
		} else {
			$this->addFlash('success', $msg);
		}
	}

	/**
	 * adds an error flash message.
	 *
	 * @param string $msg               the message
	 * @param string $forCurrentRequest shows the message on current request (without redirect)
	 */
	public function addErrorMsg($msg, $forCurrentRequest = false)
	{
		if ($forCurrentRequest) {
			$this->addFlashForCurrentRequest('danger', $msg);
		} else {
			$this->addFlash('danger', $msg);
		}
	}

	/**
	 * adds a notice flash message.
	 *
	 * @param string $msg               the message
	 * @param string $forCurrentRequest shows the message on current request (without redirect)
	 */
	public function addInfoMsg($msg, $forCurrentRequest = false)
	{
		if ($forCurrentRequest) {
			$this->addFlashForCurrentRequest('info', $msg);
		} else {
			$this->addFlash('info', $msg);
		}
	}

	/**
	 * @param string $type
	 * @param string $msg
	 */
	private function addFlashForCurrentRequest($type, $msg)
	{
		$flashMessages = [];
		if (RuntimeCache::isRegistered('currentRequestFlashMessages')) {
			$flashMessages = RuntimeCache::get('currentRequestFlashMessages');
		}
		if (!isset($flashMessages[$type])) {
			$flashMessages[$type] = [];
		}
		$flashMessages[$type][] = $msg;
		RuntimeCache::save($flashMessages, 'currentRequestFlashMessages');
	}

	/**
	 * redirects to given URL.
	 *
	 * @param string $url the URL
	 *
	 * @return RedirectResponse|null
	 */
	public function gotoUrl($url, $code = 302)
	{
		if ($this->getRequest()->get('elastic_document_indexing')) {
			return;
		}

		return $this->redirect($url, $code);
	}

	/**
	 * redirects to given static route.
	 *
	 * @param string $route  name of the static route
	 * @param array  $params the params for placeholders
	 *
	 * @return RedirectResponse|null
	 */
	public function gotoRoute($route, $params = [], $code = null)
	{
		return $this->gotoUrl(Utils::url($route, $params), $code);
	}

	/**
	 * @param string $keyword the translation key
	 *
	 * @return string translated message
	 */
	public function translate($keyword)
	{
		return $this->translator->trans($keyword);
	}

	/**
	 * because document save triggers elasticsearch document indexing,
	 * which would be handled as ajax request.
	 *
	 * @return bool
	 */
	public function isAjax()
	{
		return $this->getRequest()->isXmlHttpRequest() &&
			!\App\Tool\ElasticSearch::isUpdateRequest() &&
			!$this->getRequest()->get('elastic_document_indexing')
		;
	}

	/**
	 * checks if there is a cache hit for given key.
	 *
	 * @param string $cacheKey
	 *
	 * @return bool
	 */
	protected function isOutputCached($cacheKey)
	{
		$viewCacheKeyPrefix = 'pimcore_viewcache_';

		if (\Pimcore\Tool::isFrontendRequestByAdmin() ||
				false === \Pimcore\Cache::load($viewCacheKeyPrefix.$cacheKey) ||
				$this->getRequest()->get('elastic_document_indexing')) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * @return bool
	 */
	protected function isEditOrAdminMode()
	{
		if ($this->editmode) {
			return true;
		}
		if (\Pimcore\Tool::isFrontendRequestByAdmin()) {
			$admin = \Pimcore\Tool\Authentication::authenticateSession();
			if ($admin instanceof \Pimcore\Model\User) {
				return true;
			}
		}

		return false;
	}

	/**
	 * tries to set the given url into the referer cookie for previous page purposes.
	 *
	 * @param string $relativeUrl the relative URL
	 * @param bool   $force       force cookie overwrite
	 * @param bool   $seconds     lifetime
	 */
	protected function setPreviousUrl($relativeUrl, $force = false, $seconds = 600)
	{
		if ((!$this->getRequest()->cookies->get('referer', false) || $force) && $relativeUrl) {
			setcookie('referer', $relativeUrl, time() + $seconds, '/');
		}
	}

	/**
	 * tries to get the previous URL from referer cookie.
	 *
	 * @param string $defaultUrl default URL in case the cookie is not set
	 *
	 * @return string relative URL
	 */
	protected function getPreviousUrl($defaultUrl = null)
	{
		$referer = $this->getRequest()->cookies->get('referer', false);
		$httpReferer = $this->getHttpReferer();

		if ($referer) {
			setcookie('referer', '', time() - 111, '/');
			//return $this->docUrl($referer);
			return $referer;
		} elseif (null !== $defaultUrl) {
			return $defaultUrl;
		} elseif ($httpReferer) {
			return $httpReferer;
		} else {
			return $this->docUrl('/');
		}
	}

	/**
	 * @return misc referer URL from HTTP_REFERER server option if it is set and from the site domain or false
	 */
	protected function getHttpReferer()
	{
		$parts1 = parse_url($this->getRequest()->getSchemeAndHttpHost());
		$parts2 = parse_url($this->getRequest()->server->get('HTTP_REFERER', ''));

		if (!isset($parts1['scheme']) || !isset($parts1['host']) || !isset($parts2['scheme']) || !isset($parts2['host'])) {
			return false;
		}

		if ($parts1['scheme'] == $parts2['scheme'] && $parts1['host'] == $parts2['host'] && isset($parts2['path'])) {
			$query = ($parts2['query']) ? '?'.$parts2['query'] : '';

			return ('' != $parts2['path'] && '/' != $parts2['path']) ? $parts2['path'].$query : false;
		}

		return false;
	}

	/**
	 * @param string $url
	 */
	protected function setCanonicalUrl($url = null)
	{
		if (!$url) {
			$url = $this->canonicalUrl = preg_replace(
				'/\?.*/',
				'',
				$this->getRequest()->getSchemeAndHttpHost().$this->currentUrl
			);
		}
		if ('http' != substr($url, 0, 4)) {
			$url = $this->getRequest()->getSchemeAndHttpHost().$url;
		}

		$this->headLink->appendAlternate([
			'rel' => 'canonical',
			'href' => $url,
		]);
	}

	/**
	 * @return \Symfony\Component\HttpFoundation\Response|null
	 */
	protected function checkAuth()
	{
		// mock user in editmode
		if ($this->isEditOrAdminMode()) {
			$user = new User();
			$user->mockForEditmode();

			$token = new UsernamePasswordToken($user, 'dummy', ['ROLE_USER']);
			$this->container->get('security.token_storage')->setToken($token);
		} else {
			if (!$this->isGranted('ROLE_USER')) {
				$this->addErrorMsg($this->translate('msg_authentication_required'));

				return $this->gotoUrl($this->docUrl('/prihlaseni'));
			}
		}

		return null;
	}

	/**
	 * @return User|null
	 */
	protected function getCurrentUser()
	{
		return $this->getUser();
	}

	/**
	 * @param DataObject\Concrete $object the object instance
	 *
	 * @return array associative array with localized URLs for each language, e.g. ['en' => 'en_url', 'cs' => 'cs_url']
	 */
	protected function buildLanguageSwitchFromObject($object, $hpFallbackPath = null)
	{
		$switcher = [];
		$languages = Utils::getValidFrontendLanguages();

		if (method_exists($object, 'getUrl')) {
			foreach ($languages as $language) {
				$url = $object->getUrl($language);
				if (!$url && $hpFallbackPath) {
					$url = $this->docUrl($hpFallbackPath, $language);
					if ('/' == $url) {
						$url = null;
					}
				}
				if ($url) {
					$switcher[$language] = $url;
				}
			}
		}

		return $switcher;
	}

	/**
	 * @param string $routeName            name of the used static route
	 * @param bool   $routeForEachLanguage tells if there are seperate static routes for each language
	 * @param array  $params               params needed to assemble the URL
	 *
	 * @return array associative array with localized URLs for each language, e.g. ['en' => 'en_url', 'cs' => 'cs_url']
	 */
	protected function buildLanguageSwitchFromRoute($routeName, $routeForEachLanguage = true, $params = [])
	{
		$switcher = [];
		$languages = Utils::getValidFrontendLanguages();

		if (!$routeForEachLanguage) {
			$route = \Pimcore\Model\Staticroute::getByName($routeName, (\Pimcore\Model\Site::isSiteRequest()) ? \Pimcore\Model\Site::getCurrentSite()->getId() : 0);
		}

		foreach ($languages as $language) {
			$baseUrl = '';
			$siteId = 0;
			if ($routeForEachLanguage) {
				if (\Pimcore\Model\Site::isSiteRequest()) {
					$siteRootId = \Pimcore\Model\Document\Page::getByPath('/'.$language)->getId();
					$site = \Pimcore\Model\Site::getByRootId($siteRootId);
					if ($site) {
						$siteId = $site->getId();
						$baseUrl = $this->getRequest()->getScheme().'://'.$site->getMainDomain();
					}
				}
				$route = \Pimcore\Model\Staticroute::getByName($routeName.$language, $siteId);
			} else {
				$params['language'] = $language;
			}
			$switcher[$language] = $baseUrl.$route->assemble($params, true);
		}

		return $switcher;
	}

	/**
	 * @param misc $list    pimcore object list
	 * @param misc $current pimcore object
	 * @param misc $param   object param name to compare on
	 *
	 * @return type returns previous and next sibblings from pimcore object list
	 */
	protected function getSiblings($list, $current, $param)
	{
		$previous = null;
		$tmpList = $list->getObjects();
		end($tmpList);
		$tmpPrevious = current($tmpList); //last
		reset($tmpList);
		$next = current($tmpList); //first
		$loadNext = false;
		foreach ($list as $tmpCurrent) {
			if (true == $loadNext) {
				$next = $tmpCurrent;
				$loadNext = false;
			}
			if ($current->{'get'.ucfirst($param)}() == $tmpCurrent->{'get'.ucfirst($param)}()) {
				$previous = $tmpPrevious;
				$loadNext = true;
			}
			$tmpPrevious = $tmpCurrent;
		}

		return [$previous, $next];
	}
}
