<?php

declare(strict_types=1);

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

use App\Shared\Infrastructure\Console\ReviewFailedMessagesCommand;
use Doctrine\DBAL\Connection;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

final class ReviewFailedMessagesCommandTest extends TestCase
{
    private Connection&MockObject $connection;
    private CommandTester $commandTester;

    protected function setUp(): void
    {
        $this->connection = $this->createMock(Connection::class);
        $command = new ReviewFailedMessagesCommand($this->connection);
        $this->commandTester = new CommandTester($command);
    }

    #[Test]
    public function listShowsSuccessWhenNoFailedMessages(): void
    {
        $this->connection->method('fetchAllAssociative')->willReturn([]);

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

        self::assertSame(Command::SUCCESS, $this->commandTester->getStatusCode());
        self::assertStringContainsString('Aucun message en echec', $this->commandTester->getDisplay());
    }

    #[Test]
    public function listDisplaysMessagesTable(): void
    {
        $this->connection->method('fetchAllAssociative')->willReturn([
            ['id' => 1, 'headers' => '{"type":"App\\\\Test\\\\FooEvent"}', 'created_at' => '2026-02-08 10:00:00'],
            ['id' => 2, 'headers' => '{"type":"App\\\\Test\\\\BarEvent"}', 'created_at' => '2026-02-08 11:00:00'],
        ]);

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

        $output = $this->commandTester->getDisplay();
        self::assertSame(Command::SUCCESS, $this->commandTester->getStatusCode());
        self::assertStringContainsString('2 message(s) en echec', $output);
        self::assertStringContainsString('FooEvent', $output);
        self::assertStringContainsString('messenger:failed:retry', $output);
    }

    #[Test]
    public function listRejectsInvalidLimit(): void
    {
        $this->commandTester->execute(['--limit' => '0']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
        self::assertStringContainsString('entier positif', $this->commandTester->getDisplay());
    }

    #[Test]
    public function listRejectsNegativeLimit(): void
    {
        $this->commandTester->execute(['--limit' => '-5']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
    }

    #[Test]
    public function showRequiresIdOption(): void
    {
        $this->commandTester->execute(['action' => 'show']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
        self::assertStringContainsString('--id est requise', $this->commandTester->getDisplay());
    }

    #[Test]
    public function showRejectsNonNumericId(): void
    {
        $this->commandTester->execute(['action' => 'show', '--id' => 'abc']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
        self::assertStringContainsString('entier positif', $this->commandTester->getDisplay());
    }

    #[Test]
    public function showReturnsFailureWhenMessageNotFound(): void
    {
        $this->connection->method('fetchAssociative')->willReturn(false);

        $this->commandTester->execute(['action' => 'show', '--id' => '999']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
        self::assertStringContainsString('introuvable', $this->commandTester->getDisplay());
    }

    #[Test]
    public function showDisplaysMessageDetails(): void
    {
        $this->connection->method('fetchAssociative')->willReturn([
            'id' => 42,
            'headers' => '{"type":"App\\\\Test\\\\FooEvent"}',
            'body' => '{}',
            'queue_name' => 'failed',
            'created_at' => '2026-02-08 10:00:00',
            'delivered_at' => null,
            'available_at' => '2026-02-08 10:00:00',
        ]);

        $this->commandTester->execute(['action' => 'show', '--id' => '42']);

        $output = $this->commandTester->getDisplay();
        self::assertSame(Command::SUCCESS, $this->commandTester->getStatusCode());
        self::assertStringContainsString('Message #42', $output);
        self::assertStringContainsString('FooEvent', $output);
        self::assertStringContainsString('messenger:failed:retry 42', $output);
    }

    #[Test]
    public function invalidActionReturnsFailure(): void
    {
        $this->commandTester->execute(['action' => 'explode']);

        self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
        self::assertStringContainsString('Action inconnue', $this->commandTester->getDisplay());
    }
}
