<?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\Messenger;

use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationHandler;
use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

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

    public function testGenerationHappyScenario(): void
    {
        // Create entities
        $template = new DocGeneratorTemplate();
        $this->setPrivateProperty($template, 'id', 1);

        $storedObject = new StoredObject();
        $this->setPrivateProperty($storedObject, 'id', 2);

        $creator = new User();
        $creator->setEmail('test@example.com');
        $this->setPrivateProperty($creator, 'id', 3);

        $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
        $docGeneratorTemplateRepository->find(1)->willReturn($template);

        $storedObjectRepository = $this->prophesize(StoredObjectRepositoryInterface::class);
        $storedObjectRepository->find(2)->willReturn($storedObject);

        $userRepository = $this->prophesize(UserRepositoryInterface::class);
        $userRepository->find(3)->willReturn($creator);

        // Create a mock for the Query object
        $query = $this->prophesize(Query::class);
        $query->setParameter('id', 2)->willReturn($query->reveal());
        $query->execute()->shouldBeCalled();

        // Create a mock for the EntityManager
        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $entityManager->createQuery(Argument::containingString('UPDATE'))->willReturn($query->reveal());
        $entityManager->flush()->shouldBeCalled();

        $generator = $this->prophesize(GeneratorInterface::class);
        $generator->generateDocFromTemplate(
            $template,
            123, // entityId
            ['key' => 'value'], // contextGenerationData
            $storedObject,
            $creator
        )
            ->willReturn($storedObject)->shouldBeCalled();

        $logger = new NullLogger();

        $mailer = $this->prophesize(MailerInterface::class);

        $translator = $this->prophesize(TranslatorInterface::class);

        $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);

        // Create handler
        $handler = new RequestGenerationHandler(
            $docGeneratorTemplateRepository->reveal(),
            $entityManager->reveal(),
            $generator->reveal(),
            $logger,
            $storedObjectRepository->reveal(),
            $userRepository->reveal(),
            $mailer->reveal(),
            $translator->reveal(),
            $storedObjectManager->reveal()
        );

        // Create message
        $message = new RequestGenerationMessage(
            $creator,
            $template,
            123, // entityId
            $storedObject,
            ['key' => 'value'], // contextGenerationData
            false, // isTest
            null, // sendResultToEmail
            false // dumpOnly
        );

        // Invoke handler
        $handler->__invoke($message);

        // Assertions
        // The assertions are handled by the shouldBeCalled() expectations on the mocks
        $this->assertTrue(true); // Just to have an assertion in the test
    }

    private function setPrivateProperty(object $object, string $propertyName, $value): void
    {
        $reflection = new \ReflectionClass($object);
        $property = $reflection->getProperty($propertyName);
        $property->setAccessible(true);
        $property->setValue($object, $value);
    }
}
