<?php

declare(strict_types=1);

namespace App\Tests\Unit\Shared\Infrastructure\Messenger;

use App\Shared\Infrastructure\Messenger\DeadLetterAlertHandler;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use RuntimeException;
use stdClass;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Mime\Email;
use Twig\Environment;

final class DeadLetterAlertHandlerTest extends TestCase
{
    #[Test]
    public function itSendsAlertEmailWhenMessageWillNotRetry(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $logger = $this->createMock(LoggerInterface::class);

        $twig->expects($this->once())
            ->method('render')
            ->with('emails/dead_letter_alert.html.twig', $this->callback(
                static fn (array $params): bool => $params['eventType'] === stdClass::class
                    && $params['retryCount'] === 7
                    && $params['lastError'] === 'SMTP timeout'
                    && $params['transportName'] === 'async',
            ))
            ->willReturn('<html>alert</html>');

        $mailer->expects($this->once())
            ->method('send')
            ->with($this->callback(
                static fn (Email $email): bool => $email->getTo()[0]->getAddress() === 'admin@classeo.fr'
                    && str_contains($email->getSubject() ?? '', 'dead-letter'),
            ));

        $handler = new DeadLetterAlertHandler($mailer, $twig, $logger, 'admin@classeo.fr');

        $envelope = new Envelope(new stdClass(), [new RedeliveryStamp(7)]);
        $event = new WorkerMessageFailedEvent($envelope, 'async', new RuntimeException('SMTP timeout'));

        $handler($event);
    }

    #[Test]
    public function itDoesNotSendAlertWhenMessageWillRetry(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $logger = $this->createMock(LoggerInterface::class);

        $mailer->expects($this->never())->method('send');
        $twig->expects($this->never())->method('render');

        $handler = new DeadLetterAlertHandler($mailer, $twig, $logger, 'admin@classeo.fr');

        $envelope = new Envelope(new stdClass(), [new RedeliveryStamp(1)]);
        $event = new WorkerMessageFailedEvent($envelope, 'async', new RuntimeException('SMTP timeout'));
        $event->setForRetry();

        $handler($event);
    }

    #[Test]
    public function itLogsErrorWhenMailerFailsInsteadOfThrowing(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $logger = $this->createMock(LoggerInterface::class);

        $twig->method('render')->willReturn('<html>alert</html>');
        $mailer->method('send')->willThrowException(new RuntimeException('SMTP connection refused'));

        $logger->expects($this->once())
            ->method('error')
            ->with('Failed to send dead-letter alert email.', $this->callback(
                static fn (array $context): bool => $context['error'] === 'SMTP connection refused'
                    && $context['messageType'] === stdClass::class,
            ));

        $handler = new DeadLetterAlertHandler($mailer, $twig, $logger, 'admin@classeo.fr');

        $envelope = new Envelope(new stdClass(), [new RedeliveryStamp(7)]);
        $event = new WorkerMessageFailedEvent($envelope, 'async', new RuntimeException('Original error'));

        // Should NOT throw - the mailer failure is caught and logged
        $handler($event);
    }

    #[Test]
    public function itTruncatesLongErrorMessages(): void
    {
        $mailer = $this->createMock(MailerInterface::class);
        $twig = $this->createMock(Environment::class);
        $logger = $this->createMock(LoggerInterface::class);

        $longError = str_repeat('x', 1000);

        $twig->expects($this->once())
            ->method('render')
            ->with('emails/dead_letter_alert.html.twig', $this->callback(
                static fn (array $params): bool => mb_strlen($params['lastError']) <= 500,
            ))
            ->willReturn('<html>alert</html>');

        $mailer->method('send');

        $handler = new DeadLetterAlertHandler($mailer, $twig, $logger, 'admin@classeo.fr');

        $envelope = new Envelope(new stdClass(), [new RedeliveryStamp(7)]);
        $event = new WorkerMessageFailedEvent($envelope, 'async', new RuntimeException($longError));

        $handler($event);
    }
}
