<?php
/**
 * For the full copyright and license information, please view the
 * docs/licenses/LICENSE.txt file that was distributed with this source code.
 */

declare(strict_types=1);

namespace PrestaShop\PrestaShop\Adapter\Tax;

use Address;
use PrestaShop\Decimal\DecimalNumber;
use PrestaShop\Decimal\Operation\Division;
use PrestaShop\PrestaShop\Adapter\TaxRulesGroup\Repository\TaxRulesGroupRepository;
use PrestaShop\PrestaShop\Core\Domain\Country\ValueObject\CountryId;
use PrestaShop\PrestaShop\Core\Domain\State\ValueObject\StateId;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\ValueObject\TaxRulesGroupId;
use TaxManagerFactory;

class TaxComputer
{
    /**
     * The conversion between tax rate as percent to tax ratio as float value can make us lose some precision,
     * so we increase the default precision (6) to avoid losing two digits by diving by 100 (two decimal factors).
     */
    protected const DIVISION_PRECISION = Division::DEFAULT_PRECISION + 2;

    /**
     * @var TaxRulesGroupRepository
     */
    private $taxRulesGroupRepository;

    /**
     * @param TaxRulesGroupRepository $taxRulesGroupRepository
     */
    public function __construct(
        TaxRulesGroupRepository $taxRulesGroupRepository
    ) {
        $this->taxRulesGroupRepository = $taxRulesGroupRepository;
    }

    /**
     * @param DecimalNumber $priceTaxExcluded
     * @param TaxRulesGroupId $taxRulesGroupId
     * @param CountryId $countryId
     *
     * @return DecimalNumber
     */
    public function computePriceWithTaxes(
        DecimalNumber $priceTaxExcluded,
        TaxRulesGroupId $taxRulesGroupId,
        CountryId $countryId
    ): DecimalNumber {
        $taxRatio = $this->getTaxRatio($taxRulesGroupId, $countryId);

        return $priceTaxExcluded->times($taxRatio);
    }

    /**
     * @param DecimalNumber $priceTaxIncluded
     * @param TaxRulesGroupId $taxRulesGroupId
     * @param CountryId $countryId
     *
     * @return DecimalNumber
     */
    public function computePriceWithoutTaxes(
        DecimalNumber $priceTaxIncluded,
        TaxRulesGroupId $taxRulesGroupId,
        CountryId $countryId
    ): DecimalNumber {
        $taxRatio = $this->getTaxRatio($taxRulesGroupId, $countryId);

        return $priceTaxIncluded->dividedBy($taxRatio);
    }

    /**
     * Returns the tax rate for a group and a specific country. The value is the decimal rate (usually a float between 0 and 1)
     *
     * @param TaxRulesGroupId $taxRulesGroupId
     * @param CountryId $countryId
     *
     * @return DecimalNumber
     */
    public function getTaxRate(TaxRulesGroupId $taxRulesGroupId, CountryId $countryId): DecimalNumber
    {
        $stateId = $this->taxRulesGroupRepository->getTaxRulesGroupDefaultStateId($taxRulesGroupId, $countryId);
        if ($stateId) {
            return $this->getTaxRateByState($taxRulesGroupId, $countryId, new StateId($stateId));
        }

        $address = new Address();
        $address->id_country = $countryId->getValue();
        $taxCalculator = TaxManagerFactory::getManager($address, $taxRulesGroupId->getValue())->getTaxCalculator();

        return new DecimalNumber((string) $taxCalculator->getTotalRate());
    }

    /**
     * Returns the tax rate for a group and a specific country and state. The value is the decimal rate (usually a float between 0 and 1)
     *
     * @param TaxRulesGroupId $taxRulesGroupId
     * @param CountryId $countryId
     * @param StateId $stateId
     *
     * @return DecimalNumber
     */
    public function getTaxRateByState(TaxRulesGroupId $taxRulesGroupId, CountryId $countryId, StateId $stateId): DecimalNumber
    {
        $address = new Address();
        $address->id_country = $countryId->getValue();
        $address->id_state = $stateId->getValue();

        $taxCalculator = TaxManagerFactory::getManager($address, $taxRulesGroupId->getValue())->getTaxCalculator();

        return new DecimalNumber((string) $taxCalculator->getTotalRate());
    }

    /**
     * @param TaxRulesGroupId $taxRulesGroupId
     * @param CountryId $countryId
     *
     * @return DecimalNumber
     */
    protected function getTaxRatio(TaxRulesGroupId $taxRulesGroupId, CountryId $countryId): DecimalNumber
    {
        $countryTaxRate = $this->getTaxRate($taxRulesGroupId, $countryId);

        return $countryTaxRate->dividedBy(new DecimalNumber('100'), self::DIVISION_PRECISION)->plus(new DecimalNumber('1'));
    }
}
