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

declare(strict_types=1);

namespace BookiFlex\Extensions\SimpleQuota\Service;

use BookiFlex\Core\Contract\AvailabilityChecker\QuotaAvailabilityInterface;
use BookiFlex\Core\Model\AccommodationType\Accommodation\AccommodationType;
use BookiFlex\Core\Repository\AccommodationTypeRepository;
use BookiFlex\Core\Repository\ReservationRepository;
use BookiFlex\Core\ValueObject\StayPeriod;
use WeakReference;

defined( 'ABSPATH' ) || exit;

/**
 * Simple implementation of quota availability check.
 * Checks all confirmed reservations for the specified dates and accommodation type
 * and compares with the total number of available rooms.
 */
class QuotaAvailability implements QuotaAvailabilityInterface
{
    /** @var WeakReference<AccommodationType>[] */
    private array $cache = [];

    /** @var array<string, array<string, int>> */
    private array $occupiedAccommodationTypes = [];

    public function __construct(
        private readonly AccommodationTypeRepository $accommodationTypeRepository,
        private readonly ReservationRepository $reservationRepository,
    ) {
    }

    public function getQuota(StayPeriod $stayPeriod, int $accommodationTypeId): int
    {
        $type = $this->getById($accommodationTypeId);
        if (!$type) {
            return 0;
        }

        $hash = $this->getDateRangeHash($stayPeriod);
        if (!isset($this->occupiedAccommodationTypes[$hash])) {
            $this->occupiedAccommodationTypes[$hash] = $this->loadOccupancyMap($stayPeriod);
        }

        $booked = $this->occupiedAccommodationTypes[$hash][$type->getShortCode()] ?? 0;
        return max(0, $type->getAccommodationAvailableUnits() - $booked);
    }

    public function getById(int $id): ?AccommodationType
    {
        if (isset($this->cache[$id])) {
            $obj = $this->cache[$id]->get();
            if ($obj !== null) {
                return $obj;
            }

            unset($this->cache[$id]);
        }

        $obj = $this->accommodationTypeRepository->findByPK($id);
        if ($obj) {
            $this->cache[$id] = WeakReference::create($obj);
        }

        return $obj;
    }

    private function loadOccupancyMap(StayPeriod $stayPeriod): array
    {
        $map = [];
        foreach ($this->reservationRepository->getOccupancyOnDates($stayPeriod->getCheckInDate(), $stayPeriod->getCheckOutDate()) as $item) {
            $map[$item['accommodationTypeCode']] = (int) $item['bookedCount'];
        }
        return $map;
    }

    private function getDateRangeHash(StayPeriod $stayPeriod): string
    {
        return $stayPeriod->getCheckInDate()->format('Y-m-d') . '_' . $stayPeriod->getCheckOutDate()->format('Y-m-d');
    }
}
