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

use BookiFlex\Core\Exception\InvalidArgumentException;
use BookiFlex\Core\Exception\RuntimeException;
use BookiFlex\Core\Model\AccommodationType\Accommodation\AccommodationType;
use BookiFlex\Core\Model\DateUnit\DateUnit;
use BookiFlex\Core\Model\RatePlan\RatePlan;
use BookiFlex\Core\Util\DateTimeUtils;
use BookiFlex\Core\ValueObject\AccommodationSelection;
use BookiFlex\Core\ValueObject\StayPeriod;
use DateTimeImmutable;

defined( 'ABSPATH' ) || exit;

/**
 * Service for creating and retrieving DateUnits
 */
class DateUnitCreator
{
    public function __construct(
        private readonly DateUnitExtender $dateUnitExtender,
        private readonly DateUnitFinder $dateUnitsFinder
    ) {
    }

    /**
     * Returns DateUnit list for the given period.
     * Guarantees that DateUnit will be created for each day in the period.
     *
     * @param RatePlan $ratePlan
     * @param AccommodationType $accommodationType
     * @param array $ranges array of ranges in format ['dateFrom' => 'Y-m-d', 'dateTo' => 'Y-m-d']
     * @return DateUnit[] as [Y-m-d] => DateUnit
     * @throws InvalidArgumentException
     * @throws \Exception
     */
    public function getOrCreateDateUnits(
        RatePlan $ratePlan,
        AccommodationType $accommodationType,
        array $ranges = []
    ): array {
        list($dateFrom, $dateTo) = $this->prepareDates($ranges);

        $result = [];

        // Create new units for whole period
        $newUnits = $this->dateUnitExtender->handle(
            new DTO\ExtendDateUnits(
                $ratePlan,
                [$accommodationType],
                $dateFrom,
                $dateTo,
                [
                    'mon' => true,
                    'tue' => true,
                    'wed' => true,
                    'thu' => true,
                    'fri' => true,
                    'sat' => true,
                    'sun' => true
                ]
            )
        );

        foreach ($newUnits as $unit) {
            $key = $unit->getDate();
            $result[$key] = $unit;
        }

        // Get existing units
        $units = $this->dateUnitsFinder->find(
            new StayPeriod($dateFrom, $dateTo),
            new AccommodationSelection($accommodationType->getId(), $ratePlan->getId()),
        );

        foreach ($units->getDateUnits() as $date => $unit) {
            $result[$date] = $unit;
        }

        return $result;
    }

    /**
     * Returns min and max date from list of ranges.
     *
     * @param array $dates list of ranges in format ['dateFrom' => 'Y-m-d', 'dateTo' => 'Y-m-d']
     * @return array|DateTimeImmutable[] first element is min date, second is max date
     * @throws InvalidArgumentException
     */
    private function prepareDates(array $dates): array
    {
        list($minDate, $maxDate) = DateTimeUtils::findMinMaxInRanges($dates);

        if (!$minDate) {
            throw new InvalidArgumentException('Date from is not set.');
        }

        if (!$maxDate) {
            $maxDate = DateTimeImmutable::createFromFormat(
                'Y-m-d',
                $minDate->format('Y') . '-12-31',
                $minDate->getTimezone()
            );
        }

        return [$minDate, $maxDate];
    }
}
