<?php
declare(strict_types=1);
namespace Wga\Core\Condition;

use FluidTYPO3\Flux\Service\PageService;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Wga\Core\Utility\DatabaseUtility;
use Wga\Core\Utility\FrontendUtility;
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;

/**
 * Class WgaConditionFunctionsProvider
 * to provide new functions in TypoScript conditions
 */
class WgaConditionFunctionsProvider implements ExpressionFunctionProviderInterface
{
    /**
     * @return array|ExpressionFunction[]
     */
    public function getFunctions()
    {
        return [
            $this->isPluginOnCurrentPageFunction(),
            $this->isColPosFunction(),
            $this->isNewsDetailPageFunction(),
            $this->isCurrentHostFunction(),
            $this->isPageHasTemplateFunction(),
        ];
    }

    /**
     * Check if pluginname is anywhere on this page with a new function for conditions: isPluginOnCurrentPage()
     *
     * Example usages:
     *      [isPluginOnCurrentPage()] for tt_content.list_type=news_pi1 or
     *      [isPluginOnCurrentPage(['news_pi1', 'news_pi1'])] for both plugins
     *
     * @return ExpressionFunction
     */
    protected function isPluginOnCurrentPageFunction(): ExpressionFunction
    {
        return new ExpressionFunction('isPluginOnCurrentPage', function () {
            // Not implemented, we only use the evaluator
        }, function (array $existingVariables, array $plugins = ['news_pi1']) {
            unset($existingVariables);
            return $this->isPluginExistingOnCurrentPageInCurrentLanguage($plugins);
        });
    }

    /**
     * @param array $plugins like ['news_pi1', 'news_pi1']
     * @return bool
     */
    protected function isPluginExistingOnCurrentPageInCurrentLanguage(array $plugins): bool
    {
        $listTypes = implode('","', $plugins);
        $queryBuilder = DatabaseUtility::getQueryBuilderForTable('tt_content');
        $row = $queryBuilder
            ->select('*')
            ->from('tt_content')
            ->where('pid=' . FrontendUtility::getCurrentPageIdentifier()
                . ' and CType="list" and list_type in ("' . $listTypes . '") and sys_language_uid='
                . FrontendUtility::getSysLanguageUid())
            ->setMaxResults(1)
            ->execute()
            ->fetch();
        return !empty($row['uid']);
    }

    /**
     * Check if current colPos matches the specified
     *
     * Example usages:
     *      [isColPos()] for 0 or
     *      [isColPos( 1 )] another colPos
     *
     * @return ExpressionFunction
     */
    protected function isColPosFunction(): ExpressionFunction
    {
        return new ExpressionFunction('isColPos', function () {
            // Not implemented, we only use the evaluator
        }, function (array $existingVariables, int $colPos = 0) {
            unset($existingVariables);

            return (isset($_GET['colPos']) && $_GET['colPos']) == $colPos;
        });
    }

    /**
     * Check if current page is news detail page
     *
     * Example usages:
     *      [isNewsDetailPage()]
     *
     * @return ExpressionFunction
     */
    protected function isNewsDetailPageFunction(): ExpressionFunction
    {
        return new ExpressionFunction('isNewsDetailPage', function () {
            // Not implemented, we only use the evaluator
        }, function (array $existingVariables) {
            unset($existingVariables);
            $news_params = $_GET['tx_news_pi1'];
            // phpcs:ignore
            return ( isset($news_params['news']) && ! empty($news_params['news']) ) || ( isset($news_params['news_preview']) && ! empty($news_params['news_preview']) );
        });
    }

    /**
     * Check current host.
     *
     * Example usages:
     *      [currentHost( 'wga-typo3-11.ddev.site' )]
     *
     * @return ExpressionFunction
     */
    protected function isCurrentHostFunction(): ExpressionFunction
    {
        return new ExpressionFunction('isCurrentHost', function () {
            // Not implemented, we only use the evaluator
        }, function (array $existingVariables, string $host = '') {
            unset($existingVariables);
			if (isset($_SERVER['HTTP_HOST'])) {
				return $_SERVER['HTTP_HOST'] == $host;
			} else {
				return false;
			}
        });
    }

