<?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\DocStoreBundle\Tests\Service\Signature\Driver\BaseSigner;

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\PdfSignedMessage;
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\PdfSignedMessageHandler;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureRepository;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\SignatureStepStateChanger;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;

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

    public function testThatObjectIsWrittenInStoredObjectManagerHappyScenario(): void
    {
        // a dummy stored object
        $storedObject = new StoredObject();
        // build the associated EntityWorkflow, with one step with a person signature
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('new_step', $dto, 'new_transition', new \DateTimeImmutable(), new User());
        $step = $entityWorkflow->getCurrentStep();
        $signature = $step->getSignatures()->first();
        $stateChanger = $this->createMock(SignatureStepStateChanger::class);
        $stateChanger->expects(self::once())->method('markSignatureAsSigned')
            ->with($signature, 99);

        $handler = new PdfSignedMessageHandler(
            new NullLogger(),
            $this->buildEntityWorkflowManager($storedObject),
            $this->buildStoredObjectManager($storedObject, $expectedContent = '1234'),
            $this->buildSignatureRepository($signature),
            $this->buildEntityManager(true),
            $stateChanger,
        );

        // we simply call the handler. The mocked StoredObjectManager will check that the "write" method is invoked once
        // with the content "1234"
        $handler(new PdfSignedMessage(10, 99, $expectedContent));
    }

    private function buildSignatureRepository(EntityWorkflowStepSignature $signature): EntityWorkflowStepSignatureRepository
    {
        $entityWorkflowStepSignatureRepository = $this->createMock(EntityWorkflowStepSignatureRepository::class);
        $entityWorkflowStepSignatureRepository->method('find')->with($this->isType('int'))->willReturn($signature);

        return $entityWorkflowStepSignatureRepository;
    }

    private function buildEntityWorkflowManager(?StoredObject $associatedStoredObject): EntityWorkflowManager
    {
        $entityWorkflowManager = $this->createMock(EntityWorkflowManager::class);
        $entityWorkflowManager->method('getAssociatedStoredObject')->willReturn($associatedStoredObject);

        return $entityWorkflowManager;
    }

    private function buildStoredObjectManager(StoredObject $expectedStoredObject, string $expectedContent): StoredObjectManagerInterface
    {
        $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
        $storedObjectManager->expects($this->once())
            ->method('write')
            ->with($this->identicalTo($expectedStoredObject), $expectedContent);

        return $storedObjectManager;
    }

    private function buildEntityManager(bool $willFlush): EntityManagerInterface
    {
        $em = $this->prophesize(EntityManagerInterface::class);
        $clear = $em->clear();
        $wrap = $em->wrapInTransaction(Argument::type('callable'))->will(function ($args) {
            $callable = $args[0];

            return call_user_func($callable);
        });

        if ($willFlush) {
            $clear->shouldBeCalled();
            $wrap->shouldBeCalled();
        }

        return $em->reveal();
    }
}
