<?php

namespace SugarCalendarBookings;

use SugarCalendarBookings\Abilities\AbilityRegistrar;
use SugarCalendarBookings\Abilities\AbilityResponseFormatter;
use SugarCalendarBookings\Abilities\Appointments\GetAppointmentAbility;
use SugarCalendarBookings\Abilities\Appointments\GetAppointmentStatsAbility;
use SugarCalendarBookings\Abilities\Appointments\ListAppointmentsAbility;
use SugarCalendarBookings\Abilities\Customers\ListCustomersAbility;
use SugarCalendarBookings\Abilities\Services\GetServiceAbility;
use SugarCalendarBookings\Abilities\Services\ListServicesAbility;
use SugarCalendarBookings\Admin\AdminMenu;
use SugarCalendarBookings\Admin\Area;
use SugarCalendarBookings\Admin\DashboardWidget;
use SugarCalendarBookings\Admin\Settings\IntegrationsController;
use SugarCalendarBookings\Admin\Settings\MiscSettingsTab;
use SugarCalendarBookings\Admin\Education;
use SugarCalendarBookings\Admin\GlobalQueryNotices;
use SugarCalendarBookings\Ajax\AdminRoutes;
use SugarCalendarBookings\Ajax\CommonRoutes;
use SugarCalendarBookings\Ajax\FrontendRoutes;
use SugarCalendarBookings\Antispam\AntispamService;
use SugarCalendarBookings\Appointments\AppointmentPaymentTableMigration;
use SugarCalendarBookings\Appointments\AppointmentsTableMigration;
use SugarCalendarBookings\Appointments\CancellationHandler;
use SugarCalendarBookings\Appointments\CancellationTokenService;
use SugarCalendarBookings\Appointments\Tasks\DraftAppointmentsCleanupTask;
use SugarCalendarBookings\Availability\Tasks\DateOverridesCleanupTask;
use SugarCalendarBookings\BookingForm\BookingFormAssets;
use SugarCalendarBookings\BookingForm\BookingFormBlock;
use SugarCalendarBookings\BookingForm\BookingFormElementorWidget;
use SugarCalendarBookings\BookingForm\BookingFormShortcode;
use SugarCalendarBookings\Customers\CustomersTableMigration;
use SugarCalendarBookings\Employees\EmployeesTableMigration;
use SugarCalendarBookings\Integrations\Credits\CreditAlertEmail;
use SugarCalendarBookings\Integrations\Credits\CreditsService;
use SugarCalendarBookings\Integrations\ExternalBusyTimes\ExternalBusyTimeConflictChecker;
use SugarCalendarBookings\Integrations\ExternalBusyTimes\ExternalBusyTimeService;
use SugarCalendarBookings\Integrations\ExternalBusyTimes\ExternalBusyTimesTableMigration;
use SugarCalendarBookings\Integrations\Webhooks\IncomingWebhookHandler;
use SugarCalendarBookings\Integrations\Webhooks\WebhookSubscriptionsTableMigration;
use SugarCalendarBookings\Integrations\Webhooks\WebhookUrlMonitor;
use SugarCalendarBookings\Integrations\IntegrationCapabilityRegistry;
use SugarCalendarBookings\Integrations\IntegrationRegistry;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthConnectionErrorNotice;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthConnectionsTableMigration;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthRelayClient;
use SugarCalendarBookings\Integrations\Outlook\OutlookActionsClient;
use SugarCalendarBookings\Integrations\Outlook\Admin\OutlookSettingsRenderer;
use SugarCalendarBookings\Integrations\Outlook\OutlookBusyTimeService;
use SugarCalendarBookings\Integrations\Outlook\OutlookCalendarSettingsRepository;
use SugarCalendarBookings\Integrations\Outlook\OutlookEventPayloadBuilder;
use SugarCalendarBookings\Integrations\Outlook\OutlookIntegration;
use SugarCalendarBookings\Integrations\Outlook\OutlookOAuthCallbackHandler;
use SugarCalendarBookings\Integrations\Outlook\OutlookSmartTagPicker;
use SugarCalendarBookings\Integrations\Outlook\OutlookSubscriptionManager;
use SugarCalendarBookings\Integrations\Outlook\OutlookWebhookEventHandler;
use SugarCalendarBookings\Integrations\Zoom\ZoomActionsClient;
use SugarCalendarBookings\Integrations\Zoom\ZoomIntegration;
use SugarCalendarBookings\Integrations\Zoom\ZoomWebhookEventHandler;
use SugarCalendarBookings\Integrations\CalendarEventManager;
use SugarCalendarBookings\Integrations\CalendarEvents\CalendarEventDispatcher;
use SugarCalendarBookings\Integrations\CalendarEvents\DeliverableConnectionResolver;
use SugarCalendarBookings\Integrations\CalendarEvents\EventLinkDiffer;
use SugarCalendarBookings\Integrations\CalendarEvents\EventLinkRepository;
use SugarCalendarBookings\Integrations\MeetingManager;
use SugarCalendarBookings\Integrations\MeetingProviderCapabilityResolver;
use SugarCalendarBookings\Integrations\Zoom\ZoomOAuthCallbackHandler;
use SugarCalendarBookings\Locations\LocationsTableMigration;
use SugarCalendarBookings\Notifications\Emails\ReminderProvider;
use SugarCalendarBookings\Payments\Admin\PaymentMethodsSection;
use SugarCalendarBookings\Payments\Gateways\Onsite\OnSitePaymentGateway;
use SugarCalendarBookings\Payments\Gateways\Onsite\OnSiteProvider;
use SugarCalendarBookings\Payments\Gateways\PaymentGatewayRegistry;
use SugarCalendarBookings\Payments\Gateways\Stripe\StripePaymentGateway;
use SugarCalendarBookings\Payments\Gateways\Stripe\StripeProvider;
use SugarCalendarBookings\Payments\PaymentItemsTableMigration;
use SugarCalendarBookings\Payments\PaymentsTableMigration;
use SugarCalendarBookings\Services\Admin\EmbedServiceEditorScript;
use SugarCalendarBookings\Services\EmployeeServiceTableMigration;
use SugarCalendarBookings\Services\LocationServiceTableMigration;
use SugarCalendarBookings\Services\ServiceProvider;
use SugarCalendarBookings\Services\ServicesTableMigration;
use SugarCalendarBookings\Services\UnbookableServicesDisabler;
use SugarCalendarBookings\SetupWizard\MigrationHydrator;
use SugarCalendarBookings\SetupWizard\SetupWizard;
use SugarCalendarBookings\Support\AbstractLoader;
use SugarCalendarBookings\Support\Activities\ActivitiesTableMigration;
use SugarCalendarBookings\Support\Admin\Notices;
use SugarCalendarBookings\Support\License\Connect;
use SugarCalendarBookings\Support\Ajax\RequestHandler;
use SugarCalendarBookings\Support\Ajax\RouteRegistry;
use SugarCalendarBookings\Support\Blocks\BlocksRegistry;
use SugarCalendarBookings\Support\Bootable;
use SugarCalendarBookings\Support\Database\Database;
use SugarCalendarBookings\Support\Database\MetaTableMigration;
use SugarCalendarBookings\Support\ElementorWidgets\ElementorWidgetRegistry;
use SugarCalendarBookings\Support\License\License;
use SugarCalendarBookings\Support\Helpers\Helpers;
use SugarCalendarBookings\Support\Shortcodes\ShortcodeRegistry;
use SugarCalendarBookings\Support\Tags\TagRelationshipsTableMigration;
use SugarCalendarBookings\Support\Tags\TagsTableMigration;
use SugarCalendarBookings\Support\Tasks\TaskRegistry;
use SugarCalendarBookings\Vendor\ProductApi\ProductApi;
use SugarCalendarBookings\UsageTracking\SendUsageTask;
use SugarCalendarBookings\UsageTracking\UsageTracking;
use SugarCalendarBookings\Migration\AmeliaMigrationNotice;
use SugarCalendarBookings\Migration\Commands\CommandRegistry;
use SugarCalendarBookings\Migration\ImageMigrationHandler;
use SugarCalendarBookings\Migration\MigrationRegistry;
use SugarCalendarBookings\Migration\MigrationService;
use SugarCalendarBookings\Migration\SetupWizardMigrationHandler;
use SugarCalendarBookings\Migration\Sources\Amelia\AmeliaAdapter;
use SugarCalendarBookings\Migration\Sources\Amelia\AmeliaUIConfig;
use SugarCalendarBookings\Migration\Sources\Amelia\Mappers\AppointmentMapper;
use SugarCalendarBookings\Migration\Sources\Amelia\Mappers\CustomerMapper;
use SugarCalendarBookings\Migration\Sources\Amelia\Mappers\LocationMapper;
use SugarCalendarBookings\Migration\Sources\Amelia\Mappers\ServiceMapper;
use SugarCalendarBookings\Calendar\CalendarDisplayRangeService;

