<?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\Model\DateUnit\Calculator;

use BookiFlex\Core\ValueObject\MoneyAmount;

defined( 'ABSPATH' ) || exit;

/**
 * Price calculation result
 */
class CalculatedPriceResult
{
    /**
     * @param array $dailyPrices Array with daily breakdown
     * Format of each element:
     * [
     *   'date' => '2024-01-01',
     *   'totalPrice' => MoneyAmount,
     *   'originalTotalPrice' => ?MoneyAmount,
     *   'details' => [
     *     [
     *       'placementType' => 'adultOnMainPlace',
     *       'variant' => '__2',
     *       'quantity' => 2,
     *       'price' => MoneyAmount,       // price per unit
     *       'total' => MoneyAmount,       // total for this item
     *       'originalPrice' => ?MoneyAmount,
     *       'originalTotal' => ?MoneyAmount
     *     ]
     *   ]
     * ]
     */
    public function __construct(
        private readonly array $dailyPrices
    ) {
    }

    /**
     * Get daily breakdown
     */
    public function getDailyPrices(): array
    {
        return $this->dailyPrices;
    }

    /**
     * Get breakdown for specific date
     */
    public function getPriceForDate(string $date): ?array
    {
        foreach ($this->dailyPrices as $dayPrice) {
            if ($dayPrice['date'] === $date) {
                return $dayPrice;
            }
        }
        return null;
    }

    /**
     * Get total price for entire period
     */
    public function getTotalPrice(): MoneyAmount
    {
        $amounts = array_map(fn($day) => $day['totalPrice'], $this->dailyPrices);
        return MoneyAmount::sum($amounts);
    }

    /**
     * Get original price (before modifiers)
     */
    public function getOriginalTotalPrice(): ?MoneyAmount
    {
        $amounts = array_map(fn($day) => $day['originalTotalPrice'], $this->dailyPrices);

        // If at least one day doesn't have original price - return null
        if (in_array(null, $amounts, true)) {
            return null;
        }

        return MoneyAmount::sum($amounts);
    }

    /**
     * Get difference between current and original price (discount/markup)
     */
    public function getPriceDifference(): ?MoneyAmount
    {
        $original = $this->getOriginalTotalPrice();
        if ($original === null) {
            return null;
        }

        $current = $this->getTotalPrice();
        return $current->subtract($original);
    }

    /**
     * Get discount/markup percentage
     */
    public function getPriceDifferencePercentage(): ?float
    {
        $original = $this->getOriginalTotalPrice();
        if ($original === null || $original->isZero()) {
            return null;
        }

        $difference = $this->getPriceDifference();
        if ($difference === null) {
            return null;
        }

        return ($difference->getAmount() / $original->getAmount()) * 100;
    }

    /**
     * Has discount
     */
    public function hasDiscount(): bool
    {
        $difference = $this->getPriceDifference();
        return $difference !== null && $difference->isNegative();
    }

    /**
     * Has markup
     */
    public function hasMarkup(): bool
    {
        $difference = $this->getPriceDifference();
        return $difference !== null && $difference->isPositive();
    }

    /**
     * Get nights count
     */
    public function getNightsCount(): int
    {
        return count($this->dailyPrices);
    }

    /**
     * Get average price per night
     */
    public function getAverageNightPrice(): MoneyAmount
    {
        $nights = $this->getNightsCount();
        if ($nights === 0) {
            return MoneyAmount::zero();
        }

        return $this->getTotalPrice()->divide($nights);
    }

    /**
     * Convert to array (for serialization)
     */
    public function toArray(): array
    {
        return [
            'dailyPrices' => array_map(function($day) {
                return [
                    'date' => $day['date'],
                    'totalPrice' => $day['totalPrice']->getAmount(),
                    'originalTotalPrice' => $day['originalTotalPrice']?->getAmount(),
                    'details' => array_map(function($detail) {
                        return [
                            'placementType' => $detail['placementType'],
                            'variant' => $detail['variant'],
                            'quantity' => $detail['quantity'],
                            'price' => $detail['price']->getAmount(),
                            'total' => $detail['total']->getAmount(),
                            'originalPrice' => $detail['originalPrice']?->getAmount(),
                            'originalTotal' => $detail['originalTotal']?->getAmount(),
                        ];
                    }, $day['details'])
                ];
            }, $this->dailyPrices),
            'summary' => [
                'totalPrice' => $this->getTotalPrice()->getAmount(),
                'originalTotalPrice' => $this->getOriginalTotalPrice()?->getAmount(),
                'priceDifference' => $this->getPriceDifference()?->getAmount(),
                'differencePercentage' => $this->getPriceDifferencePercentage(),
                'nightsCount' => $this->getNightsCount(),
                'averageNightPrice' => $this->getAverageNightPrice()->getAmount(),
                'hasDiscount' => $this->hasDiscount(),
                'hasMarkup' => $this->hasMarkup(),
            ]
        ];
    }

    // For backward compatibility
    public function getPriceDetails(): array
    {
        return $this->dailyPrices;
    }

    public function getOriginTotalPrice(): ?MoneyAmount
    {
        return $this->getOriginalTotalPrice();
    }
}
