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

use BookiFlex\Core\Exception\InvalidArgumentException;
use BookiFlex\Core\Model\AccommodationType\Accommodation\AccommodationType;
use BookiFlex\Core\Model\RatePlan\RatePlan;
use BookiFlex\Core\Repository\AccommodationTypeRepository;
use BookiFlex\Core\Repository\RatePlanRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Service for validating and retrieving RatePlan and AccommodationType entities
 */
class EntityValidator
{
    public function __construct(
        private readonly RatePlanRepository $ratePlanRepository,
        private readonly AccommodationTypeRepository $accommodationTypeRepository
    ) {
    }

    /**
     * Get and validate RatePlan by ID
     *
     * @throws InvalidArgumentException
     */
    public function getRatePlan(int $id): RatePlan
    {
        $ratePlan = $this->ratePlanRepository->findByPK($id);
        if (!$ratePlan) {
            throw new InvalidArgumentException(
                sprintf('Rate plan with ID %d is not found', $id),
                404
            );
        }

        return $ratePlan;
    }

    /**
     * Get and validate AccommodationType by ID
     *
     * @throws InvalidArgumentException
     */
    public function getAccommodationType(int $id): AccommodationType
    {
        $accommodationType = $this->accommodationTypeRepository->findByPK($id);
        if (!$accommodationType) {
            throw new InvalidArgumentException(
                sprintf('Accommodation type with ID %d is not found', $id),
                404
            );
        }

        return $accommodationType;
    }

    /**
     * Validate that RatePlan supports AccommodationType
     *
     * @throws InvalidArgumentException
     */
    public function validateRatePlanSupportsAccommodation(
        RatePlan $ratePlan,
        AccommodationType $accommodationType
    ): void {
        if (!$ratePlan->supportAccommodationType($accommodationType->getId())) {
            throw new InvalidArgumentException(
                sprintf(
                    'Rate plan %s does not support accommodation type %s',
                    $ratePlan->getName(),
                    $accommodationType->getName()
                ),
                200,
                null,
                [
                    'ratePlan' => $ratePlan->getName(),
                    'accommodationType' => $accommodationType->getName()
                ]
            );
        }
    }

    /**
     * Get AccommodationType from RatePlan's supported types
     *
     * @throws InvalidArgumentException
     */
    public function getAccommodationTypeFromRatePlan(
        RatePlan $ratePlan,
        int $accommodationTypeId
    ): AccommodationType {
        if (!$ratePlan->supportAccommodationType($accommodationTypeId)) {
            throw new InvalidArgumentException('Rate plan does not support accommodation type');
        }

        $accommodationType = array_filter(
            $ratePlan->getAccommodationTypes(),
            fn(AccommodationType $at) => $at->getId() === $accommodationTypeId
        );

        if (count($accommodationType) !== 1) {
            throw new InvalidArgumentException('Accommodation type not found');
        }

        return array_values($accommodationType)[0];
    }
}
