<?php

namespace App\Tests\Command;

use App\Command\MailCommand;
use App\Entity\Contrats;
use App\Entity\Customer;
use App\Entity\Devis;
use App\Service\Mailer\Mailer;
use App\Service\Signature\Client;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\HttpKernel\KernelInterface;

#[AllowMockObjectsWithoutExpectations]
class MailCommandTest extends TestCase
{
    private MockObject&KernelInterface $kernel;
    private MockObject&Client $client;
    private MockObject&Mailer $mailer;
    private MockObject&EntityManagerInterface $entityManager;
    private MockObject&EntityRepository $devisRepository;
    private MockObject&EntityRepository $contratsRepository;

    protected function setUp(): void
    {
        $this->kernel = $this->createMock(KernelInterface::class);
        $this->client = $this->createMock(Client::class);
        $this->mailer = $this->createMock(Mailer::class);
        $this->entityManager = $this->createMock(EntityManagerInterface::class);
        $this->devisRepository = $this->createMock(EntityRepository::class);
        $this->contratsRepository = $this->createMock(EntityRepository::class);

        // Setup repository mocks
        $this->entityManager->method('getRepository')
            ->willReturnMap([
                [Devis::class, $this->devisRepository],
                [Contrats::class, $this->contratsRepository],
            ]);
    }

    public function testExecuteProcessUnsignedDevisDeletion()
    {
        // Setup Devis to be deleted (> 3 days)
        $devis = $this->createMock(Devis::class);
        $devis->method('getCreateA')->willReturn(new \DateTimeImmutable('-4 days'));
        $devis->method('getNum')->willReturn('D-123');
        // Ensure getDevisLines/Options return iterables
        $devis->method('getDevisLines')->willReturn(new ArrayCollection([]));
        $devis->method('getDevisOptions')->willReturn(new ArrayCollection([]));

        $this->devisRepository->expects($this->once())
            ->method('findBy')
            ->with(['state' => 'wait-sign'])
            ->willReturn([$devis]);

        // Expect removal
        $this->entityManager->expects($this->once())->method('remove')->with($devis);
        
        // Mock other calls to return empty to isolate this test case
        $this->contratsRepository->method('findBy')->willReturn([]);
        
        // Mock QueryBuilder for sendEventReminders and Satisfaction which use createQueryBuilder
        $qb = $this->createMock(QueryBuilder::class);
        $query = $this->createMock(Query::class);
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);
        $query->method('getResult')->willReturn([]);
        
        $this->contratsRepository->method('createQueryBuilder')->willReturn($qb);

        // Execute
        $command = new MailCommand(
            $this->kernel,
            $this->client,
            $this->mailer,
            $this->entityManager
        );
        $application = new Application();
        $application->addCommand($command);
        $commandTester = new CommandTester($application->find('app:mail'));

        $commandTester->execute([]);

        $output = $commandTester->getDisplay();
        $this->assertStringContainsString('Suppression du devis N°D-123', $output);
    }

    public function testExecuteProcessUnsignedDevisReminder()
    {
        // Setup Devis to be reminded (1 day)
        $customer = $this->createMock(Customer::class);
        $customer->method('getEmail')->willReturn('test@example.com');
        $customer->method('getName')->willReturn('John');
        
        $devis = $this->createMock(Devis::class);
        $devis->method('getCreateA')->willReturn(new \DateTimeImmutable('-1 day -2 hours'));
        $devis->method('getNum')->willReturn('D-456');
        $devis->method('getCustomer')->willReturn($customer);
        $devis->method('getSignatureId')->willReturn('sign_123');

        $this->devisRepository->expects($this->once())
            ->method('findBy')
            ->with(['state' => 'wait-sign'])
            ->willReturn([$devis]);

        // Expect Mailer call
        $this->mailer->expects($this->once())
            ->method('send')
            ->with('test@example.com', 'John', $this->stringContains('Devis N°D-456'));
        
        // Mock other calls to return empty
        $this->contratsRepository->method('findBy')->willReturn([]);
        
        // Mock QueryBuilder
        $qb = $this->createMock(QueryBuilder::class);
        $query = $this->createMock(Query::class);
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);
        $query->method('getResult')->willReturn([]);
        
        $this->contratsRepository->method('createQueryBuilder')->willReturn($qb);

        // Execute
        $command = new MailCommand(
            $this->kernel,
            $this->client,
            $this->mailer,
            $this->entityManager
        );
        $application = new Application();
        $application->addCommand($command);
        $commandTester = new CommandTester($application->find('app:mail'));

        $commandTester->execute([]);

        $output = $commandTester->getDisplay();
        $this->assertStringContainsString('Relance envoyée pour le devis : D-456', $output);
    }
}
