<?php

namespace Opentalent\OtCore\Tests\Unit\Website;

use FluidTYPO3\Vhs\Service\PageService;
use Opentalent\OtCore\Website\OtPageRepository;
use Nimut\TestingFramework\TestCase\UnitTestCase;
use Opentalent\OtCore\Tests\Unit\Fixtures\PageFixtures;
use Opentalent\OtCore\Tests\Unit\QueryBuilderProphet;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;


class OtPageRepositoryTest extends UnitTestCase
{
    use ProphecyTrait;

    protected $pageService;
    protected $pageRepository;
    protected $connectionPool;
    protected $pageFixtures;
    protected $otPageRepository;

    public function setUp(): void
    {
        // Prophecies to be injected in the under test OtPageRepository
        $this->pageService = $this->prophesize(PageService::class);
        $this->pageRepository = $this->prophesize(PageRepository::class);
        $this->connectionPool = $this->prophesize(ConnectionPool::class);

        // Fixtures
        $this->pageFixtures = new PageFixtures();

        // The under test OtPageRepository
        $this->otPageRepository = new OtPageRepository();
    }

    /**
     * Inject the prophecies instantiated at setUp into the OtPageRepository under test object
     */
    protected function injectProphecies() {
        $this->otPageRepository->injectPageService($this->pageService->reveal());
        $this->otPageRepository->injectPageRepository($this->pageRepository->reveal());
        $this->otPageRepository->injectConnectionPool($this->connectionPool->reveal());
    }

