<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocGeneratorBundle\tests\Service\Context\Generator;

use Chill\DocGeneratorBundle\Context\ContextManagerInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\Service\Generator\Generator;
use Chill\DocGeneratorBundle\Service\Generator\ObjectReadyException;
use Chill\DocGeneratorBundle\Service\Generator\RelatedEntityNotFoundException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;

/**
 * @internal
 *
 * @coversNothing
 */
class GeneratorTest extends TestCase
{
    use ProphecyTrait;

    public function testSuccessfulGeneration(): void
    {
        $templateStoredObject = new StoredObject();
        $templateStoredObject->registerVersion(type: 'application/test');
        $template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
        $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
        $reflection = new \ReflectionClass($destinationStoredObject);
        $reflection->getProperty('id')->setValue($destinationStoredObject, 1);
        $entity = new class () {};
        $data = [];

        $context = $this->prophesize(DocGeneratorContextInterface::class);
        $context->getData($template, $entity, Argument::type('array'))->willReturn($data);
        $context->getName()->willReturn('dummy_context');
        $context->getEntityClass()->willReturn('DummyClass');
        $context = $context->reveal();

        $contextManagerInterface = $this->prophesize(ContextManagerInterface::class);
        $contextManagerInterface->getContextByDocGeneratorTemplate($template)
            ->willReturn($context);

        $driver = $this->prophesize(DriverInterface::class);
        $driver->generateFromString('template', 'application/test', $data, Argument::any())
            ->willReturn('generated');

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->find(StoredObject::class, 1)
            ->willReturn($destinationStoredObject);
        $entityManager->find('DummyClass', Argument::type('int'))
            ->willReturn($entity);
        $entityManager->clear()->shouldBeCalled();
        $entityManager->flush()->shouldNotBeCalled();

        $managerRegistry = $this->prophesize(ManagerRegistry::class);
        $managerRegistry->getManagerForClass('DummyClass')->willReturn($entityManager->reveal());
        $managerRegistry->getManagerForClass(StoredObject::class)->willReturn($entityManager->reveal());

        $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
        $storedObjectManager->read($templateStoredObject)->willReturn('template');
        $storedObjectManager->write($destinationStoredObject, 'generated', 'application/test')
            ->will(function ($args): StoredObjectVersion {
                /** @var StoredObject $storedObject */
                $storedObject = $args[0];

                return $storedObject->registerVersion(type: $args[2]);
            })
            ->shouldBeCalled();

        $generator = new Generator(
            $contextManagerInterface->reveal(),
            $driver->reveal(),
            $managerRegistry->reveal(),
            new NullLogger(),
            $storedObjectManager->reveal()
        );

        $generator->generateDocFromTemplate(
            $template,
            1,
            [],
            $destinationStoredObject,
            new User()
        );
    }

    public function testPreventRegenerateDocument(): void
    {
        $this->expectException(ObjectReadyException::class);

        $generator = new Generator(
            $this->prophesize(ContextManagerInterface::class)->reveal(),
            $this->prophesize(DriverInterface::class)->reveal(),
            $this->prophesize(ManagerRegistry::class)->reveal(),
            new NullLogger(),
            $this->prophesize(StoredObjectManagerInterface::class)->reveal()
        );

        $templateStoredObject = new StoredObject();
        $templateStoredObject->registerVersion(type: 'application/test');
        $template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
        $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_READY);

        $generator->generateDocFromTemplate(
            $template,
            1,
            [],
            $destinationStoredObject,
            new User()
        );
    }

    public function testRelatedEntityNotFound(): void
    {
        $this->expectException(RelatedEntityNotFoundException::class);

        $templateStoredObject = new StoredObject();
        $templateStoredObject->registerVersion(type: 'application/test');
        $template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
        $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
        $reflection = new \ReflectionClass($destinationStoredObject);
        $reflection->getProperty('id')->setValue($destinationStoredObject, 1);

        $context = $this->prophesize(DocGeneratorContextInterface::class);
        $context->getName()->willReturn('dummy_context');
        $context->getEntityClass()->willReturn('DummyClass');
        $context = $context->reveal();

        $contextManagerInterface = $this->prophesize(ContextManagerInterface::class);
        $contextManagerInterface->getContextByDocGeneratorTemplate($template)
            ->willReturn($context);

        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->find(Argument::type('string'), Argument::type('int'))
            ->willReturn(null);

        $managerRegistry = $this->prophesize(ManagerRegistry::class);
        $managerRegistry->getManagerForClass('DummyClass')->willReturn($entityManager->reveal());
        $managerRegistry->getManagerForClass(StoredObject::class)->willReturn($entityManager->reveal());

        $generator = new Generator(
            $contextManagerInterface->reveal(),
            $this->prophesize(DriverInterface::class)->reveal(),
            $managerRegistry->reveal(),
            new NullLogger(),
            $this->prophesize(StoredObjectManagerInterface::class)->reveal()
        );

        $generator->generateDocFromTemplate(
            $template,
            1,
            [],
            $destinationStoredObject,
            new User()
        );
    }
}
