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

use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * Serialize a RequestPdfSignMessage, for external consumer.
 */
final readonly class RequestPdfSignMessageSerializer implements SerializerInterface
{
    public function __construct(
        private NormalizerInterface $normalizer,
        private DenormalizerInterface $denormalizer,
    ) {}

    public function decode(array $encodedEnvelope): Envelope
    {
        $body = $encodedEnvelope['body'];
        $headers = $encodedEnvelope['headers'];

        if (RequestPdfSignMessage::class !== ($headers['Message'] ?? null)) {
            throw new MessageDecodingFailedException('serializer does not support this message');
        }

        $data = json_decode((string) $body, true);

        $zoneSignature = $this->denormalizer->denormalize($data['signatureZone'], PDFSignatureZone::class, 'json', [
            AbstractNormalizer::GROUPS => ['write'],
        ]);

        $content = base64_decode((string) $data['content'], true);

        if (false === $content) {
            throw new MessageDecodingFailedException('the content could not be converted from base64 encoding');
        }

        $message = new RequestPdfSignMessage(
            $data['signatureId'],
            $zoneSignature,
            $data['signatureZoneIndex'],
            $data['reason'],
            $data['signerText'],
            $content,
        );

        // in case of redelivery, unserialize any stamps
        $stamps = [];
        if (isset($headers['stamps'])) {
            $stamps = unserialize($headers['stamps']);
        }

        return new Envelope($message, $stamps);
    }

    public function encode(Envelope $envelope): array
    {
        $message = $envelope->getMessage();

        if (!$message instanceof RequestPdfSignMessage) {
            throw new MessageDecodingFailedException('Message is not a RequestPdfSignMessage');
        }

        $data = [
            'signatureId' => $message->signatureId,
            'signatureZoneIndex' => $message->signatureZoneIndex,
            'signatureZone' => $this->normalizer->normalize($message->PDFSignatureZone, 'json', [AbstractNormalizer::GROUPS => ['read']]),
            'reason' => $message->reason,
            'signerText' => $message->signerText,
            'content' => base64_encode($message->content),
        ];

        $allStamps = [];
        foreach ($envelope->all() as $stamp) {
            if ($stamp instanceof NonSendableStampInterface) {
                continue;
            }
            $allStamps = [...$allStamps, ...$stamp];
        }

        return [
            'body' => json_encode($data, JSON_THROW_ON_ERROR, 512),
            'headers' => [
                'stamps' => serialize($allStamps),
                'Message' => RequestPdfSignMessage::class,
            ],
        ];
    }
}