    /**
     * Calling getPagesByPid() on a page should return its direct descendants
     *
     * @test
     */
    public function getPagesByPid() {

        // Prepare prophecies
        $page_uid = 210;
        $subpages = $this->pageFixtures->getDirectSubpagesFor($page_uid);

        $prophet = new QueryBuilderProphet($subpages);
        $queryBuilder = $prophet->prophesize();
        $this->connectionPool->getQueryBuilderForTable('pages')->willReturn($queryBuilder);
        $this->injectProphecies();

        // Test
        $expected = $subpages;
        $actual = $this->otPageRepository->getPagesByPid($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getRootPageFor() on a site's page should return the root page of the site
     *
     * @test
     */
    public function getRootPageForSiteSubpage() {

        // Prepare prophecies
        $page_uid = 210;
        $page = $this->pageFixtures->getByUid($page_uid);

        $root_uid = 200;
        $root_page = $this->pageFixtures->getByUid($root_uid);

        $this->pageRepository->getPage_noCheck($page_uid)->shouldBeCalled()->willReturn($page);
        $this->pageRepository->getPage_noCheck($root_uid)->shouldBeCalled()->willReturn($root_page);
        $this->injectProphecies();

        // Test
        $expected = $root_page;
        $actual = $this->otPageRepository->getRootPageFor($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getRootPageFor() on a site's root page should return this page
     *
     * @test
     */
    public function getRootPageForSiteRootpage() {

        // Prepare prophecies
        $root_uid = 200;
        $root_page = $this->pageFixtures->getByUid($root_uid);

        $this->pageRepository->getPage_noCheck($root_uid)->shouldBeCalled()->willReturn($root_page);
        $this->injectProphecies();

        // Test
        $expected = $root_page;
        $actual = $this->otPageRepository->getRootPageFor($root_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getRootPageFor() on a page that is not a site's page should return an empty array
     *
     * @test
     */
    public function getRootPageForNonSitePage() {

        // Prepare prophecies
        $page_uid = 100;
        $page = $this->pageFixtures->getByUid($page_uid);

        $this->pageRepository->getPage_noCheck($page_uid)->shouldBeCalled()->willReturn($page);
        $this->pageRepository->getPage_noCheck(0)->shouldBeCalled()->willReturn([]);
        $this->injectProphecies();

        // Test
        $expected = [];
        $actual = $this->otPageRepository->getRootPageFor($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getAllSubpagesForPage() on a site's page should return an array containing
     * as many records that it has subpages
     *
     * @test
     */
    public function getAllSubpagesForPageFromSubPage() {

        // Prepare prophecies
        $page_uid = 230;

        $sub_pages = [
                $this->pageFixtures->getByUid(231),
                $this->pageFixtures->getByUid(232),
                $this->pageFixtures->getByUid(233),
            ];

        $prophet = new QueryBuilderProphet($sub_pages, [], [], []);
        $queryBuilder = $prophet->prophesize();
        $this->connectionPool->getQueryBuilderForTable('pages')->willReturn($queryBuilder);
        $this->injectProphecies();

        // Test
        $expected = $sub_pages;
        $actual = $this->otPageRepository->getAllSubpagesForPage($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getAllSubpagesForPage() on a site's root page should return an array containing
     * as many records that it has pages
     *
     * @test
     */
    public function getAllSubpagesForPageFromRootPage() {

        // Prepare prophecies
        $page_uid = 200;

        $sub_pages = [];
        foreach ($this->pageFixtures->getAll() as $page) {
            if ($page['uid'] > 200) {
                $sub_pages[] = $page;
            }
        }

        $willReturn = [$this->pageFixtures->getDirectSubpagesFor($page_uid)];
        foreach ($sub_pages as $page) {
            $willReturn[] = $this->pageFixtures->getDirectSubpagesFor($page['uid']);
        }

        $prophet = new QueryBuilderProphet(...$willReturn);
        $queryBuilder = $prophet->prophesize();
        $this->connectionPool->getQueryBuilderForTable('pages')->willReturn($queryBuilder);
        $this->injectProphecies();

        // Test
        $expected = $sub_pages;
        $actual = $this->otPageRepository->getAllSubpagesForPage($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getAllSubpagesForPage() on a site's page that has got no child should
     * return an empty array
     *
     * @test
     */
    public function getAllSubpagesForPageFromEndpage() {

        // Prepare prophecies
        $page_uid = 211;

        $prophet = new QueryBuilderProphet([]);
        $queryBuilder = $prophet->prophesize();
        $this->connectionPool->getQueryBuilderForTable('pages')->willReturn($queryBuilder);
        $this->injectProphecies();

        // Test
        $expected = [];
        $actual = $this->otPageRepository->getAllSubpagesForPage($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getPageWithSubpages() should return the page itself, along with all of its subpages
     * No need to test more cases, since this method only extends the well tested getAllSubpagesForPage method.
     *
     * @test
     */
    public function getPageWithSubpagesForRootPage() {
        // Prepare prophecies
        $page_uid = 200;

        $sub_pages = [];
        foreach ($this->pageFixtures->getAll() as $page) {
            if ($page['uid'] > 200) {
                $sub_pages[] = $page;
            }
        }

        $willReturn = [$this->pageFixtures->getDirectSubpagesFor($page_uid)];
        foreach ($sub_pages as $page) {
            $willReturn[] = $this->pageFixtures->getDirectSubpagesFor($page['uid']);
        }

        $prophet = new QueryBuilderProphet(...$willReturn);
        $queryBuilder = $prophet->prophesize();
        $this->connectionPool->getQueryBuilderForTable('pages')->willReturn($queryBuilder);
        $this->injectProphecies();

        $page = $this->pageFixtures->getByUid($page_uid);

        $this->pageRepository->getPage_noCheck($page_uid)->willReturn($page);
        $this->otPageRepository->injectPageRepository($this->pageRepository->reveal());

        // Test
        $expected = array_merge([$page], $sub_pages);
        $actual = $this->otPageRepository->getPageWithSubpages($page_uid);

        $this->assertEquals($expected, $actual);
    }

    /**
     * Calling getCurrentBeRootUid() when the current page is a site's page
     * should return the root page uid of the site
     *
     * @test
     */
    public function getCurrentBeRootUidOnSitePage() {

        // Prepare prophecies
        $page_uid = 210;
        $page = $this->pageFixtures->getByUid($page_uid);

        $root_uid = 200;
        $root_page = $this->pageFixtures->getByUid($root_uid);

        $this->pageRepository->getPage_noCheck($page_uid)->shouldBeCalled()->willReturn($page);
        $this->pageRepository->getPage_noCheck($root_uid)->shouldBeCalled()->willReturn($root_page);
        $this->injectProphecies();

        $GLOBALS['_POST'] = ['id' => $page_uid];

        // Test
        $expected = $root_uid;
        $actual = $this->otPageRepository->getCurrentBeRootUid();

        $this->assertEquals($expected, $actual);
    }


}
