<?php

namespace Opentalent\OtTemplating\Page;

use Opentalent\OtCore\Website\OtPageRepository;
use Opentalent\OtCore\Website\OtWebsiteRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3\CMS\Fluid\View\TemplateView;
use TYPO3Fluid\Fluid\View\ViewInterface;

class ErrorHandler implements PageErrorHandlerInterface
{
    const string TEMPLATES_ROOT_PATHS = 'EXT:ot_templating/Resources/Private/Templates';
    const string LAYOUTS_ROOT_PATHS = 'EXT:ot_templating/Resources/Private/Layouts';
    const string PARTIALS_ROOT_PATHS = 'EXT:ot_templating/Resources/Private/Partials';

    const array TEMPLATE_FILES = [
        403 => self::TEMPLATES_ROOT_PATHS . '/Page/Error/403.html',
        404 => self::TEMPLATES_ROOT_PATHS . '/Page/Error/404.html',
        500 => self::TEMPLATES_ROOT_PATHS . '/Page/Error/500.html'
    ];

    const string REDIRECT_FALLBACK = 'https://opentalent.fr';

    /**
     * Status code of the response
     * @var int
     */
    protected int $statusCode;

    /**
     * Configuration of the error handler as set in site configuration.
     * @var array
     */
    protected array $errorHandlerConfiguration;

    private OtWebsiteRepository $otWebsiteRepository;
    private OtPageRepository $otPageRepository;
    private RenderingContext $renderingContext;
    private Request $request;

    /**
     * @var ViewInterface
     */
    protected $view;

    public function __construct(int $statusCode, array $configuration)
    {
        $this->statusCode = $statusCode;
        $this->errorHandlerConfiguration = $configuration;

        $this->view = GeneralUtility::makeInstance(TemplateView::class);

        $this->otWebsiteRepository = GeneralUtility::makeInstance(OtWebsiteRepository::class);
        $this->otPageRepository = GeneralUtility::makeInstance(OtPageRepository::class);

        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
        $this->otWebsiteRepository->injectConnectionPool($connectionPool);

        $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
        $this->otPageRepository->injectPageRepository($pageRepository);
        $this->otPageRepository->injectConnectionPool($connectionPool);

        $this->request = GeneralUtility::makeInstance(Request::class);

        $this->renderingContext = GeneralUtility::makeInstance(RenderingContext::class);
    }

    /**
     * @param OtWebsiteRepository $otWebsiteRepository
     * @return void
     */
    public function injectOtWebsiteRepository(OtWebsiteRepository $otWebsiteRepository) {
        $this->otWebsiteRepository = $otWebsiteRepository;
    }

    /**
     * @param OtPageRepository $otWebsiteRepository
     * @return void
     */
    public function injectOtPageRepository(OtPageRepository $otPageRepository) {
        $this->otPageRepository = $otPageRepository;
    }

    /**
     * @param RenderingContext $renderingContext
     * @return void
     */
    public function injectRenderingContext(RenderingContext $renderingContext) {
        $this->renderingContext = $renderingContext;
    }

    /**
     * @param Request $request
     * @return void
     */
    public function injectRequest(Request $request) {
        $this->request = $request;
    }

    /**
     * @param ServerRequestInterface $request
     * @param string $message
     * @param array $reasons
     * @return ResponseInterface
     */
    public function handlePageError(
        ServerRequestInterface $request,
        string $message,
        array $reasons = []
    ): ResponseInterface {
        $this->request->setControllerExtensionName('ot_templating');
        $this->renderingContext->setRequest($this->request);

        $this->view->setRenderingContext($this->renderingContext);
        $this->view->setTemplateRootPaths([self::TEMPLATES_ROOT_PATHS]);
        $this->view->setLayoutRootPaths([self::LAYOUTS_ROOT_PATHS]);
        $this->view->setPartialRootPaths([self::PARTIALS_ROOT_PATHS]);
        $this->view->setTemplatePathAndFilename(
            GeneralUtility::getFileAbsFileName(self::TEMPLATE_FILES[$this->statusCode])
        );

        $homeUri = "https://opentalent.fr";
        $title = 'Page Introuvable';

        // This variable aims to prevent redirection loop
        $isCircular = preg_match('/.*\/page-introuvable/', $request->getUri()->getPath());

        $website = $this->otWebsiteRepository->getCurrentWebsiteFromFEGlobals();

        if ($website && !$isCircular){

            $homeUri = $this->otWebsiteRepository->resolveWebsiteBaseUri($website);
            $rootUid = $this->otWebsiteRepository->getWebsiteRootUid($website['uid']);

            $rootPage = $this->otPageRepository->getPage($rootUid);
            if (!empty($rootPage)) {

                $title = $rootPage['title'];

                $subPages = $this->otPageRepository->getAllSubpagesForPage($rootUid, true);
                foreach ($subPages as $page) {
                    if ($page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->error/404') {

                        $errorHandler = GeneralUtility::makeInstance(
                            PageContentErrorHandler::class,
                            $this->statusCode,
                            [
                                'errorHandler' => 'Page',
                                'errorContentSource' => 't3://page?uid=' . $page['uid']
                            ]
                        );
                        return $errorHandler->handlePageError($request, $message, $reasons);
                    }
                }
            } else {
                $title = 'Page Introuvable';
                $homeUri = self::REDIRECT_FALLBACK;
            }

        }

        // At this point, the site has not been determined or
        //   this site has no subpage with slug '/page-introuvable'
        $this->view->assignMultiple([
            'request' => $request,
            'message' => $message,
            'reasons' => $reasons,
            'homeUri' => $homeUri,
            'siteTitle' => $title
        ]);

        return new HtmlResponse($this->view->render(), $this->statusCode);
    }

}
