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

use BookiFlex\Core;
use BookiFlex\Core\Contract\AllocationServiceInterface;
use BookiFlex\Core\Contract\Queue\QueueInterface;
use BookiFlex\Core\Contract\Taxation\TaxRuleProviderInterface;
use BookiFlex\Core\Infrastructure\Storage\CacheRepositoryInterface;
use BookiFlex\Core\Infrastructure\Storage\OptionRepositoryInterface;
use BookiFlex\Core\Infrastructure\Storage\WordPress\WordPressCacheRepository;
use BookiFlex\Core\Infrastructure\Storage\WordPress\WordPressOptionRepository;
use BookiFlex\Core\Model\AccommodationType\Accommodation\AccommodationType;
use BookiFlex\Core\Model\CancellationPolicy\CancellationPolicy;
use BookiFlex\Core\Model\DateUnit\RestrictionsChecker;
use BookiFlex\Core\Model\PaymentType\PaymentType;
use BookiFlex\Core\Model\RatePlan\RatePlan;
use BookiFlex\Core\Model\Reservation\Adjustment;
use BookiFlex\Core\Model\Reservation\GroupReservation;
use BookiFlex\Core\Model\Reservation\Reservation;
use BookiFlex\Core\Model\Reservation\ReservationAccommodationItem;
use BookiFlex\Core\Model\Reservation\ReservationExtraItem;
use BookiFlex\Core\Admin\CPT\AccommodationType as AccommodationTypeCPT;
use BookiFlex\Core\Admin\CPT\CancellationPolicy as CancellationPolicyCPT;
use BookiFlex\Core\Admin\CPT\RatePlan as RatePlanCPT;
use BookiFlex\Core\ORM\DataProvider\CarbonFieldsParser;
use BookiFlex\Core\ORM\DataProvider\CoreNormalizers;
use BookiFlex\Core\ORM\DataProvider\CustomPostTypeDataProvider;
use BookiFlex\Core\Repository\AccommodationTypeRepository;
use BookiFlex\Core\Repository\CancellationPolicyRepository;
use BookiFlex\Core\Repository\CustomerRepository;
use BookiFlex\Core\Repository\DateUnitRepository;
use BookiFlex\Core\Repository\PaymentTypeRepository;
use BookiFlex\Core\Repository\PenaltyRepository;
use BookiFlex\Core\Repository\RatePlanRepository;
use BookiFlex\Core\Repository\ReservationAccommodationItemRepository;
use BookiFlex\Core\Repository\ReservationAdjustmentRepository;
use BookiFlex\Core\Repository\ReservationExtraItemRepository;
use BookiFlex\Core\Repository\ReservationGroupRepository;
use BookiFlex\Core\Repository\ReservationRepository;
use BookiFlex\Core\Service;
use BookiFlex\Core\Service\AccommodationType\AccommodationCapacityFinder;
use BookiFlex\Core\Service\AccommodationType\BaseAllocationService;
use BookiFlex\Core\Service\AccommodationType\ScenarioGeneratorService;
use BookiFlex\Core\Service\AvailabilityChecker\AvailabilityChecker;
use BookiFlex\Core\Service\CptCreator\CptCreator;
use BookiFlex\Core\Service\CptCreator\CptCreatorInterface;
use BookiFlex\Core\Service\DateUnit\DateUnitFinder;
use BookiFlex\Core\Service\DateUnit\DateUnitSynchronization;
use BookiFlex\Core\Service\DateUnit\GetCalendar;
use BookiFlex\Core\Service\DateUnit\GetMinMaxDates;
use BookiFlex\Core\Service\EntityValidator;
use BookiFlex\Core\Service\NumberGenerator\NumberGenerator;
use BookiFlex\Core\Service\OfferFinder\OfferFinder;
use BookiFlex\Core\Service\Queue\ActionSchedulerQueue;
use BookiFlex\Core\Service\Queue\SyncQueue;
use BookiFlex\Core\Service\StayCostCalculator\StayCostCalculator;
use BookiFlex\Core\Service\SuitableAccommodationType\SuitableAccommodationType;
use BookiFlex\Core\Service\SuitableRatePlan\SuitableRatePlan;
use Cycle\ORM;
use Cycle\ORM\EntityManager;
use Cycle\ORM\EntityManagerInterface;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Psr\Log\LoggerInterface;

defined( 'ABSPATH' ) || exit;

