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

use BookiFlex\Core\Contract\AllocationServiceInterface;
use BookiFlex\Core\Model\AccommodationType\Accommodation\AccommodationType;
use BookiFlex\Core\Model\AccommodationType\Guest\AdultGuest;
use BookiFlex\Core\Model\AccommodationType\Scheme\Allocation;
use BookiFlex\Core\Model\AccommodationType\Scheme\AllocationScheme;
use BookiFlex\Core\ValueObject\GuestConfiguration;
use BookiFlex\Core\ValueObject\PlacementLocation;

defined( 'ABSPATH' ) || exit;

/**
 * Base allocation service for free version
 * All guests (adults and children) are treated equally and occupy full places
 */
final class BaseAllocationService implements AllocationServiceInterface
{
    /**
     * Check if accommodation can fit the guests
     */
    public function canAccommodate(
        AccommodationType $accommodation,
        GuestConfiguration $guestConfiguration,
    ): bool {
        try {
            $this->createScheme($accommodation, $guestConfiguration);
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Create placement scheme for given guests
     * All guests are treated as adults and placed on main/extra places
     */
    public function createScheme(
        AccommodationType $accommodation,
        GuestConfiguration $guestConfiguration,
    ): AllocationScheme {
        $scheme = new AllocationScheme();
        $capacity = $accommodation->getCapacity();
        $rules = $accommodation->getRules();

        // Total guest count (adults + children, all treated equally)
        $totalGuests = $guestConfiguration->getAdults() + count($guestConfiguration->getChildren());

        if ($totalGuests === 0) {
            return $scheme;
        }

        $mainLeft = $capacity->getMainPlaces();
        $extraLeft = $capacity->getExtraPlaces();

        // Check if we have enough total capacity
        $totalCapacity = $mainLeft + $extraLeft;
        if ($totalGuests > $totalCapacity) {
            throw new \RuntimeException(
                sprintf(
                    'Not enough capacity for all guests. Need %1$d, have %2$d',
                    $totalGuests,
                    $totalCapacity
                )
            );
        }

        // Place guests on main places first
        if ($totalGuests > 0 && $mainLeft > 0) {
            $guestsOnMain = min($totalGuests, $mainLeft);

            $adultGuest = new AdultGuest($guestsOnMain);
            if (!$rules->canAccommodate($adultGuest, PlacementLocation::main())) {
                throw new \RuntimeException(
                    sprintf(
                        'Cannot place %d guests on main places',
                        $guestsOnMain
                    )
                );
            }

            $scheme->addAllocation(new Allocation(
                $adultGuest,
                PlacementLocation::main(),
                $guestsOnMain
            ));

            $mainLeft -= $guestsOnMain;
            $totalGuests -= $guestsOnMain;
        }

        // Place remaining guests on extra places
        if ($totalGuests > 0) {
            if ($totalGuests > $extraLeft) {
                throw new \RuntimeException("Not enough extra capacity for remaining guests");
            }

            $adultGuest = new AdultGuest(1);
            if (!$rules->canAccommodate($adultGuest, PlacementLocation::extra())) {
                throw new \RuntimeException("Cannot place guests on extra places");
            }

            $scheme->addAllocation(new Allocation(
                $adultGuest,
                PlacementLocation::extra(),
                $totalGuests
            ));
        }

        return $scheme;
    }
}
