<?php

namespace Opentalent\OtCore\Website;

use Opentalent\OtCore\Exception\NoSiteSelected;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
 * Class OtPageRepository
 *
 * Provides some useful methods to query typo3 pages
 *
 * @package Opentalent\OtCore\Page
 */
class OtPageRepository
{
    /**
     * @var \FluidTYPO3\Vhs\Service\PageService
     */
    private \FluidTYPO3\Vhs\Service\PageService $pageService;

    /**
     * @var \TYPO3\CMS\Frontend\Page\PageRepository
     */
    private \TYPO3\CMS\Frontend\Page\PageRepository $pageRepository;

    /**
     * @var \TYPO3\CMS\Core\Database\ConnectionPool
     */
    private \TYPO3\CMS\Core\Database\ConnectionPool $connectionPool;

    /**
     * @var \TYPO3\CMS\Core\Site\SiteFinder
     */
    private \TYPO3\CMS\Core\Site\SiteFinder $siteFinder;

    public function injectPageService(\FluidTYPO3\Vhs\Service\PageService $pageService)
    {
        $this->pageService = $pageService;
    }

    public function injectPageRepository(\TYPO3\CMS\Frontend\Page\PageRepository $pageRepository)
    {
        $this->pageRepository = $pageRepository;
    }

    public function injectConnectionPool(\TYPO3\CMS\Core\Database\ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    public function injectSiteFinder(\TYPO3\CMS\Core\Site\SiteFinder $siteFinder)
    {
        $this->siteFinder = $siteFinder;
    }

    /**
     * Returns all the subpages of the given page
     *
     * @param int $pid The uid of the parent page
     * @param bool $withRestrictions Set to true to add the standard restrictions (deleted, forbidden...etc.)
     * @return array
     */
    public function getPagesByPid(int $pid, bool $withRestrictions=false): array
    {
        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
        if (!$withRestrictions) {
            $queryBuilder->getRestrictions()->removeAll();
        }
        return $queryBuilder
            ->select('*')
            ->from('pages')
            ->where($queryBuilder->expr()->eq('pid', $pid))
            ->execute()
            ->fetchAll();
    }

    /**
     * Returns the root page of the given page website,
     * or the page itself if the given page is
     * already the rootpage of the site
     *
     * @param $pageUid
     *
     * @return array
     */
    public function getRootPageFor($pageUid): array
    {
        $rootLine = $this->pageService->getRootLine($pageUid);

        for (end($rootLine); key($rootLine)!==null; prev($rootLine)){
            $page = current($rootLine);
            if ($page['is_siteroot'] == 1) {
                $page = $this->pageRepository->getPage($page['uid']);
                return $page;
            }
        }
        return [];
    }

    /**
     * Recursively returns all the subpages of the given page
     *
     * @param int $pageUid The uid of the parent page
     * @param bool $withRestrictions Set to true to add the standard restrictions (deleted, forbidden...etc.)
     * @return array
     */
    public function getAllSubpagesForPage(int $pageUid, bool $withRestrictions=false): array
    {
        $subpages = [];

        $stack = $this->getPagesByPid($pageUid, $withRestrictions);

        foreach ($stack as $page) {
            $subpages[] = $page;
            $children = $this->getAllSubpagesForPage($page['uid']);
            if (!empty($children)) {
                $subpages = array_merge($subpages, $children);
            }
        }
        return $subpages;
    }

    /**
     * Returns all the pages of the given page's website, starting from the root page
     *
     * @param int $pageUid
     * @param bool $withRestrictions Set to true to add the standard restrictions (deleted, forbidden...etc.)
     * @return array
     */
    public function getPageWithSubpages(int $pageUid, bool $withRestrictions=false): array
    {
        return array_merge([$this->getPage($pageUid)], $this->getAllSubpagesForPage($pageUid));
    }

    /**
     * Return the Site object for the given page
     *
     * @param int $pageUid
     * @return Site
     * @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
     */
    public function getSiteFor(int $pageUid): Site
    {
        $rootPage = $this->getRootPageFor($pageUid);
        $rootUid = $rootPage['uid'];

        return $this->siteFinder->getSiteByRootPageId($rootUid);
    }

    /**
     * Returns the typo3 site matching the current request (FE only)
     *
     * @return Site
     * @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
     */
    public function getCurrentSite(): Site
    {
        $request = $GLOBALS['TYPO3_REQUEST'];
        $site = $request->getAttribute('site');
        return $this->siteFinder->getSiteByIdentifier($site->getIdentifier());
    }

    /**
     * Returns the current site's rootpage uid (FE only)
     *
     * @return int
     * @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
     */
    public function getCurrentSiteRootPageUid(): int
    {
        $site = $this->getCurrentSite();
        return $site->getRootPageId();
    }

    /**
     * Returns the current site's rootpage URI (FE only)
     *
     * @return \Psr\Http\Message\UriInterface
     * @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
     */
    public function getCurrentSiteRootPageUri(): \Psr\Http\Message\UriInterface
    {
        $site = $this->getCurrentSite();
        return $site->getBase();
    }

    /**
     * [Frontend Only]
     * Returns the current site's rootpage array
     *
     * @return array
     * @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
     */
    public function getCurrentSiteRootPage(): array
    {
        $uid = $this->getCurrentSiteRootPageUid();
        return $this->pageRepository->getPage($uid);
    }

    /**
     * [Backend Only]
     * Returns the page currently selected in the backend if any
     *
     * @return int|null
     */
    public function getCurrentPageId(): ?int
    {
        return (int)GeneralUtility::_GP('id');
    }

    /**
     * [Backend Only]
     * Return the root uid of the currently selected website if any,
     * or throw a NoSiteSelected exception
     *
     * @return int
     * @throws NoSiteSelected
     */
    public function getCurrentRootUid(): int
    {
        $pageUid = $this->getCurrentPageId();

        $rootPage = $this->getRootPageFor($pageUid);
        $rootUid = $rootPage['uid'] ?? 0;
        if (!$rootUid > 0) {
            throw new NoSiteSelected();
        }
        return $rootUid;
    }

    /**
     * [Backend Only]
     *
     * Return the mountpoint(s) for the current BE User
     */
    public function getCurrentBeUserMountpoints(): array
    {
        // Check if the current be-user has a db_mountpoint, and only has one.
        // If so, this will be considered as the selected site (since its the only one available)
        $be_user = $GLOBALS['BE_USER'];
        if ($be_user == null) {
            return [];
        }

        $mountpoints = $be_user->returnWebmounts();
        return array_filter($mountpoints, function($m) { return is_numeric($m) && (int)$m > 0; });;
    }

    public function getPage(int $uid): array
    {
        return $this->pageRepository->getPage($uid, true);
    }
}
