<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Tests\Message;

use Ferienpass\CoreBundle\Message\RemindAttendance;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;

final class RemindAttendanceSerializationTest extends TestCase
{
    public function testRoundTripRestoresTheAttendanceIds(): void
    {
        $msg = self::roundTrip(new RemindAttendance('55', '66'));

        self::assertSame(['55', '66'], $msg->getAttendanceIds());
    }

    public function testRoundTripOfAnEmptyMessage(): void
    {
        $msg = self::roundTrip(new RemindAttendance());

        self::assertSame([], $msg->getAttendanceIds());
    }

    public function testRoundTripThroughTheTransportSerializer(): void
    {
        // The async transport is a Doctrine transport without an explicit
        // serializer, i.e. plain PHP (un)serialization of the envelope.
        $serializer = new PhpSerializer();
        $encoded = $serializer->encode(new Envelope(new RemindAttendance('55', '66')));

        $msg = $serializer->decode($encoded)->getMessage();

        self::assertInstanceOf(RemindAttendance::class, $msg);
        self::assertSame(['55', '66'], $msg->getAttendanceIds());
    }

    public function testLegacyPayloadIsPromotedToTheList(): void
    {
        $msg = unserialize(self::legacyPayload('55'));

        self::assertInstanceOf(RemindAttendance::class, $msg);
        self::assertSame(['55'], $msg->getAttendanceIds());
    }

    public function testLegacyPayloadWithAnEmptyIdYieldsNoAttendances(): void
    {
        $msg = unserialize(self::legacyPayload(''));

        self::assertInstanceOf(RemindAttendance::class, $msg);
        self::assertSame([], $msg->getAttendanceIds());
    }

    public function testPayloadWithoutAnyPropertyYieldsNoAttendances(): void
    {
        $msg = unserialize(\sprintf('O:%d:"%s":0:{}', \strlen(RemindAttendance::class), RemindAttendance::class));

        self::assertInstanceOf(RemindAttendance::class, $msg);
        self::assertSame([], $msg->getAttendanceIds());
    }

    private static function roundTrip(RemindAttendance $msg): RemindAttendance
    {
        $unserialized = unserialize(serialize($msg));

        self::assertInstanceOf(RemindAttendance::class, $unserialized);

        return $unserialized;
    }

    /**
     * Rebuilds what the async transport stored before reminders were grouped per
     * recipient: a single private readonly `attendanceId` string.
     */
    private static function legacyPayload(string $attendanceId): string
    {
        $property = "\0".RemindAttendance::class."\0attendanceId";

        return \sprintf(
            'O:%d:"%s":1:{s:%d:"%s";s:%d:"%s";}',
            \strlen(RemindAttendance::class),
            RemindAttendance::class,
            \strlen($property),
            $property,
            \strlen($attendanceId),
            $attendanceId,
        );
    }
}
