<?php declare(strict_types=1);


namespace Newland\NeosViewHelpers\Tests\Unit\ViewHelpers;


use Neos\Flow\Core\ApplicationContext;
use Neos\Flow\ObjectManagement\ObjectManager;
use Neos\Flow\Utility\Environment;
use Neos\FluidAdaptor\View\StandaloneView;
use Newland\NeosViewHelpers\ErrorHandling\ViewErrorHandler;
use Neos\Flow\ObjectManagement\Configuration\Configuration as ObjectConfiguration;
use Newland\NeosViewHelpers\Tests\Mock\ThrowableStorageMock;
use Newland\NeosViewHelpers\Tests\Unit\ViewHelperTestCase;
use TYPO3Fluid\Fluid\Core\ViewHelper;

/**
 * @property ObjectManager $objectManager
 */
class RenderViewHelperTest extends ViewHelperTestCase
{

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

    public function setUp(): void
    {
        parent::setUp();
        $this->setupErrorHandler(null);
    }

    public function testRendersPartial(): void
    {
        $this->view->setTemplateSource('<vh:render partial="PartialWithoutError" />');
        $this->assertEquals('No error here', $this->view->render());
    }

    public function testRendersSection(): void
    {
        $this->view->setTemplateSource('
            <f:section name="foo">section content</f:section>
            Here comes the section: <vh:render section="foo" />
        ');
        $content = trim($this->view->render());
        $this->assertEquals('Here comes the section: section content', $content);
    }

    public function testThrowsExceptionIfEnabled(): void
    {
        $this->setupErrorHandler(true);
        $this->view->setTemplateSource('<vh:render partial="PartialWithError" />');

        $this->expectException(ViewHelper\Exception::class);
        $this->view->render();
    }

    public function testRendersNiceErrorMessageIfDisabled(): void
    {
        $this->setupErrorHandler(false);
        $this->view->setTemplateSource('
            This text outside of the partial is rendered normally
            <vh:render partial="PartialWithError" />
        ');

        $content = $this->view->render();
        $this->assertContains('This text outside of the partial is rendered normally', $content);
        $this->assertContains('Error rendering partial PartialWithError', $content);
    }

    public function testSavesErrorInformationUsingThrowableStorageIfDisabled(): void
    {
        $storage = $this->setupErrorHandler(false);
        $this->assertCount(0, $storage->loggedThrowables);

        $this->view->setTemplateSource('
            This text outside of the partial is rendered normally
            <vh:render partial="PartialWithError" />
        ');
        $this->view->render();

        $this->assertCount(1, $storage->loggedThrowables);
    }

    public function testThrowsExceptionIfNotConfiguredAndInDevelopmentMode(): void
    {
        $this->setupErrorHandler(null, 'Development');
        $this->view->setTemplateSource('<vh:render partial="PartialWithError" />');

        $this->expectException(ViewHelper\Exception::class);
        $this->view->render();
    }

    public function testFailsNicelyIfNotConfiguredAndInProductionMode(): void
    {
        $this->setupErrorHandler(null, 'Production');
        $this->view->setTemplateSource('
            This text outside of the partial is rendered normally
            <vh:render partial="PartialWithError" />
        ');

        $content = $this->view->render();
        $this->assertContains('This text outside of the partial is rendered normally', $content);
        $this->assertContains('Error rendering partial PartialWithError', $content);
    }

    private function setupErrorHandler(?bool $throwOnPartialError, string $context = 'Testing'): ThrowableStorageMock
    {
        $environment = new Environment(new ApplicationContext($context));
        $storage = new ThrowableStorageMock();

        $objects = $this->objectManager->getAllObjectConfigurations();
        $objects[ViewErrorHandler::class] = [ 's' => ObjectConfiguration::SCOPE_SINGLETON ];
        $this->objectManager->setObjects($objects);

        $handler = $this->objectManager->get(ViewErrorHandler::class);
        $this->inject($handler, 'throwOnPartialError', $throwOnPartialError);
        $this->inject($handler, 'environment', $environment);
        $this->inject($handler, 'exceptionLogger', $storage);
        $this->objectManager->setInstance(ViewErrorHandler::class, $handler);

        return $storage;
    }
}