class ServiceProvider implements ServiceProviderInterface
{
    /**
     * Register common services
     */
    public function register(Container $pimple): void
    {
        $pimple[QueueInterface::class] = function ($c) {
            // Use SyncQueue for testing/dev, ActionSchedulerQueue for production
            if (defined('WP_DEBUG') && WP_DEBUG && defined('BOOKIFLEX_SYNC_QUEUE') && BOOKIFLEX_SYNC_QUEUE) {
                return new SyncQueue();
            }

            return new ActionSchedulerQueue();
        };

        /**
         * Infrastructure - Storage
         */
        $pimple[OptionRepositoryInterface::class] = function ($c) {
            return new WordPressOptionRepository('bookiflex_');
        };

        $pimple[CacheRepositoryInterface::class] = function ($c) {
            return new WordPressCacheRepository('bookiflex_');
        };

        $pimple[ORM\EntityManagerInterface::class] = function ($c) {
            return new Core\ORM\EntityManagerDecorator(
                new EntityManager($c->get(ORM\ORM::class)),
                $c->get(Core\Contract\DeferrerEvent\EventsReleaser::class)
            );
        };
        $pimple[Core\Contract\DeferrerEvent\EventsReleaser::class] = function ($c) {
            return new Core\Contract\DeferrerEvent\EventsReleaser(
                $c->get('event_dispatcher')
            );
        };

        $pimple[Service\EntityValidator::class] = function ($c) {
            return new EntityValidator(
                $c->get(RatePlanRepository::class),
                $c->get(AccommodationTypeRepository::class),
            );
        };

        CarbonFieldsParser::registerNormalizer(AccommodationTypeCPT::POST_TYPE, CoreNormalizers::accommodationType(...));
        CarbonFieldsParser::registerNormalizer(RatePlanCPT::POST_TYPE,           CoreNormalizers::ratePlan(...));
        CarbonFieldsParser::registerNormalizer(CancellationPolicyCPT::POST_TYPE, CoreNormalizers::cancellationPolicy(...));

        $pimple[CustomPostTypeDataProvider::class] = function () {
            return new CustomPostTypeDataProvider(
                new CarbonFieldsParser()
            );
        };
        $pimple[OfferFinder::class] = function ($c) {
            return new OfferFinder(
                $c->get(StayCostCalculator::class),
                $c->get(SuitableRatePlan::class),
                $c->get(SuitableAccommodationType::class),
                $c->get(Service\AccommodationType\ScenarioGeneratorService::class),
                $c->get(Core\Contract\Taxation\TaxCalculatorInterface::class),
                $c->get(Core\Contract\Fee\FeeCalculatorInterface::class)
            );
        };
        $pimple[AllocationServiceInterface::class] = function () {
            return new BaseAllocationService();
        };
        $pimple[ScenarioGeneratorService::class] = function ($c) {
            return new ScenarioGeneratorService(
                $c->get(AllocationServiceInterface::class)
            );
        };
        $pimple[Core\Model\DateUnit\Calculator\PriceCalculatorFactory::class] = function () {
            return new Core\Model\DateUnit\Calculator\PriceCalculatorFactory();
        };
        $pimple[StayCostCalculator::class] = function ($c) {
            return new StayCostCalculator(
                $c->get(EntityValidator::class),
                $c->get(DateUnitFinder::class),
                $c->get(Core\Model\DateUnit\Calculator\PriceCalculatorFactory::class),
                $c->get(AllocationServiceInterface::class),
                $c->get('config'),
                null,
                $c->get(LoggerInterface::class)
            );
        };
        $pimple[DateUnitFinder::class] = function ($c) {
            return new DateUnitFinder(
                $c->get(DateUnitRepository::class),
                $c->get(RatePlanRepository::class),
                $c->get(RestrictionsChecker::class)
            );
        };
        $pimple[RestrictionsChecker::class] = function () {
            return new RestrictionsChecker();
        };

        $pimple[SuitableRatePlan::class] = function ($c) {
            return new SuitableRatePlan(
                $c->get(RatePlanRepository::class)
            );
        };
        $pimple[SuitableAccommodationType::class] = function ($c) {
            return new SuitableAccommodationType(
                $c->get(AccommodationTypeRepository::class),
                $c->get(AllocationServiceInterface::class),
                $c->get(Core\Contract\AvailabilityChecker\QuotaAvailabilityInterface::class)
            );
        };

        $pimple[AccommodationCapacityFinder::class] = function ($c) {
            return new AccommodationCapacityFinder(
                $c->get(AccommodationTypeRepository::class)
            );
        };

        $pimple[Service\Reservation\ReservationDetails::class] = function ($c) {
            return new Service\Reservation\ReservationDetails(
                $c->get(ReservationRepository::class),
                $c->get(RatePlanRepository::class),
                $c->get(AccommodationTypeRepository::class),
                $c->get(Core\Contract\Payment\PaymentAllocationProviderInterface::class)
            );
        };

        $pimple[AvailabilityChecker::class] = function ($c) {
            return new AvailabilityChecker(
                $c->get(AccommodationTypeRepository::class),
                $c->get(RatePlanRepository::class),
                $c->get(DateUnitFinder::class),
                $c->get(AllocationServiceInterface::class),
                $c->get(Core\Contract\AvailabilityChecker\QuotaAvailabilityInterface::class)
            );
        };
        $pimple[NumberGenerator::class] = function () {
            return new NumberGenerator();
        };
        $pimple[Core\Service\Reservation\ReservationStatusManager::class] = function ($c) {
            return new Core\Service\Reservation\ReservationStatusManager(
                $c->get(ReservationRepository::class),
                $c->get(ORM\EntityManagerInterface::class)
            );
        };

        $pimple[Core\Service\Reservation\ReservationListService::class] = function ($c) {
            return new Core\Service\Reservation\ReservationListService(
                $c->get(ReservationRepository::class),
                $c->offsetExists(Core\Contract\Payment\PaymentAllocationProviderInterface::class)
                    ? $c->get(Core\Contract\Payment\PaymentAllocationProviderInterface::class)
                    : null
            );
        };

        $pimple[Core\Service\Customer\CustomerListService::class] = function ($c) {
            return new Core\Service\Customer\CustomerListService(
                $c->get(CustomerRepository::class)
            );
        };

        $pimple[GetCalendar::class] = function ($c) {
            return new GetCalendar(
                $c->get(EntityValidator::class),
                $c->get(DateUnitFinder::class),
                $c->get(GetMinMaxDates::class)
            );
        };
        $pimple[GetMinMaxDates::class] = function ($c) {
            return new GetMinMaxDates(
                $c->get(DateUnitRepository::class)
            );
        };
        $pimple[Core\Util\DateRange\DateRangeCreator::class] = function ($c) {
            return new Core\Util\DateRange\DateRangeCreator();
        };
        $pimple[Core\Service\DateUnit\DateUnitSynchronization::class] = function ($c) {
            return new DateUnitSynchronization(
                $c->get(EntityValidator::class),
                $c->get(Core\Service\DateUnit\DateUnitExtender::class),
                $c->get(GetMinMaxDates::class)
            );
        };
        $pimple[Service\DateUnit\DateUnitCreator::class] = function ($c) {
            return new Core\Service\DateUnit\DateUnitCreator(
                $c->get(Service\DateUnit\DateUnitExtender::class),
                $c->get(DateUnitFinder::class),
            );
        };
        $pimple[Core\Service\DateUnit\DateUnitExtender::class] = function ($c) {
            return new Core\Service\DateUnit\DateUnitExtender(
                $c->get(Core\Util\DateRange\DateRangeCreator::class),
                $c->get(GetMinMaxDates::class),
                $c->get(DateUnitRepository::class),
                $c->get(EntityManagerInterface::class)
            );
        };
        $pimple[Core\Service\DateUnit\DateUnitManager::class] = function ($c) {
            return new Core\Service\DateUnit\DateUnitManager(
                $c->get(EntityValidator::class),
                $c->get(Service\DateUnit\DateUnitCreator::class),
                $c->get(EntityManagerInterface::class)
            );
        };


        /**
         * Repositories
         */
        $pimple[DateUnitRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(Core\Model\DateUnit\DateUnit::class);
        };
        $pimple[RatePlanRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(RatePlan::class);
        };
        $pimple[AccommodationTypeRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(AccommodationType::class);
        };
        $pimple[CustomerRepository::class] = function ($c) {
            $repository = $c->get(ORM\ORM::class)->getRepository(Core\Model\Customer\Customer::class);
	        $repository->setEntityManager($c->get(EntityManagerInterface::class));
	        return $repository;
        };
        $pimple[PaymentTypeRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(PaymentType::class);
        };
        $pimple[ReservationRepository::class] = function ($c) {
            $repository = $c->get(ORM\ORM::class)->getRepository(Reservation::class);
			$repository->setEntityManager($c->get(EntityManagerInterface::class));
			return $repository;
        };
        $pimple[ReservationAccommodationItemRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(ReservationAccommodationItem::class);
        };
        $pimple[ReservationExtraItemRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(ReservationExtraItem::class);
        };
        $pimple[ReservationAdjustmentRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(Adjustment::class);
        };
        $pimple[ReservationGroupRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(GroupReservation::class);
        };
        $pimple[CancellationPolicyRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(CancellationPolicy::class);
        };
        $pimple[Core\Repository\PenaltyRepository::class] = function ($c) {
            return $c->get(ORM\ORM::class)->getRepository(Core\Model\Penalty\Penalty::class);
        };

        /**
         * Controllers
         */
        $pimple[Core\Api\Controller\AccountController::class] = function ($c) {
            return new Core\Api\Controller\AccountController(
                $c->get(Service\Reservation\ReservationDetails::class),
                $c->get(Service\Reservation\ReservationStatusManager::class)
            );
        };
        $pimple[Core\Api\Controller\AccommodationTypeController::class] = function ($c) {
            return new Core\Api\Controller\AccommodationTypeController(
                $c->get(AccommodationTypeRepository::class),
                $c->get(Core\Api\Transformer\AccommodationTypeTransformer::class)
            );
        };
        $pimple[Core\Api\Controller\DateUnitController::class] = function ($c) {
            return new Core\Api\Controller\DateUnitController(
                $c->get(GetCalendar::class),
                $c->get(Core\Service\DateUnit\DateUnitSynchronization::class),
                $c->get(Core\Service\DateUnit\DateUnitManager::class),
                $c->get('config')
            );
        };
        $pimple[Core\Api\Transformer\Offer\OffersTransformer::class] = function () {
            return new Core\Api\Transformer\Offer\OffersTransformer();
        };

        $pimple[Core\Api\Controller\OfferController::class] = function ($c) {
            return new Core\Api\Controller\OfferController(
                $c->get(OfferFinder::class),
                $c->get('config'),
                $c->get(Core\Api\Transformer\Offer\OffersTransformer::class),
            );
        };
        $pimple[Core\Api\Controller\PricingController::class] = function ($c) {
            return new Core\Api\Controller\PricingController(
                $c->get(OfferFinder::class)
            );
        };
        $pimple[Core\Api\Controller\RatePlanController::class] = function ($c) {
            return new Core\Api\Controller\RatePlanController();
        };
        $pimple[Core\Api\Controller\ReservationController::class] = function ($c) {
            return new Core\Api\Controller\ReservationController(
                $c->get(Core\Service\Reservation\ReservationDetails::class),
                $c->get(Core\Service\Reservation\ReservationStatusManager::class),
                $c->get(Service\Reservation\ReservationService::class)
            );
        };

        /**
         * Transformers
         */
        $pimple[Core\Api\Transformer\AccommodationTypeTransformer::class] = function ($c) {
            return new Core\Api\Transformer\AccommodationTypeTransformer();
        };

        /**
         * Services
         */
        $pimple[CptCreatorInterface::class] = function ($c) {
            return new CptCreator();
        };

        $pimple[Core\Service\Reservation\ReservationService::class] = function ($c) {
            return new Core\Service\Reservation\ReservationService(
                $c->get(ReservationRepository::class),
                $c->get(PenaltyRepository::class),
                $c->get(EntityManagerInterface::class),
                $c->get(Core\Contract\Payment\PaymentAllocationProviderInterface::class)
            );
        };

        /**
         * Not instantiable classes
         */
        $pimple[Core\Contract\Taxation\TaxCalculatorInterface::class] = function () {
            return null;
        };
        $pimple[Core\Contract\Fee\FeeCalculatorInterface::class] = function () {
            return null;
        };
        $pimple[Core\Contract\AvailabilityChecker\QuotaAvailabilityInterface::class] = function () {
            return null;
        };
        $pimple[Core\Contract\Payment\PaymentFactoryInterface::class] = function () {
            return null;
        };
        $pimple[TaxRuleProviderInterface::class] = function () {
            return null;
        };
        $pimple[Core\Contract\Payment\PaymentAllocationProviderInterface::class] = function () {
            return null;
        };
        $pimple[Core\Contract\Payment\PaymentGatewayManagerInterface::class] = null;
    }
}