/**
 * Loader.
 *
 * @since 1.0.0
 */
class Loader extends AbstractLoader {

	/**
	 * Get the classes that should be registered with the container and initialized.
	 *
	 * @since 1.0.0
	 *
	 * @return array<class-string<Bootable>, (callable|null)> Keys are the class names, values are the builders.
	 */
	public function provides(): array {

		return [
			Database::class                          => null,
			Area::class                              => null,
			AdminMenu::class                         => null,
			DashboardWidget::class                   => null,
			Notices::class                           => null,
			Connect::class                           => null,
			BlocksRegistry::class                    => null,
			ShortcodeRegistry::class                 => null,
			ElementorWidgetRegistry::class           => null,
			BookingFormAssets::class                 => null,
			RequestHandler::class                    => null,
			AdminRoutes::class                       => fn() => new AdminRoutes( $this->get( RouteRegistry::class ) ),
			FrontendRoutes::class                    => fn() => new FrontendRoutes( $this->get( RouteRegistry::class ) ),
			CommonRoutes::class                      => fn() => new CommonRoutes( $this->get( RouteRegistry::class ) ),
			GlobalQueryNotices::class                => fn() => new GlobalQueryNotices( $this->get( Notices::class ) ),
			ReminderProvider::class                  => null,
			Education::class                         => null,
			CommandRegistry::class                   => null,
			PaymentMethodsSection::class             => null,
			OnSiteProvider::class                    => fn() => new OnSiteProvider( $this->get( OnSitePaymentGateway::class ) ),
			StripeProvider::class                    => fn() => new StripeProvider( $this->get( StripePaymentGateway::class ) ),
			TaskRegistry::class                      => null,
			ServiceProvider::class                   => null,
			EmbedServiceEditorScript::class          => null,
			UnbookableServicesDisabler::class        => null,
			CancellationHandler::class               => null,
			CancellationTokenService::class          => null,
			SetupWizard::class                       => null,
			MigrationHydrator::class                 => fn() => new MigrationHydrator( $this->get( MigrationRegistry::class ) ),
			SetupWizardMigrationHandler::class       => fn() => new SetupWizardMigrationHandler(
				$this->get( MigrationService::class ),
				$this->get( MigrationRegistry::class )
			),
			License::class                           => null,

			IntegrationRegistry::class               => null,
			IntegrationCapabilityRegistry::class     => null,
			AbilityResponseFormatter::class          => null,
			AbilityRegistrar::class                  => fn() => new AbilityRegistrar(
				[
					ListAppointmentsAbility::class,
					GetAppointmentAbility::class,
					GetAppointmentStatsAbility::class,
					ListServicesAbility::class,
					GetServiceAbility::class,
					ListCustomersAbility::class,
				]
			),
			ListAppointmentsAbility::class           => null,
			GetAppointmentAbility::class             => null,
			GetAppointmentStatsAbility::class        => null,
			ListServicesAbility::class               => null,
			GetServiceAbility::class                 => null,
			ListCustomersAbility::class              => null,
			ZoomIntegration::class                   => fn() => new ZoomIntegration(
				new OAuthRelayClient(),
				$this->get( ZoomActionsClient::class )
			),
			ZoomActionsClient::class                 => null,
			OutlookSmartTagPicker::class             => fn() => new OutlookSmartTagPicker(
				$this->get( Options::class )
			),
			OutlookEventPayloadBuilder::class        => fn() => new OutlookEventPayloadBuilder(
				$this->get( OutlookSmartTagPicker::class ),
				$this->get( Options::class )
			),
			OutlookCalendarSettingsRepository::class => fn() => new OutlookCalendarSettingsRepository(
				$this->get( OutlookActionsClient::class )
			),
			OutlookBusyTimeService::class            => fn() => new OutlookBusyTimeService(
				$this->get( OutlookActionsClient::class ),
				$this->get( OutlookCalendarSettingsRepository::class )
			),
			OutlookSettingsRenderer::class           => fn() => new OutlookSettingsRenderer(
				$this->get( OutlookCalendarSettingsRepository::class ),
				$this->get( OutlookSmartTagPicker::class )
			),
			OutlookIntegration::class                => fn() => new OutlookIntegration(
				new OAuthRelayClient(),
				$this->get( OutlookActionsClient::class ),
				$this->get( OutlookEventPayloadBuilder::class ),
				$this->get( OutlookBusyTimeService::class ),
				$this->get( OutlookCalendarSettingsRepository::class ),
				$this->get( OutlookSmartTagPicker::class ),
				$this->get( OutlookSettingsRenderer::class )
			),
			OutlookActionsClient::class              => null,
			OutlookSubscriptionManager::class        => fn() => new OutlookSubscriptionManager(
				$this->get( OutlookActionsClient::class ),
				$this->get( OutlookCalendarSettingsRepository::class )
			),
			OutlookOAuthCallbackHandler::class       => fn() => new OutlookOAuthCallbackHandler(
				new OAuthRelayClient(),
				$this->get( OutlookSubscriptionManager::class )
			),
			OutlookWebhookEventHandler::class        => fn() => new OutlookWebhookEventHandler(
				$this->get( OutlookActionsClient::class ),
				$this->get( OutlookCalendarSettingsRepository::class )
			),
			EventLinkRepository::class               => null,
			EventLinkDiffer::class                   => null,
			DeliverableConnectionResolver::class     => fn() => new DeliverableConnectionResolver(
				$this->get( IntegrationCapabilityRegistry::class )
			),
			CalendarEventDispatcher::class           => fn() => new CalendarEventDispatcher(
				$this->get( IntegrationCapabilityRegistry::class )
			),
			CalendarEventManager::class              => fn() => new CalendarEventManager(
				$this->get( IntegrationCapabilityRegistry::class ),
				$this->get( EventLinkRepository::class ),
				$this->get( DeliverableConnectionResolver::class ),
				$this->get( EventLinkDiffer::class ),
				$this->get( CalendarEventDispatcher::class )
			),
			MeetingManager::class                    => fn() => new MeetingManager(
				$this->get( IntegrationCapabilityRegistry::class )
			),
			MeetingProviderCapabilityResolver::class => null,
			ZoomOAuthCallbackHandler::class          => fn() => new ZoomOAuthCallbackHandler( new OAuthRelayClient() ),
			ExternalBusyTimeService::class           => null,
			ExternalBusyTimeConflictChecker::class   => null,
			IncomingWebhookHandler::class            => null,
			WebhookUrlMonitor::class                 => fn() => new WebhookUrlMonitor(
				$this->get( IntegrationCapabilityRegistry::class ),
				$this->get( Notices::class ),
				$this->get( Area::class )
			),
			ZoomWebhookEventHandler::class           => null,
			CreditsService::class                    => null,
			CreditAlertEmail::class                  => null,
			OAuthConnectionErrorNotice::class        => fn() => new OAuthConnectionErrorNotice(
				$this->get( Notices::class )
			),

			// Migration service and registry.
			MigrationRegistry::class                 => null,
			MigrationService::class                  => null,
			ImageMigrationHandler::class             => null,
			AmeliaMigrationNotice::class             => fn() => new AmeliaMigrationNotice(
				$this->get( Notices::class ),
				$this->get( Options::class ),
				$this->get( Area::class )
			),
			ServiceMapper::class                     => fn() => new ServiceMapper( $this->get( ImageMigrationHandler::class ) ),
			CustomerMapper::class                    => null,
			LocationMapper::class                    => null,
			AppointmentMapper::class                 => null,
			CalendarDisplayRangeService::class       => null,
			UsageTracking::class                     => null,
			MiscSettingsTab::class                   => null,
			IntegrationsController::class            => null,
			AntispamService::class                   => null,
		];
	}

