<?php

namespace App\Tests\Command;

use App\Command\CheckNddCommand;
use App\Entity\Customer;
use App\Entity\Domain;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Twig\Environment;

class CheckNddCommandTest extends TestCase
{
    private EntityManagerInterface $em;
    private MailerService $mailer;
    private Environment $twig;
    private LoggerInterface $logger;

    protected function setUp(): void
    {
        $this->em = $this->createStub(EntityManagerInterface::class);
        $this->mailer = $this->createStub(MailerService::class);
        $this->twig = $this->createStub(Environment::class);
        $this->logger = $this->createStub(LoggerInterface::class);
    }

    private function makeCommand(): CheckNddCommand
    {
        return new CheckNddCommand(
            $this->em,
            $this->mailer,
            $this->twig,
            $this->logger,
        );
    }

    private function execute(?CheckNddCommand $command = null): CommandTester
    {
        $tester = new CommandTester($command ?? $this->makeCommand());
        $tester->execute([]);

        return $tester;
    }

    /**
     * Sets up the QueryBuilder chain on $this->em to return the given domains.
     */
    private function mockQueryReturning(array $domains): void
    {
        $query = $this->createStub(Query::class);
        $query->method('getResult')->willReturn($domains);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('orderBy')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);

        $this->em->method('createQueryBuilder')->willReturn($qb);
    }

    private function makeDomain(string $fqdn, \DateTimeImmutable $expiredAt, ?string $email = 'client@example.com'): Domain
    {
        $customer = $this->createStub(Customer::class);
        $customer->method('getFullName')->willReturn('John Doe');
        $customer->method('getEmail')->willReturn($email);

        $domain = $this->createStub(Domain::class);
        $domain->method('getFqdn')->willReturn($fqdn);
        $domain->method('getRegistrar')->willReturn('OVH');
        $domain->method('getCustomer')->willReturn($customer);
        $domain->method('getExpiredAt')->willReturn($expiredAt);

        return $domain;
    }

    // ------------------------------------------------------------------
    // No expiring domains → SUCCESS + email sent with empty subject line
    // ------------------------------------------------------------------

    public function testNoExpiringDomains(): void
    {
        $this->mockQueryReturning([]);
        $this->twig->method('render')->willReturn('<html>empty</html>');

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects($this->once())
            ->method('sendEmail')
            ->with(
                $this->anything(),
                $this->stringContains('Aucune expiration'),
                $this->anything(),
            );
        $this->mailer = $mailer;

        $tester = $this->execute($this->makeCommand());

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Aucun nom de domaine en expiration', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // Multiple domains: some not yet expired (positive daysLeft),
    // some already expired (negative daysLeft) → SUCCESS + count in subject
    // ------------------------------------------------------------------

    public function testMultipleDomainsWithMixedExpiry(): void
    {
        $now = new \DateTimeImmutable();
        $soonExpiring = $this->makeDomain('example.fr', $now->modify('+10 days'));
        $alreadyExpired = $this->makeDomain('expired.fr', $now->modify('-5 days'));

        $this->mockQueryReturning([$soonExpiring, $alreadyExpired]);
        $this->twig->method('render')->willReturn('<html>domains</html>');

        $mailer = $this->createMock(MailerService::class);
        $mailer->expects($this->once())
            ->method('sendEmail')
            ->with(
                $this->anything(),
                $this->stringContains('2 expiration'),
                $this->anything(),
            );
        $this->mailer = $mailer;

        $tester = $this->execute($this->makeCommand());

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('2 domaine(s) en expiration', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // Email send throws → FAILURE + error logged
    // ------------------------------------------------------------------

    public function testEmailSendThrowsReturnsFailure(): void
    {
        $this->mockQueryReturning([]);
        $this->twig->method('render')->willReturn('<html>ok</html>');

        $mailer = $this->createStub(MailerService::class);
        $mailer->method('sendEmail')->willThrowException(new \RuntimeException('SMTP down'));
        $this->mailer = $mailer;

        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->once())
            ->method('error')
            ->with($this->stringContains('SMTP down'));
        $this->logger = $logger;

        $tester = $this->execute($this->makeCommand());

        $this->assertSame(1, $tester->getStatusCode());
        $this->assertStringContainsString('Erreur envoi mail', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // Domain with null customer email → covers `$customer->getEmail() ?? '-'`
    // ------------------------------------------------------------------

    public function testDomainWithNullCustomerEmail(): void
    {
        $now = new \DateTimeImmutable();
        $domain = $this->makeDomain('null-email.fr', $now->modify('+20 days'), null);

        $this->mockQueryReturning([$domain]);
        $this->twig->method('render')->willReturn('<html>one</html>');

        $mailer = $this->createStub(MailerService::class);
        $this->mailer = $mailer;

        $tester = $this->execute($this->makeCommand());

        $this->assertSame(0, $tester->getStatusCode());
        // The io->text line uses `?? '-'` — make sure '-' appears in output
        $this->assertStringContainsString('-', $tester->getDisplay());
    }
}
