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

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectToPdfConverter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowAttachment;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Messenger\PostSendExternalMessage;
use Chill\MainBundle\Workflow\Messenger\PostSendExternalMessageHandler;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;

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

    public function testSendMessageHappyScenario(): void
    {
        $entityWorkflow = $this->buildEntityWorkflow();

        // Prepare attachments (2 attachments)
        $attachmentStoredObject1 = new StoredObject();
        $attachmentStoredObject2 = new StoredObject();
        new EntityWorkflowAttachment('generic_doc', ['id' => 1], $entityWorkflow, $attachmentStoredObject1);
        new EntityWorkflowAttachment('generic_doc', ['id' => 2], $entityWorkflow, $attachmentStoredObject2);

        // Prepare transition DTO and sends
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestineeEmails = ['external@example.com'];
        $dto->futureDestineeThirdParties = [(new ThirdParty())->setEmail('3party@example.com')];
        $entityWorkflow->setStep('send_external', $dto, 'to_send_external', new \DateTimeImmutable(), new User());

        // Repository returns our workflow
        $repository = $this->prophesize(EntityWorkflowRepository::class);
        $repository->find(1)->willReturn($entityWorkflow);

        // Mailer must send to both recipients
        $mailer = $this->prophesize(MailerInterface::class);
        $mailer->send(Argument::that($this->buildCheckAddressCallback('3party@example.com')))->shouldBeCalledOnce();
        $mailer->send(Argument::that($this->buildCheckAddressCallback('external@example.com')))->shouldBeCalledOnce();

        // Workflow manager and handler
        $workflowHandler = $this->prophesize(EntityWorkflowHandlerInterface::class);
        $workflowHandler->getEntityTitle($entityWorkflow, Argument::any())->willReturn('title');
        $workflowManager = $this->prophesize(EntityWorkflowManager::class);
        $workflowManager->getHandler($entityWorkflow)->willReturn($workflowHandler->reveal());

        // Associated stored object for the workflow
        $associatedStoredObject = new StoredObject();
        $workflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn($associatedStoredObject);

        // Converter should be called for each attachment and the associated stored object
        $converter = $this->prophesize(StoredObjectToPdfConverter::class);
        $converter->addConvertedVersion($attachmentStoredObject1, 'fr')->shouldBeCalledOnce();
        $converter->addConvertedVersion($attachmentStoredObject2, 'fr')->shouldBeCalledOnce();
        $converter->addConvertedVersion($associatedStoredObject, 'fr')->shouldBeCalledOnce();

        // Logger (not used in happy path, but required by handler)
        $logger = $this->prophesize(LoggerInterface::class);

        $handler = new PostSendExternalMessageHandler(
            $repository->reveal(),
            $mailer->reveal(),
            $workflowManager->reveal(),
            $converter->reveal(),
            $logger->reveal(),
        );

        $handler(new PostSendExternalMessage(1, 'fr'));

        // prophecy should do the check at the end of this test
    }

    private function buildCheckAddressCallback(string $emailToCheck): callable
    {
        return fn (TemplatedEmail $email): bool => in_array($emailToCheck, array_map(fn (Address $addr) => $addr->getAddress(), $email->getTo()), true);
    }

    private function buildEntityWorkflow(): EntityWorkflow
    {
        $entityWorkflow = new EntityWorkflow();
        $reflection = new \ReflectionClass($entityWorkflow);
        $idProperty = $reflection->getProperty('id');
        $idProperty->setValue($entityWorkflow, 1);

        return $entityWorkflow;
    }
}
