<?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\EventListener\Workflow;

use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Enum\OfferTransition;
use Ferienpass\CoreBundle\EventListener\Workflow\OfferRelaunchTransactionListener;
use Ferienpass\CoreBundle\Message\RelaunchOffer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Workflow\Event\EnteredEvent;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\Transition;

class OfferRelaunchTransactionListenerTest extends TestCase
{
    private MessageBusInterface&MockObject $commandBus;

    protected function setUp(): void
    {
        $this->commandBus = $this->createMock(MessageBusInterface::class);
    }

    public function testDispatchesRelaunchOfferWithOfferUuid(): void
    {
        $listener = new OfferRelaunchTransactionListener($this->commandBus);

        $uuid = Uuid::v4();
        $offer = $this->createMock(OfferInterface::class);
        $offer->method('getUuid')->willReturn($uuid);

        $this->commandBus
            ->expects($this->once())
            ->method('dispatch')
            ->with($this->callback(static function (Envelope $envelope) use ($uuid): bool {
                $command = $envelope->getMessage();

                // The handler resolves the offer via Uuid::fromString(getOfferId()),
                // so the command must carry the UUID string — not the integer PK
                // (which previously caused a TypeError / would fail Uuid parsing).
                return $command instanceof RelaunchOffer
                    && (string) $uuid === $command->getOfferId()
                    && 'note to families' === $command->getNotifyText();
            }))
            ->willReturnCallback(static fn ($msg) => new Envelope($msg));

        $listener($this->createEnteredEvent($offer, OfferTransition::RELAUNCH, [
            'uuid' => (string) Uuid::v7(),
            'notifyText' => 'note to families',
        ]));
    }

    public function testDoesNotDispatchForOtherTransition(): void
    {
        $listener = new OfferRelaunchTransactionListener($this->commandBus);

        $offer = $this->createMock(OfferInterface::class);

        $this->commandBus->expects($this->never())->method('dispatch');

        $listener($this->createEnteredEvent($offer, OfferTransition::PUBLISH));
    }

    public function testThrowsRuntimeExceptionForNonOfferSubject(): void
    {
        $listener = new OfferRelaunchTransactionListener($this->commandBus);

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage('Unexpected event subject');

        $listener($this->createEnteredEvent(new \stdClass(), OfferTransition::RELAUNCH));
    }

    private function createEnteredEvent(object $subject, string $transition, array $context = []): EnteredEvent
    {
        return new EnteredEvent($subject, new Marking(), new Transition($transition, 'from', 'to'), null, $context);
    }
}
