<?php

namespace App\Tests\Command;

use App\Command\ReminderFacturesPrestataireCommand;
use App\Entity\FacturePrestataire;
use App\Entity\Prestataire;
use App\Service\MailerService;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

class ReminderFacturesPrestataireCommandTest extends TestCase
{
    private EntityManagerInterface $em;
    private MailerService $mailer;

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

    private function makePrestataire(string $raisonSociale, ?string $siret = null): Prestataire
    {
        $p = new Prestataire($raisonSociale);
        if (null !== $siret) {
            $p->setSiret($siret);
        }

        return $p;
    }

    private function makeFacturePrestataire(int $year, int $month): FacturePrestataire
    {
        $f = $this->createStub(FacturePrestataire::class);
        $f->method('getYear')->willReturn($year);
        $f->method('getMonth')->willReturn($month);

        return $f;
    }

    private function stubRepository(array $prestataires): void
    {
        $repo = $this->createStub(EntityRepository::class);
        $repo->method('findBy')->willReturn($prestataires);

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

    public function testNoActivePrestatairesReturnsSuccess(): void
    {
        $this->stubRepository([]);

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

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Toutes les factures prestataires', $tester->getDisplay());
    }

    public function testAllPrestatairesHaveFactureForPreviousMonth(): void
    {
        $now = new \DateTimeImmutable();
        $prev = $now->modify('first day of last month');
        $year = (int) $prev->format('Y');
        $month = (int) $prev->format('n');

        $presta = $this->makePrestataire('ACME Corp', '12345678900011');
        $facture = $this->makeFacturePrestataire($year, $month);
        $presta->getFactures()->add($facture);

        $this->stubRepository([$presta]);

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

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Toutes les factures prestataires', $tester->getDisplay());
    }

    public function testMissingFacturesSendsReminderEmail(): void
    {
        $presta1 = $this->makePrestataire('ACME Corp', '12345678900011');
        $presta2 = $this->makePrestataire('Beta SARL');

        // presta1 has no factures for the previous month, presta2 has none at all
        $this->stubRepository([$presta1, $presta2]);

        $this->mailer->expects($this->once())
            ->method('getAdminEmail')
            ->willReturn('admin@e-cosplay.fr');

        $this->mailer->expects($this->once())
            ->method('sendEmail')
            ->with(
                'admin@e-cosplay.fr',
                $this->stringContains('Rappel : factures prestataires'),
                $this->stringContains('ACME Corp'),
                null,
                null,
                false,
            );

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $display = $tester->getDisplay();
        $this->assertStringContainsString('2 facture(s) prestataire(s) manquante(s)', $display);
        $this->assertStringContainsString('rappel envoye', $display);
    }

    public function testPrestataireWithFactureForDifferentMonthStillMissing(): void
    {
        $now = new \DateTimeImmutable();
        $prev = $now->modify('first day of last month');
        $year = (int) $prev->format('Y');
        $month = (int) $prev->format('n');

        // Facture for a different month (two months ago)
        $wrongMonth = $month === 1 ? 12 : $month - 1;
        $wrongYear = $month === 1 ? $year - 1 : $year;

        $presta = $this->makePrestataire('Old Corp', '99999999900011');
        $oldFacture = $this->makeFacturePrestataire($wrongYear, $wrongMonth);
        $presta->getFactures()->add($oldFacture);

        $this->stubRepository([$presta]);

        $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr');
        $this->mailer->expects($this->once())->method('sendEmail');

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('1 facture(s) prestataire(s) manquante(s)', $tester->getDisplay());
    }

    public function testEmailContainsPrestataireDetails(): void
    {
        $presta = $this->makePrestataire('Dupont & Cie', '11122233300011');

        $this->stubRepository([$presta]);

        $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr');

        $capturedHtml = null;
        $this->mailer->method('sendEmail')
            ->willReturnCallback(function (string $to, string $subject, string $html) use (&$capturedHtml) {
                $capturedHtml = $html;
            });

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertNotNull($capturedHtml);
        $this->assertStringContainsString('Dupont & Cie', $capturedHtml);
        $this->assertStringContainsString('11122233300011', $capturedHtml);
        $this->assertStringContainsString('crm.e-cosplay.fr/admin/prestataires', $capturedHtml);
    }

    public function testPrestataireWithoutSiretSendsEmailWithoutSiret(): void
    {
        $presta = $this->makePrestataire('Anonymous Corp');

        $this->stubRepository([$presta]);

        $this->mailer->method('getAdminEmail')->willReturn('admin@e-cosplay.fr');

        $capturedHtml = null;
        $this->mailer->method('sendEmail')
            ->willReturnCallback(function (string $to, string $subject, string $html) use (&$capturedHtml) {
                $capturedHtml = $html;
            });

        $command = new ReminderFacturesPrestataireCommand($this->em, $this->mailer);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertNotNull($capturedHtml);
        $this->assertStringContainsString('Anonymous Corp', $capturedHtml);
    }
}
