<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Shop\DTO;

final class ShippingQuote
{
    /**
     * @param list<string> $details
     */
    public function __construct(
        public readonly int $amountTTC,
        public readonly int $amountHT,
        public readonly int $taxAmount,
        public readonly float $taxRate,
        public readonly ?string $shippingName = null,
        public readonly array $details = [],
    ) {
    }

    /**
     * Construit un devis transport à partir d'un montant TTC.
     */
    public static function fromTTC(
        int $amountTTC,
        float $taxRate,
        ?string $shippingName = null,
        array $details = [],
    ): self {
        $amountTTC = max(0, $amountTTC);
        $taxRate = max(0.0, $taxRate);

        if (0 === $amountTTC) {
            return new self(
                amountTTC: 0,
                amountHT: 0,
                taxAmount: 0,
                taxRate: $taxRate,
                shippingName: $shippingName,
                details: $details,
            );
        }

        if ($taxRate <= 0.0) {
            return new self(
                amountTTC: $amountTTC,
                amountHT: $amountTTC,
                taxAmount: 0,
                taxRate: 0.0,
                shippingName: $shippingName,
                details: $details,
            );
        }

        $amountHT = self::roundMoney($amountTTC / (1 + $taxRate));
        $taxAmount = $amountTTC - $amountHT;

        return new self(
            amountTTC: $amountTTC,
            amountHT: $amountHT,
            taxAmount: $taxAmount,
            taxRate: $taxRate,
            shippingName: $shippingName,
            details: $details,
        );
    }

    /**
     * Construit un devis transport à partir d'un montant HT.
     */
    public static function fromHT(
        int $amountHT,
        float $taxRate,
        ?string $shippingName = null,
        array $details = [],
    ): self {
        $amountHT = max(0, $amountHT);
        $taxRate = max(0.0, $taxRate);

        if (0 === $amountHT) {
            return new self(
                amountTTC: 0,
                amountHT: 0,
                taxAmount: 0,
                taxRate: $taxRate,
                shippingName: $shippingName,
                details: $details,
            );
        }

        if ($taxRate <= 0.0) {
            return new self(
                amountTTC: $amountHT,
                amountHT: $amountHT,
                taxAmount: 0,
                taxRate: 0.0,
                shippingName: $shippingName,
                details: $details,
            );
        }

        $amountTTC = self::roundMoney($amountHT * (1 + $taxRate));
        $taxAmount = $amountTTC - $amountHT;

        return new self(
            amountTTC: $amountTTC,
            amountHT: $amountHT,
            taxAmount: $taxAmount,
            taxRate: $taxRate,
            shippingName: $shippingName,
            details: $details,
        );
    }

    public function withAmountTTC(int $amountTTC): self
    {
        return self::fromTTC(
            amountTTC: $amountTTC,
            taxRate: $this->taxRate,
            shippingName: $this->shippingName,
            details: $this->details,
        );
    }

    public function withAmountHT(int $amountHT): self
    {
        return self::fromHT(
            amountHT: $amountHT,
            taxRate: $this->taxRate,
            shippingName: $this->shippingName,
            details: $this->details,
        );
    }

    public function withShippingName(?string $shippingName): self
    {
        return new self(
            amountTTC: $this->amountTTC,
            amountHT: $this->amountHT,
            taxAmount: $this->taxAmount,
            taxRate: $this->taxRate,
            shippingName: $shippingName,
            details: $this->details,
        );
    }

    public function withTaxRate(float $taxRate): self
    {
        return self::fromTTC(
            amountTTC: $this->amountTTC,
            taxRate: $taxRate,
            shippingName: $this->shippingName,
            details: $this->details,
        );
    }

    public function addAmountTTC(int $amountTTC): self
    {
        return $this->withAmountTTC($this->amountTTC + max(0, $amountTTC));
    }

    public function addAmountHT(int $amountHT): self
    {
        return $this->withAmountHT($this->amountHT + max(0, $amountHT));
    }

    public function addDetail(string $detail): self
    {
        $details = $this->details;
        $details[] = $detail;

        return new self(
            amountTTC: $this->amountTTC,
            amountHT: $this->amountHT,
            taxAmount: $this->taxAmount,
            taxRate: $this->taxRate,
            shippingName: $this->shippingName,
            details: $details,
        );
    }

    public function isFree(): bool
    {
        return 0 === $this->amountTTC;
    }

    private static function roundMoney(float $value): int
    {
        return (int) round($value, 0, \PHP_ROUND_HALF_UP);
    }
}
