<?php

namespace SugarCalendarBookings\Services;

use SugarCalendarBookings\Integrations\IntegrationCapabilityRegistry;
use SugarCalendarBookings\Integrations\MeetingProviderInterface;
use SugarCalendarBookings\Plugin;

/**
 * Disables services that have become unbookable after an integration state change.
 *
 * Called after OAuth connection changes (delete/auth_error) to sweep services
 * whose only location path was an integration that is no longer available.
 * Services with at least one still-bookable location path are left untouched.
 *
 * Re-enabling stays an explicit admin action — when a provider comes back,
 * services stay disabled until the admin toggles them on themselves.
 *
 * @since 1.4.0
 */
class UnbookableServicesDisabler {

	/**
	 * Disable every enabled service whose only bookable path was the given
	 * meeting provider and that has no other bookable option left.
	 *
	 * @since 1.4.0
	 *
	 * @param string $provider_slug The slug of the provider whose state just changed.
	 *
	 * @return int Number of services that were disabled.
	 */
	public function disable_orphaned_services( string $provider_slug ): int {

		$available_slugs = $this->get_available_meeting_provider_slugs();
		$disabled_count  = 0;

		$services = ServiceModel::query()
			->with(
				[
					'locations' => fn( $q ) => $q->select( [ 'id' ] ),
					'meta'      => fn( $q ) => $q->where( 'meta_key', '=', 'online_locations', '%s' ),
				]
			)
			->where( 'status', '=', 'enabled', '%s' )
			->get();

		foreach ( $services as $service ) {
			if ( ! $this->service_uses_provider( $service, $provider_slug ) ) {
				continue;
			}

			if ( $this->service_has_bookable_location( $service, $available_slugs ) ) {
				continue;
			}

			$service->status = 'disabled';

			$service->save();

			++$disabled_count;
		}

		return $disabled_count;
	}

	/**
	 * Check whether a service references the given provider among its online locations.
	 *
	 * @since 1.4.0
	 *
	 * @param ServiceModel $service       Service model.
	 * @param string       $provider_slug Provider slug.
	 *
	 * @return bool
	 */
	private function service_uses_provider( ServiceModel $service, string $provider_slug ): bool {

		foreach ( $service->online_locations as $location ) {
			if ( $provider_slug === ( $location['provider'] ?? '' ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check whether a service still has any bookable location.
	 *
	 * Bookable paths: in-person locations, custom_link online locations,
	 * and online locations whose meeting provider is currently available.
	 *
	 * @since 1.4.0
	 *
	 * @param ServiceModel $service         Service model.
	 * @param string[]     $available_slugs Meeting provider slugs currently available.
	 *
	 * @return bool
	 */
	private function service_has_bookable_location( ServiceModel $service, array $available_slugs ): bool {

		if ( ! empty( $service->locations ) ) {
			return true;
		}

		foreach ( $service->online_locations as $location ) {
			$slug = $location['provider'] ?? '';

			if ( $slug === 'custom_link' ) {
				return true;
			}

			if ( in_array( $slug, $available_slugs, true ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Get the slugs of meeting providers currently available for booking.
	 *
	 * @since 1.4.0
	 *
	 * @return string[]
	 */
	private function get_available_meeting_provider_slugs(): array {

		$providers = Plugin::get( IntegrationCapabilityRegistry::class )->get( MeetingProviderInterface::class );

		return array_values(
			array_map(
				static fn( $provider ) => $provider->get_provider_slug(),
				array_filter(
					$providers,
					static fn( $provider ) => $provider->is_available()
				)
			)
		);
	}
}
