<?php

namespace Opentalent\OtCore\Tests\Unit\Controller;

use Nimut\TestingFramework\TestCase\UnitTestCase;
use Opentalent\OtCore\Controller\SelectedSiteController;
use Opentalent\OtCore\Exception\NoSiteSelected;
use Opentalent\OtCore\Website\OtPageRepository;
use Opentalent\OtCore\Website\OtWebsiteRepository;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;

class TestableSelectedSiteController extends SelectedSiteController {
    public $currentWebsite;
    public $currentRootUid;

    public function callActionMethod(RequestInterface $request): \Psr\Http\Message\ResponseInterface
    {
        return parent::callActionMethod($request);
    }

    public function callParentActionMethod(RequestInterface $request): \Psr\Http\Message\ResponseInterface
    {
        return parent::callParentActionMethod($request);
    }
}

class SelectedSiteControllerTest extends UnitTestCase
{
    protected MockObject | OtPageRepository $otPageRepository;
    protected MockObject | OtWebsiteRepository $otWebsiteRepository;

    public function setUp(): void
    {
        $this->otPageRepository = $this
            ->getMockBuilder(OtPageRepository::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->otWebsiteRepository = $this
            ->getMockBuilder(OtWebsiteRepository::class)
            ->disableOriginalConstructor()
            ->getMock();
    }

    public function getMockForMethod(string $methodName): MockObject | TestableSelectedSiteController
    {
        $controller = $this->getMockBuilder(TestableSelectedSiteController::class)
            ->setMethodsExcept([$methodName, 'injectOtPageRepository', 'injectOtWebsiteRepository'])
            ->getMock();

        $controller->injectOtPageRepository($this->otPageRepository);
        $controller->injectOtWebsiteRepository($this->otWebsiteRepository);

        return $controller;
    }

    /**
     * If the current Be-user has only one website mounted,
     * then the currentRootUid should be this website root uid
     *
     * @test
     */
    public function callActionMethodForSingleSite() {
        $controller = $this->getMockForMethod('callActionMethod');

        $this->otPageRepository->method('getCurrentBeRootUid')->willThrowException(new NoSiteSelected());

        $this->otPageRepository
            ->expects(self::once())
            ->method('getCurrentBeUserMountpoints')
            ->willReturn([1]);

        $this->otWebsiteRepository
            ->expects(self::once())
            ->method('getWebsiteByPageUid')
            ->with(1)
            ->willReturn(['uid' => 1, 'title' => 'Foo']);

        $response = $this
            ->getMockBuilder(ResponseInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $controller
            ->expects(self::once())
            ->method('callParentActionMethod')
            ->willReturn($response);

        $request = $this->getMockBuilder(RequestInterface::class)->disableOriginalConstructor()->getMock();

        $result = $controller->callActionMethod($request);

        $this->assertEquals($response, $result);
        $this->assertEquals($controller->currentRootUid, 1);
        $this->assertEquals($controller->currentWebsite, ['uid' => 1, 'title' => 'Foo']);
    }

    /**
     * If the current Be-user has many websites mounted and a website page is selected,
     * then the currentRootUid should be the currently selected website root page uid
     *
     * @test
     */
    public function callActionMethodForMultiSite() {
        $controller = $this->getMockForMethod('callActionMethod');

        $this->otPageRepository->method('getCurrentBeRootUid')->willReturn(2);

        $this->otPageRepository
            ->expects(self::once())
            ->method('getCurrentBeUserMountpoints')
            ->willReturn([1, 2]);

        $this->otWebsiteRepository
            ->method('getWebsiteByPageUid')
            ->with(2, true)
            ->willReturn(['uid' => 2, 'title' => 'Bar']);

        $request = $this->getMockBuilder(RequestInterface::class)->disableOriginalConstructor()->getMock();

        $response = $this
            ->getMockBuilder(ResponseInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $controller
            ->expects(self::once())
            ->method('callParentActionMethod')
            ->willReturn($response);

        $result = $controller->callActionMethod($request);

        $this->assertEquals($response, $result);
        $this->assertEquals($controller->currentRootUid, 2);
        $this->assertEquals($controller->currentWebsite, ['uid' => 2, 'title' => 'Bar']);
    }

    /**
     * If the current Be-user has many websites mounted and no website page is selected,
     * then the currentRootUid should be null
     *
     * @test
     */
    public function callActionMethodForNoSite() {
        $controller = $this->getMockForMethod('callActionMethod');

        $this->otPageRepository->method('getCurrentBeRootUid')->willThrowException(new NoSiteSelected());

        $this->otPageRepository
            ->expects(self::once())
            ->method('getCurrentBeUserMountpoints')
            ->willReturn([1, 2]);

        $this->otWebsiteRepository
            ->expects(self::never())
            ->method('getWebsiteByPageUid');

        $controller
            ->expects(self::never())
            ->method('callParentActionMethod');

        $request = $this->getMockBuilder(RequestInterface::class)->disableOriginalConstructor()->getMock();

        $this->expectException(\Exception::class);
        $this->expectExceptionMessage("No website selected");

        $controller->callActionMethod($request);
    }
}
