<?php
/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Core\Service\StayCostCalculator\ValueObject;

use BookiFlex\Core\Contract\Adjustment\{AdjustableInterface, AdjustmentInterface};
use BookiFlex\Core\Contract\Taxation\TaxableInterface;
use BookiFlex\Core\Service\CashierFactory;
use BookiFlex\Core\ValueObject\MoneyAmount;
use Brick\Money\Money;
use DateTimeImmutable;
use Vaened\PriceEngine\Cashier;

defined( 'ABSPATH' ) || exit;

/**
 * Stay cost for one day
 * Uses Summary for complete calculation with taxes and fees
 */
final class StayCostByDate implements AdjustableInterface, TaxableInterface
{
    private DateTimeImmutable $date;
    private Money $basePrice;        // Base price without taxes and fees
    private ?Money $originalPrice;   // Original price (if inheritance discount exists)
    private array $details;
    private array $context;
    /** @var AdjustmentInterface[] */
    private array $adjustments = [];
    private ?Cashier $cachedSummary = null;

    private function __construct(
        DateTimeImmutable $date,
        Money $basePrice,
        ?Money $originalPrice,
        array $details,
        array $context
    ) {
        $this->date = $date;
        $this->basePrice = $basePrice;
        $this->originalPrice = $originalPrice;
        $this->details = $details;
        $this->context = $context;
    }

    /**
     * Create from calculation result
     */
    public static function fromCalculatedPrice(
        array $dayPrice,
        string $currency,
        array $context
    ): self {
        $date = DateTimeImmutable::createFromFormat('Y-m-d', $dayPrice['date'])
            ->setTime(0, 0, 0);
        if ($date === false) {
            throw new \InvalidArgumentException('Invalid date format: ' . $dayPrice['date']);
        }

        $basePrice = $dayPrice['totalPrice']->toBrickMoney($currency);

        $originalTotalPrice = null;
        if (isset($dayPrice['originalTotalPrice'])) {
            $originalTotalPrice = $dayPrice['originalTotalPrice']->toBrickMoney($currency);
        }

        return new self(
            $date,
            $basePrice,      // this is the base price
            $originalTotalPrice,
            $dayPrice['details'] ?? [],
            $context
        );
    }

    // ============================================
    // Main methods for price work
    // ============================================

    /**
     * Get complete summary with taxes and fees
     * This is the main method for getting final cost
     */
    public function getCashier(): Cashier
    {
        if ($this->cachedSummary === null || $this->hasAdjustmentsChanged()) {
            $this->cachedSummary = CashierFactory::make($this->getBasePrice(), $this->adjustments);
        }
        return $this->cachedSummary;
    }

    /**
     * Get base price (without taxes and fees)
     */
    public function getBasePrice(): Money
    {
        return $this->basePrice;
    }

    /**
     * Get original base price (for discount calculation)
     * This is price before applying inheritance modifiers
     */
    public function getOriginalBasePrice(): ?Money
    {
        return $this->originalPrice;
    }

    /**
     * Has inheritance discount (not to be confused with adjustment discounts)
     */
    public function hasInheritanceDiscount(): bool
    {
        if ($this->originalPrice === null) {
            return false;
        }

        return $this->basePrice->isLessThan($this->originalPrice);
    }

    /**
     * Get inheritance discount amount
     */
    public function getInheritanceDiscount(): ?Money
    {
        if (!$this->hasInheritanceDiscount()) {
            return null;
        }

        return $this->originalPrice->minus($this->basePrice);
    }

    /**
     * Get inheritance discount percentage
     */
    public function getInheritanceDiscountPercentage(): ?float
    {
        if (!$this->hasInheritanceDiscount() || $this->originalPrice->isZero()) {
            return null;
        }

        $discount = $this->getInheritanceDiscount();
        return ($discount->getAmount()->dividedBy($this->originalPrice->getAmount()))
            ->multipliedBy(100)
            ->toFloat();
    }

    // ============================================
    // Methods for convenience
    // ============================================

    public function getDate(): DateTimeImmutable
    {
        return $this->date;
    }

    public function getDateString(): string
    {
        return $this->date->format('Y-m-d');
    }

    public function getPriceDetails(): array
    {
        return $this->details;
    }

    public function getContext(): array
    {
        return $this->context;
    }

    /**
     * Get final cost (with taxes and fees)
     * Convenient wrapper method over getSummary()->total()
     */
    public function getFinalPrice(): Money
    {
        return $this->getCashier()->total();
    }

    /**
     * Get final cost as MoneyAmount
     */
    public function getFinalPriceAmount(): MoneyAmount
    {
        $brickMoney = $this->getFinalPrice();
        return MoneyAmount::fromBrickMoney($brickMoney);
    }

    // ============================================
    // AdjustableInterface implementation
    // ============================================

    public function addAdjustment(AdjustmentInterface $adjustment): self
    {
        $this->adjustments[$adjustment->getCode()] = $adjustment;
        $this->cachedSummary = null; // Reset cache
        return $this;
    }

    public function getAdjustments(): array
    {
        return $this->adjustments;
    }

    private function hasAdjustmentsChanged(): bool
    {
        // Simple check for quantity change
        // In reality can add more complex logic
        return false;
    }

    // ============================================
    // TaxableInterface implementation
    // ============================================

    public function getTaxableAmount(): Money
    {
        return $this->getBasePrice(); // Taxes calculated from base price
    }

    public function getTaxableType(): string
    {
        return 'accommodation_daily';
    }

    public function getTaxContext(): array
    {
        return array_merge($this->context, [
            'date' => $this->getDateString(),
            'nights' => 1,
            'hasInheritanceDiscount' => $this->hasInheritanceDiscount(),
            'inheritanceDiscountPercentage' => $this->getInheritanceDiscountPercentage(),
        ]);
    }

    /**
     * Convert to array
     */
    public function toArray(): array
    {
        $summary = $this->getCashier();

        return [
            'date' => $this->getDateString(),
            'basePrice' => $this->basePrice->getAmount(),
            'originalBasePrice' => $this->originalPrice?->getAmount(),

            // Inheritance discount information
            'inheritance' => [
                'hasDiscount' => $this->hasInheritanceDiscount(),
                'discountAmount' => $this->getInheritanceDiscount()?->getAmount(),
                'discountPercentage' => $this->getInheritanceDiscountPercentage(),
            ],

            // Complete summary with taxes and fees
            'summary' => [
                'subtotal' => $summary->subtotal()->net()->getAmount(),
                'taxes' => $summary->taxes()->total()->getAmount(),
                'charges' => $summary->charges()->total()->getAmount(),
                'discounts' => $summary->discounts()->total()->getAmount(),
                'total' => $summary->total()->getAmount(),
            ],

            'details' => $this->details,
        ];
    }
}
