<?php

namespace NoahVet\Reef\Test\A_Unit\Domain\Tool;

use NoahVet\Reef\Domain\Tool\VatTool;
use NoahVet\Reef\Domain\Tool\VatToolInterface;
use NoahVet\Reef\Domain\Vat\VatCategoryEnum;
use PHPUnit\Framework\TestCase;

final class VatToolTest extends TestCase
{
    private VatToolInterface $subject;

    protected function setUp(): void
    {
        $this->subject = new VatTool();
    }

    public function testAddVat(): void
    {
        // Add 20% vat on 100€
        $this->assertSame(
            12000,
            $this->subject->addVat(10000, 2000),
        );
    }

    public function testRemoveVat(): void
    {
        // Remove 20% vat on 120€
        $this->assertSame(
            10000,
            $this->subject->removeVat(12000, 2000),
        );
    }

    public function testGetVat(): void
    {
        $this->assertSame(
            2000,
            $this->subject->getVat(12000, 10000),
        );
    }

    public function testGetVatWithDecimal(): void
    {
        $this->assertSame(
            850,
            $this->subject->getVat(53165, 49000),
        );

        $this->assertSame(
            855,
            $this->subject->getVat(10855, 10000),
        );
    }

    public function testGetVatWhenAmountWithoutVatIsLowerThanZero(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('The `$amountWithoutVat` should be greater than 0.');
        $this->subject->getVat(0, 0);

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('The `$amountWithoutVat` should be greater than 0.');
        $this->subject->getVat(-2, -1);
    }

    public function testGetVatWhenAmountWithoutVatIsGreaterThanAmountWithVat(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('The `$amountWithVat` should be lower than or equals to `$amountWithVat`.');
        $this->subject->getVat(1000, 1300);
    }

    public function testCheckVatWhenAmountsAndVatAreCompliant(): void
    {
        self::assertTrue($this->subject->checkVat(0, 0, VatCategoryEnum::EXEMPT));
        self::assertTrue($this->subject->checkVat(2000, 2400, VatCategoryEnum::STANDARD));
    }

    public function testCheckVatAllowsRoundingDifference(): void
    {
        self::assertTrue($this->subject->checkVat(999, 1199, VatCategoryEnum::STANDARD));
    }

    public function testCheckVatWhenAmountsAndVatAreNotCompliant(): void
    {
        self::assertFalse($this->subject->checkVat(2000, 2400, VatCategoryEnum::REDUCED_RATE));
    }

    public function testGetVatCategoryFromAmountsReturnsOverseas(): void
    {
        $result = $this->subject->getVatCategoryFromAmounts(
            4900,
            5316,
        );

        self::assertSame(
            VatCategoryEnum::OVERSEAS,
            $result,
        );
    }

    public function testGetVatCategoryFromAmountsReturnsStandard(): void
    {
        $result = $this->subject->getVatCategoryFromAmounts(
            10000,
            12000,
        );

        self::assertSame(
            VatCategoryEnum::STANDARD,
            $result,
        );
    }

    public function testGetVatCategoryFromAmountsReturnsSpecialRate(): void
    {
        $result = $this->subject->getVatCategoryFromAmounts(
            10000,
            10210,
        );

        self::assertSame(
            VatCategoryEnum::SPECIAL_RATE,
            $result,
        );
    }

    public function testGetVatCategoryFromAmountsReturnsZero(): void
    {
        $result = $this->subject->getVatCategoryFromAmounts(
            10000,
            10000,
        );

        self::assertSame(
            VatCategoryEnum::ZERO_CODE_Z,
            $result,
        );
    }

    public function testGetVatCategoryFromAmountsThrowsForInvalidAmounts(): void
    {
        $this->expectException(\InvalidArgumentException::class);

        $this->subject->getVatCategoryFromAmounts(
            0,
            10000,
        );
    }

    public function testGetVatCategoryFromAmountsThrowsForUnknownRate(): void
    {
        $this->expectException(\LogicException::class);

        $this->subject->getVatCategoryFromAmounts(
            10000,
            11700,
        );
    }
}
