<?php

namespace App\Tests\Command;

use App\Command\MonitorMessengerCommand;
use App\Entity\MessengerLog;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

class MonitorMessengerCommandTest extends TestCase
{
    private EntityManagerInterface $em;
    private MailerService $mailer;
    private CommandTester $tester;

    protected function setUp(): void
    {
        $this->em = $this->createMock(EntityManagerInterface::class);
        $this->mailer = $this->createMock(MailerService::class);

        $command = new MonitorMessengerCommand($this->em, $this->mailer);
        $app = new Application();
        $app->addCommand($command);
        $this->tester = new CommandTester($app->find('app:monitor:messenger'));
    }

    public function testNoFailedMessages(): void
    {
        $repo = $this->createMock(EntityRepository::class);
        $repo->method('findBy')->willReturn([]);

        $this->em->method('getRepository')
            ->with(MessengerLog::class)
            ->willReturn($repo);

        $this->mailer->expects(self::never())->method('sendEmail');

        $this->tester->execute([]);

        self::assertSame(0, $this->tester->getStatusCode());
        self::assertStringContainsString('No failed messages', $this->tester->getDisplay());
    }

    public function testFailedMessagesSendsEmail(): void
    {
        $log = $this->createMock(MessengerLog::class);
        $log->method('getMessageClass')->willReturn('App\\Message\\TestMessage');
        $log->method('getCreatedAt')->willReturn(new \DateTimeImmutable('2026-03-23 10:00'));
        $log->method('getErrorMessage')->willReturn('Something went wrong');

        $repo = $this->createMock(EntityRepository::class);
        $repo->method('findBy')->willReturn([$log]);

        $this->em->method('getRepository')
            ->with(MessengerLog::class)
            ->willReturn($repo);

        $this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
        $this->mailer->expects(self::once())->method('sendEmail')->with(
            'admin@test.com',
            '[E-Ticket] 1 message(s) Messenger en echec',
            $this->callback(fn (string $html) => str_contains($html, 'TestMessage') && str_contains($html, 'Something went wrong')),
            null,
            null,
            false,
        );

        $this->tester->execute([]);

        self::assertSame(0, $this->tester->getStatusCode());
        self::assertStringContainsString('1 failed message(s)', $this->tester->getDisplay());
        self::assertStringContainsString('admin@test.com', $this->tester->getDisplay());
    }

    public function testFailedMessageWithNullError(): void
    {
        $log = $this->createMock(MessengerLog::class);
        $log->method('getMessageClass')->willReturn('App\\Message\\Other');
        $log->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
        $log->method('getErrorMessage')->willReturn(null);

        $repo = $this->createMock(EntityRepository::class);
        $repo->method('findBy')->willReturn([$log]);

        $this->em->method('getRepository')->willReturn($repo);

        $this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
        $this->mailer->expects(self::once())->method('sendEmail');

        $this->tester->execute([]);

        self::assertSame(0, $this->tester->getStatusCode());
    }

    public function testMultipleFailedMessages(): void
    {
        $logs = [];
        for ($i = 0; $i < 3; ++$i) {
            $log = $this->createMock(MessengerLog::class);
            $log->method('getMessageClass')->willReturn('Msg'.$i);
            $log->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
            $log->method('getErrorMessage')->willReturn('Error '.$i);
            $logs[] = $log;
        }

        $repo = $this->createMock(EntityRepository::class);
        $repo->method('findBy')->willReturn($logs);

        $this->em->method('getRepository')->willReturn($repo);

        $this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
        $this->mailer->expects(self::once())->method('sendEmail')->with(
            'admin@test.com',
            '[E-Ticket] 3 message(s) Messenger en echec',
            $this->anything(),
            null,
            null,
            false,
        );

        $this->tester->execute([]);

        self::assertStringContainsString('3 failed message(s)', $this->tester->getDisplay());
    }
}
