<?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\Core\Pricing\Product;

use PrestaShop\Decimal\DecimalNumber;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\ImmutableTaxablePrice;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\PriceBreakdown;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\PriceModification;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\TaxablePrice;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\TaxablePriceInterface;
use PrestaShop\PrestaShop\Core\Pricing\ValueObject\TaxRate;

/**
 * Debug-aware ProductPrice that auto-records every setter call as a PriceModification
 * via debug_backtrace, capturing which calculator made the change. Calculators are
 * completely unaware of the tracking — same interface as ProductPrice.
 */
class TrackedProductPrice implements ProductPriceInterface
{
    protected TaxablePrice $unitPrice;
    protected TaxablePrice $originalPrice;
    protected TaxablePrice $discountPrice;
    protected ImmutableTaxablePrice $finalPrice;
    protected PriceBreakdown $breakdown;

    protected function __construct(
        protected readonly int $productId,
        protected readonly int $combinationId,
        protected readonly int $quantity,
    ) {
        $this->unitPrice = TaxablePrice::zero();
        $this->originalPrice = TaxablePrice::zero();
        $this->discountPrice = TaxablePrice::zero();
        $this->finalPrice = new ImmutableTaxablePrice(
            new DecimalNumber('0'),
            new DecimalNumber('0'),
            new DecimalNumber('0'),
            TaxRate::zero(),
        );
        $this->breakdown = new PriceBreakdown();
    }

    public static function create(int $productId, int $combinationId, int $quantity = 1): self
    {
        return new self($productId, $combinationId, $quantity);
    }

    public function getProductId(): int
    {
        return $this->productId;
    }

    public function getCombinationId(): int
    {
        return $this->combinationId;
    }

    public function getQuantity(): int
    {
        return $this->quantity;
    }

    public function getUnitPrice(): TaxablePrice
    {
        return $this->unitPrice;
    }

    public function setUnitPrice(TaxablePrice $unitPrice): void
    {
        $this->recordModification('unitPrice', $this->unitPrice, $unitPrice);
        $this->unitPrice = $unitPrice;
    }

    public function getOriginalPrice(): TaxablePrice
    {
        return $this->originalPrice;
    }

    public function setOriginalPrice(TaxablePrice $originalPrice): void
    {
        $this->recordModification('originalPrice', $this->originalPrice, $originalPrice);
        $this->originalPrice = $originalPrice;
    }

    public function getDiscountPrice(): TaxablePrice
    {
        return $this->discountPrice;
    }

    public function setDiscountPrice(TaxablePrice $discountPrice): void
    {
        $this->recordModification('discountPrice', $this->discountPrice, $discountPrice);
        $this->discountPrice = $discountPrice;
    }

    public function getFinalPrice(): ImmutableTaxablePrice
    {
        return $this->finalPrice;
    }

    public function setFinalPrice(ImmutableTaxablePrice $finalPrice): void
    {
        $this->recordModification('finalPrice', $this->finalPrice, $finalPrice);
        $this->finalPrice = $finalPrice;
    }

    public function getBreakdown(): PriceBreakdown
    {
        return $this->breakdown;
    }

    protected function recordModification(string $property, TaxablePriceInterface $previous, TaxablePriceInterface $new): void
    {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
        $caller = $trace[2] ?? [];

        $this->breakdown->addStep(new PriceModification(
            callerClass: $caller['class'] ?? 'unknown',
            callerLine: $caller['line'] ?? 0,
            property: $property,
            previousValue: (string) $previous->getTaxExcluded(),
            newValue: (string) $new->getTaxExcluded(),
        ));
    }
}