	/**
	 * Boot the loader.
	 *
	 * @since 1.0.0
	 */
	public function boot() {

		// Register migrations.
		$this->get( Database::class )->register_migrations(
			[
				ActivitiesTableMigration::class,
				AppointmentsTableMigration::class,
				AppointmentPaymentTableMigration::class,
				CustomersTableMigration::class,
				EmployeeServiceTableMigration::class,
				EmployeesTableMigration::class,
				MetaTableMigration::class,
				PaymentsTableMigration::class,
				PaymentItemsTableMigration::class,
				ServicesTableMigration::class,
				TagRelationshipsTableMigration::class,
				TagsTableMigration::class,
				LocationsTableMigration::class,
				LocationServiceTableMigration::class,
				OAuthConnectionsTableMigration::class,
				ExternalBusyTimesTableMigration::class,
				WebhookSubscriptionsTableMigration::class,
			]
		);

		// Register shortcodes.
		$this->get( ShortcodeRegistry::class )->register_shortcodes(
			[
				BookingFormShortcode::class,
			]
		);

		// Register blocks.
		$this->get( BlocksRegistry::class )->register_blocks(
			[
				BookingFormBlock::class,
			]
		);

		// Register Elementor widgets.
		$this->get( ElementorWidgetRegistry::class )->register_widgets(
			[
				BookingFormElementorWidget::class,
			]
		);

		// Register tasks.
		$this->get( TaskRegistry::class )->register_tasks(
			[
				DraftAppointmentsCleanupTask::class,
				DateOverridesCleanupTask::class,
				SendUsageTask::class,
			]
		);

		// Register payment gateways.
		$this->get( PaymentGatewayRegistry::class )->register_gateways(
			[
				OnSitePaymentGateway::class,
				StripePaymentGateway::class,
			]
		);

		ProductApi::configure(
			[
				'api_url'        => defined( 'SCBOOKINGS_PRODUCT_API_BASE_URL' ) ? SCBOOKINGS_PRODUCT_API_BASE_URL : 'https://bookings.sugarcalendarapi.com',
				'site_url'       => get_option( 'siteurl' ),
				'license_key'    => $this->get( Options::class )->get( 'license.key' ),
				'license_valid'  => $this->get( License::class )->is_valid(),
				'is_pro'         => sugar_calendar_bookings()->is_pro(),
				'user_agent'     => Helpers::get_default_user_agent(),
				'environment'    => wp_get_environment_type(),
				'plugin_slug'    => 'sugar-calendar-bookings',
				'plugin_version' => SCBOOKINGS_PLUGIN_VERSION,
			]
		)->boot();

		// Register migration sources (adapters and their UI configs).
		$this->get( MigrationRegistry::class )->register_sources(
			[
				[
					'adapter'   => AmeliaAdapter::class,
					'ui_config' => AmeliaUIConfig::class,
				],
			]
		);
	}
}
