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

declare(strict_types=1);

namespace BookiFlex\Extensions\StripePayment\ValueObject;

use BookiFlex\Core\Model\Reservation\ReservationAccommodationItem;
use DateTimeImmutable;

defined( 'ABSPATH' ) || exit;

/**
 * Groups accommodation items by type for line item aggregation.
 *
 * This value object collects multiple nights of the same accommodation type
 * and provides aggregated data for creating a single Stripe line item.
 *
 * @package BookiFlex\Extensions\StripePayment\Pro\ValueObject
 */
final class AccommodationGroup
{
    /** @var ReservationAccommodationItem[] */
    private array $items = [];

    private int $totalAmountCents = 0;

    /**
     * Create a new accommodation group.
     *
     * @param string $accommodationTypeCode Accommodation type identifier (e.g., 'DBL', 'SUITE')
     * @param string $accommodationTypeName Display name (e.g., 'Standard Room')
     */
    public function __construct(
        private readonly string $accommodationTypeCode,
        private readonly string $accommodationTypeName
    ) {
    }

    /**
     * Add an accommodation item to this group.
     *
     * @param ReservationAccommodationItem $item Item to add
     */
    public function addItem(ReservationAccommodationItem $item): void
    {
        $this->items[] = $item;
        $this->totalAmountCents += $item->getTotalPrice()->getMinorAmount()->toInt();
    }

    /**
     * Get accommodation type code.
     *
     * @return string
     */
    public function getAccommodationTypeCode(): string
    {
        return $this->accommodationTypeCode;
    }

    /**
     * Get accommodation type name.
     *
     * @return string
     */
    public function getAccommodationTypeName(): string
    {
        return $this->accommodationTypeName;
    }

    /**
     * Get number of nights (quantity).
     *
     * @return int
     */
    public function getQuantity(): int
    {
        return count($this->items);
    }

    /**
     * Calculate average unit amount in cents.
     *
     * Returns the average price per night. If prices vary by night,
     * this calculates the mean value.
     *
     * @return int Average price per night in cents
     */
    public function getUnitAmountCents(): int
    {
        if ($this->getQuantity() === 0) {
            return 0;
        }

        return (int) round($this->totalAmountCents / $this->getQuantity());
    }

    /**
     * Get total amount in cents.
     *
     * @return int
     */
    public function getTotalAmountCents(): int
    {
        return $this->totalAmountCents;
    }

    /**
     * Get currency code from first item.
     *
     * @return string Currency code (e.g., 'EUR', 'USD')
     */
    public function getCurrency(): string
    {
        if (empty($this->items)) {
            return 'EUR'; // Default fallback
        }

        return $this->items[0]->getTotalPrice()->getCurrency()->getCurrencyCode();
    }

    /**
     * Get check-in date (earliest date in group).
     *
     * @return DateTimeImmutable
     */
    public function getCheckInDate(): DateTimeImmutable
    {
        if (empty($this->items)) {
            return new DateTimeImmutable();
        }

        $dates = array_map(fn ($item) => $item->getDate(), $this->items);
        return min($dates);
    }

    /**
     * Get check-out date (latest date + 1 day).
     *
     * @return DateTimeImmutable
     */
    public function getCheckOutDate(): DateTimeImmutable
    {
        if (empty($this->items)) {
            return new DateTimeImmutable();
        }

        $dates = array_map(fn ($item) => $item->getDate(), $this->items);
        return max($dates)->modify('+1 day');
    }

    /**
     * Get number of adults (from first item).
     *
     * Assumes all items in group have same guest configuration.
     *
     * @return int
     */
    public function getAdults(): int
    {
        if (empty($this->items)) {
            return 0;
        }

        return $this->items[0]->getAdults();
    }

    /**
     * Get children information (from first item).
     *
     * Assumes all items in group have same guest configuration.
     *
     * @return array<int>
     */
    public function getChildren(): array
    {
        if (empty($this->items)) {
            return [];
        }

        return $this->items[0]->getChildren();
    }

    /**
     * Get all items in this group.
     *
     * @return ReservationAccommodationItem[]
     */
    public function getItems(): array
    {
        return $this->items;
    }
}