    /**
     * Checks if page has template
     * [isPageHasTemplate('Wga.WgaTheme->home')]
     *
     * @return ExpressionFunction
     */
    protected function isPageHasTemplateFunction(): ExpressionFunction
    {
        return $this->getExpressionFunction('isPageHasTemplate', function ($arguments, $desiredTemplateName) {
            $pageUid = $this->determinePageUid();

            if ($pageUid == null) {
                return false;
            }

            $pageDokType = $this->fetchPageDocTypeByUid($pageUid);

            if (in_array($pageDokType, [PageRepository::DOKTYPE_SYSFOLDER, PageRepository::DOKTYPE_SPACER, PageRepository::DOKTYPE_RECYCLER])) {
                return false;
            }

            $pageTemplateName = $this->fetchPageTemplateNameByUid($pageUid);

            return $pageTemplateName == $desiredTemplateName;
        });
    }

    /**
     * @param int $pageUid
     *
     * @return string
     */
    protected function fetchPageTemplateNameByUid(int $pageUid): string
    {
        $container = GeneralUtility::getContainer();

        $pageTemplateConfiguration1 = $container->get(PageRepository::class)->getPage($pageUid)['tx_fed_page_controller_action'];

        $pageTemplateConfiguration2 = $container->get(PageService::class)->getPageTemplateConfiguration($pageUid)['tx_fed_page_controller_action'];

        if ($pageTemplateConfiguration1) {
            $pageTemplateConfiguration = $pageTemplateConfiguration1;
        } elseif ($pageTemplateConfiguration2) {
            $pageTemplateConfiguration = $pageTemplateConfiguration2;
        }

        return $pageTemplateConfiguration ?? '';
    }

    /**
     * @param string   $name
     * @param callable $function
     *
     * @return ExpressionFunction
     */
    protected function getExpressionFunction(string $name, callable $function): ExpressionFunction
    {
        return new ExpressionFunction(
            $name,
            function () {
            },
            $function
        );
    }

    /**
     * @return int|null
     */
    protected function determinePageUid(): ?int
    {
        return $this->determinePageUidByIdGetParams() ??  $this->determinePageUidByReturnUrlGetParams();
    }

    /**
     * @return int|null
     */
    protected function determinePageUidByIdGetParams(): ?int
    {
        if (!empty($_GET['id'])) {
            return (int)$_GET['id'];
        }

        return null;
    }

    /**
     * @return int|null
     */
    protected function determinePageUidByReturnUrlGetParams(): ?int
    {
        if (!empty($_GET['returnUrl'])) {
            $returnUrl = $_GET['returnUrl'];
            $returnUrlPaths = parse_url($returnUrl);
            $returnUrlQuery = \GuzzleHttp\Psr7\Query::parse($returnUrlPaths['query'], true);

            if (!empty($returnUrlQuery['id'])) {
                return (int)$returnUrlQuery['id'];
            }
        }

        return null;
    }

    /**
     * @param int $pageUid
     * @return int
     */
    protected function fetchPageDocTypeByUid(int $pageUid): int
    {
        $queryBuilder = $this->getQueryBuilder('pages');
        $queryBuilder->getRestrictions()->removeAll();

        $page = $queryBuilder
            ->select('doktype')
            ->from('pages')
            ->where(
                $queryBuilder->expr()->eq('uid', (int)$pageUid)
            )
            ->execute()
            ->fetchAssociative();

        return $page['doktype'] ?? 0;
    }

    /**
     * @param string $tableName
     *
     * @return QueryBuilder
     */
    protected function getQueryBuilder(string $tableName): QueryBuilder
    {
        /** @var ConnectionPool $connectionPool */
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
        return $connectionPool->getQueryBuilderForTable($tableName)->from($tableName);
    }
}
