<?php

namespace App\Tests\EventSubscriber;

use App\EventSubscriber\MessengerFailureSubscriber;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;

class MessengerFailureSubscriberTest extends TestCase
{
    public function testGetSubscribedEvents(): void
    {
        $events = MessengerFailureSubscriber::getSubscribedEvents();

        self::assertArrayHasKey(WorkerMessageFailedEvent::class, $events);
        self::assertSame('onMessageFailed', $events[WorkerMessageFailedEvent::class]);
    }

    public function testOnMessageFailedPersistsLogAndSendsEmail(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $mailer = $this->createMock(MailerInterface::class);

        $em->expects(self::once())->method('persist');
        $em->expects(self::once())->method('flush');
        $mailer->expects(self::once())->method('send');

        $subscriber = new MessengerFailureSubscriber($em, $mailer, 'test@example.com');

        $message = new \stdClass();
        $envelope = new Envelope($message);
        $exception = new \RuntimeException('Test failure');

        $event = new WorkerMessageFailedEvent($envelope, 'async', $exception);

        $subscriber->onMessageFailed($event);
    }

    public function testOnMessageFailedWithRedeliveryStamp(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $mailer = $this->createMock(MailerInterface::class);

        $em->expects(self::once())->method('persist');
        $em->expects(self::once())->method('flush');
        $mailer->expects(self::once())->method('send');

        $subscriber = new MessengerFailureSubscriber($em, $mailer, 'test@example.com');

        $message = new \stdClass();
        $envelope = new Envelope($message, [new RedeliveryStamp(3)]);
        $exception = new \RuntimeException('Retry failure');

        $event = new WorkerMessageFailedEvent($envelope, 'async', $exception);

        $subscriber->onMessageFailed($event);
    }

    public function testOnMessageFailedWithNonSerializableMessage(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $mailer = $this->createMock(MailerInterface::class);

        $em->expects(self::once())->method('persist');
        $em->expects(self::once())->method('flush');
        $mailer->expects(self::once())->method('send');

        $subscriber = new MessengerFailureSubscriber($em, $mailer, 'test@example.com');

        $message = new class {
            public function __serialize(): array
            {
                throw new \RuntimeException('not serializable');
            }
        };
        $envelope = new Envelope($message);
        $exception = new \RuntimeException('Test failure');

        $event = new WorkerMessageFailedEvent($envelope, 'async', $exception);

        $subscriber->onMessageFailed($event);
    }

    public function testOnMessageFailedHandlesMailerException(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $mailer = $this->createMock(MailerInterface::class);

        $em->expects(self::once())->method('persist');
        $em->expects(self::once())->method('flush');
        $mailer->method('send')->willThrowException(new \RuntimeException('mail failed'));

        $subscriber = new MessengerFailureSubscriber($em, $mailer, 'test@example.com');

        $message = new \stdClass();
        $envelope = new Envelope($message);
        $exception = new \RuntimeException('Test failure');
        $event = new WorkerMessageFailedEvent($envelope, 'async', $exception);

        @$subscriber->onMessageFailed($event);

        self::assertTrue(true);
    }
}
