<?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\Service\Signature\PDFPage;
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessage;
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessageSerializer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;

/**
 * @internal
 *
 * @coversNothing
 */
class RequestPdfSignMessageSerializerTest extends TestCase
{
    public function testEncode(): void
    {
        $serializer = $this->buildSerializer();

        $envelope = new Envelope(
            $request = new RequestPdfSignMessage(
                0,
                new PDFSignatureZone(0, 10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0)),
                0,
                'metadata to add to the signature',
                'Mme Caroline Diallo',
                'abc'
            ),
        );

        $actual = $serializer->encode($envelope);
        $expectedBody = json_encode([
            'signatureId' => $request->signatureId,
            'signatureZoneIndex' => $request->signatureZoneIndex,
            'signatureZone' => ['x' => 10.0],
            'reason' => $request->reason,
            'signerText' => $request->signerText,
            'content' => base64_encode($request->content),
        ]);

        self::assertIsArray($actual);
        self::assertArrayHasKey('body', $actual);
        self::assertArrayHasKey('headers', $actual);
        self::assertEquals($expectedBody, $actual['body']);
    }

    public function testDecode(): void
    {
        $serializer = $this->buildSerializer();

        $request = new RequestPdfSignMessage(
            0,
            new PDFSignatureZone(0, 10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0)),
            0,
            'metadata to add to the signature',
            'Mme Caroline Diallo',
            'abc'
        );

        $bodyAsString = json_encode([
            'signatureId' => $request->signatureId,
            'signatureZoneIndex' => $request->signatureZoneIndex,
            'signatureZone' => ['x' => 10.0],
            'reason' => $request->reason,
            'signerText' => $request->signerText,
            'content' => base64_encode($request->content),
        ], JSON_THROW_ON_ERROR);

        $actual = $serializer->decode([
            'body' => $bodyAsString,
            'headers' => [
                'Message' => RequestPdfSignMessage::class,
            ],
        ]);

        self::assertInstanceOf(RequestPdfSignMessage::class, $actual->getMessage());
        self::assertEquals($request->signatureId, $actual->getMessage()->signatureId);
        self::assertEquals($request->signatureZoneIndex, $actual->getMessage()->signatureZoneIndex);
        self::assertEquals($request->reason, $actual->getMessage()->reason);
        self::assertEquals($request->signerText, $actual->getMessage()->signerText);
        self::assertEquals($request->content, $actual->getMessage()->content);
        self::assertNotNull($actual->getMessage()->PDFSignatureZone);
    }

    private function buildSerializer(): RequestPdfSignMessageSerializer
    {
        $normalizer =
            new class () implements NormalizerInterface {
                public function normalize($object, ?string $format = null, array $context = []): array
                {
                    if (!$object instanceof PDFSignatureZone) {
                        throw new UnexpectedValueException('expected RequestPdfSignMessage');
                    }

                    return [
                        'x' => $object->x,
                    ];
                }

                public function supportsNormalization($data, ?string $format = null): bool
                {
                    return $data instanceof PDFSignatureZone;
                }
            };
        $denormalizer = new class () implements DenormalizerInterface {
            public function denormalize($data, string $type, ?string $format = null, array $context = [])
            {
                return new PDFSignatureZone(0, 10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0));
            }

            public function supportsDenormalization($data, string $type, ?string $format = null)
            {
                return PDFSignatureZone::class === $type;
            }
        };

        $serializer = new Serializer([$normalizer, $denormalizer]);

        return new RequestPdfSignMessageSerializer($serializer, $serializer);
    }
}
