<?php

namespace App\Tests\Service;

use App\Entity\Advert;
use App\Entity\Facture;
use App\Entity\OrderNumber;
use App\Service\FactureService;
use App\Service\OrderNumberService;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;

class FactureServiceTest extends TestCase
{
    private const HMAC_SECRET = 'test-hmac-secret';

    public function testCreateWithoutAdvert(): void
    {
        $orderNumber = new OrderNumber('04/2026-00001');
        $orderNumber->markAsUsed();

        $orderService = $this->createStub(OrderNumberService::class);
        $orderService->method('generateAndUse')->willReturn($orderNumber);

        $em = $this->createStub(EntityManagerInterface::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->create();

        $this->assertInstanceOf(Facture::class, $facture);
        $this->assertSame($orderNumber, $facture->getOrderNumber());
        $this->assertNull($facture->getAdvert());
    }

    public function testCreateFromAdvertFirstFacture(): void
    {
        $orderNumber = new OrderNumber('04/2026-00002');

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getFactures')->willReturn(new ArrayCollection());

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->create($advert);

        $this->assertInstanceOf(Facture::class, $facture);
        $this->assertSame(0, $facture->getSplitIndex());
    }

    public function testCreateFromAdvertSecondFacture(): void
    {
        $orderNumber = new OrderNumber('04/2026-00003');

        $firstFacture = new Facture($orderNumber, self::HMAC_SECRET);

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getFactures')->willReturn(new ArrayCollection([$firstFacture]));

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->create($advert);

        $this->assertSame(2, $facture->getSplitIndex());
        $this->assertSame(1, $firstFacture->getSplitIndex());
    }

    public function testCreateFromAdvertThirdFacture(): void
    {
        $orderNumber = new OrderNumber('04/2026-00004');

        $f1 = new Facture($orderNumber, self::HMAC_SECRET);
        $f1->setSplitIndex(1);
        $f2 = new Facture($orderNumber, self::HMAC_SECRET);
        $f2->setSplitIndex(2);

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getFactures')->willReturn(new ArrayCollection([$f1, $f2]));

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->create($advert);

        $this->assertSame(3, $facture->getSplitIndex());
    }

    public function testCreateFromAdvertDirectCall(): void
    {
        $orderNumber = new OrderNumber('04/2026-00005');

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getFactures')->willReturn(new ArrayCollection());

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->createFromAdvert($advert);

        $this->assertInstanceOf(Facture::class, $facture);
    }

    // --- isTvaEnabled ---

    public function testIsTvaEnabledReturnsTrueForTrueString(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET,
            'true'
        );
        $this->assertTrue($service->isTvaEnabled());
    }

    public function testIsTvaEnabledReturnsTrueForOne(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET,
            '1'
        );
        $this->assertTrue($service->isTvaEnabled());
    }

    public function testIsTvaEnabledReturnsFalseByDefault(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET
        );
        $this->assertFalse($service->isTvaEnabled());
    }

    // --- getTvaRate ---

    public function testGetTvaRateReturnsFloat(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET,
            'false',
            '0.20'
        );
        $this->assertSame(0.20, $service->getTvaRate());
    }

    // --- computeTotals ---

    public function testComputeTotalsTvaDisabled(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET,
            'false',
            '0.20'
        );
        $result = $service->computeTotals('150.00');

        $this->assertSame('150.00', $result['totalHt']);
        $this->assertSame('0.00', $result['totalTva']);
        $this->assertSame('150.00', $result['totalTtc']);
    }

    public function testComputeTotalsTvaEnabled(): void
    {
        $service = new FactureService(
            $this->createStub(OrderNumberService::class),
            $this->createStub(EntityManagerInterface::class),
            $this->createStub(\Psr\Log\LoggerInterface::class),
            self::HMAC_SECRET,
            'true',
            '0.20'
        );
        $result = $service->computeTotals('100.00');

        $this->assertSame('100.00', $result['totalHt']);
        $this->assertSame('20.00', $result['totalTva']);
        $this->assertSame('120.00', $result['totalTtc']);
    }

    // --- createPaidFactureFromAdvert ---

    public function testCreatePaidFactureFromAdvertSuccess(): void
    {
        $orderNumber = new OrderNumber('04/2026-00010');

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getFactures')->willReturn(new ArrayCollection());
        $advert->method('getTotalTtc')->willReturn('200.00');
        $advert->method('getLines')->willReturn(new ArrayCollection());

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->createPaidFactureFromAdvert($advert, '200.00', 'Virement');

        $this->assertInstanceOf(Facture::class, $facture);
        $this->assertTrue($facture->isPaid());
        $this->assertSame('Virement', $facture->getPaidMethod());
        $this->assertSame(Facture::STATE_PAID, $facture->getState());
    }

    public function testCreatePaidFactureFromAdvertAmountMismatch(): void
    {
        $orderNumber = new OrderNumber('04/2026-00011');

        $advert = $this->createStub(Advert::class);
        $advert->method('getOrderNumber')->willReturn($orderNumber);
        $advert->method('getTotalTtc')->willReturn('200.00');

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->createPaidFactureFromAdvert($advert, '150.00', 'CB');

        $this->assertNull($facture);
    }

    public function testCreateFromAdvertCopiesLinesWithDescriptionTypeServiceId(): void
    {
        $orderNumber = new OrderNumber('04/2026-00020');

        // Build real AdvertLines with description/type/serviceId so createFromAdvert copies them
        $advert = new \App\Entity\Advert($orderNumber, 'secret');

        $line1 = new \App\Entity\AdvertLine($advert, 'Hébergement', '60.00', 1);
        $line1->setDescription('Hébergement annuel');
        $line1->setType('hosting');
        $line1->setServiceId(5);
        $advert->addLine($line1);

        $line2 = new \App\Entity\AdvertLine($advert, 'Domaine', '15.00', 2);
        // no description, no type, no serviceId
        $advert->addLine($line2);

        $em = $this->createStub(EntityManagerInterface::class);
        $orderService = $this->createStub(OrderNumberService::class);

        $service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
        $facture = $service->createFromAdvert($advert);

        $this->assertInstanceOf(Facture::class, $facture);
        $this->assertCount(2, $facture->getLines());
    }
}
