<?php

namespace NoahVet\Reef\Domain\Tool;

use NoahVet\Reef\Domain\Vat\VatCategoryEnum;

class VatTool implements VatToolInterface
{
    /**
     * Express VAT rates in basis points (per 10_000) to avoid floating point issues.
     */
    protected static int $calculBase = 10000;

    /**
     * Precision used when rounding VAT computations.
     */
    protected static int $precision = 6;

    #[\Override]
    public static function addVat(int $amountWithoutVat, int $vatAmount): int
    {
        return (int) ((float) $amountWithoutVat * (1.0 + (float) $vatAmount / (float) self::$calculBase));
    }

    #[\Override]
    public static function getVat(int $amountWithVat, int $amountWithoutVat): int
    {
        if (0 >= $amountWithoutVat) {
            throw new \InvalidArgumentException(
                'The `$amountWithoutVat` should be greater than 0.',
            );
        }
        if ($amountWithVat < $amountWithoutVat) {
            throw new \InvalidArgumentException(
                'The `$amountWithVat` should be lower than or equals to `$amountWithVat`.',
            );
        }

        return (int) (\round(((float) $amountWithVat / (float) $amountWithoutVat) - 1.0, self::$precision) * (float) self::$calculBase);
    }

    #[\Override]
    public static function removeVat(int $amountWithVat, int $vatAmount): int
    {
        return (int) ((float) $amountWithVat / (1.0 + (float) $vatAmount / (float) self::$calculBase));
    }

    #[\Override]
    public static function checkVat(
        int $amountWithoutVat,
        int $amountWithVat,
        VatCategoryEnum $vatCategory,
    ): bool {
        if (0 === $amountWithoutVat && 0 === $amountWithVat) {
            return true;
        }

        if (0 >= $amountWithoutVat || $amountWithVat < $amountWithoutVat) {
            return false;
        }

        $vatAmount = self::getVatAmountFromCategory($vatCategory);
        $vat = self::getVat($amountWithVat, $amountWithoutVat);

        if (1 >= \abs($vat - $vatAmount)) {
            return true;
        }
        $expectedAmountWithVat = self::addVat($amountWithoutVat, $vatAmount);

        return 1 >= \abs($expectedAmountWithVat - $amountWithVat);
    }

    #[\Override]
    public static function getVatCategoryFromAmounts(int $amountWithoutVat, int $amountWithVat): VatCategoryEnum
    {
        if (0 >= $amountWithoutVat || $amountWithVat < $amountWithoutVat) {
            throw new \InvalidArgumentException('Invalid amounts provided.');
        }

        $vatRate = (int) \round((float) ($amountWithVat - $amountWithoutVat) / (float) $amountWithoutVat * (float) self::$calculBase);

        return match (true) {
            0 === $vatRate => VatCategoryEnum::ZERO_CODE_Z,
            209 <= $vatRate && 211 >= $vatRate => VatCategoryEnum::SPECIAL_RATE,
            549 <= $vatRate && 551 >= $vatRate => VatCategoryEnum::REDUCED_RATE,
            849 <= $vatRate && 851 >= $vatRate => VatCategoryEnum::OVERSEAS,
            999 <= $vatRate && 1001 >= $vatRate => VatCategoryEnum::INTERMEDIATE,
            1999 <= $vatRate && 2001 >= $vatRate => VatCategoryEnum::STANDARD,
            default => throw new \LogicException(
                \sprintf('Unknown VAT rate from amounts: %s‰', $vatRate),
            ),
        };
    }

    private static function getVatAmountFromCategory(VatCategoryEnum $vatCategory): int
    {
        return match ($vatCategory) {
            VatCategoryEnum::INTERMEDIATE => 1000,
            VatCategoryEnum::OVERSEAS => 850,
            VatCategoryEnum::REDUCED_RATE => 550,
            VatCategoryEnum::SPECIAL_RATE => 210,
            VatCategoryEnum::STANDARD => 2000,
            default => 0,
        };
    }
}
