<?php

namespace SugarCalendarBookings\Services\Admin;

use SugarCalendarBookings\Admin\Settings\AvailabilitySettingsTab;
use SugarCalendarBookings\Admin\Settings\EmailsSettingsTab;
use SugarCalendarBookings\Admin\Settings\IntegrationsSettingsTab;
use SugarCalendarBookings\Admin\Settings\PaymentsSettingsTab;
use SugarCalendarBookings\Availability\WorkingScheduleDefaults;
use SugarCalendarBookings\Integrations\IntegrationCapabilityRegistry;
use SugarCalendarBookings\Integrations\MeetingProviderInterface;
use SugarCalendarBookings\Locations\OnlineLocationProviderFactory;
use SugarCalendarBookings\Locations\LocationModel;
use SugarCalendarBookings\Notifications\Emails\EmailDefaults;
use SugarCalendarBookings\Notifications\Emails\EmailService;
use SugarCalendarBookings\Options;
use SugarCalendarBookings\Payments\Gateways\PaymentGatewayRegistry;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Services\ServiceModel;
use SugarCalendarBookings\Support\Helpers\Time;
use SugarCalendarBookings\Support\Tags\TagModel;

/**
 * Service for adding new and editing service form.
 *
 * @since 1.0.0
 */
class ServiceFormService {

	/**
	 * Get sanitization and validation rules.
	 *
	 * @since 1.0.0
	 *
	 * @param array $additional_rules Additional rules.
	 *
	 * @return array
	 */
	public function rules( $additional_rules = [] ) {

		return array_merge(
			[
				'name'                        => 'required|string|max:255',
				'color'                       => 'required|string',
				'duration'                    => 'required|numeric|min:1,max:720',
				'locations'                   => 'array',
				'locations.*.id'              => 'required|id',
				'locations.*.order'           => 'required|numeric',
				'online_locations'            => 'array',
				'online_locations.*.uid'      => 'string',
				'online_locations.*.order'    => 'required|numeric',
				'online_locations.*.provider' => 'required|string|in:' . implode( ',', OnlineLocationProviderFactory::get_registered_slugs() ),
				'online_locations.*.url'      => 'required_if:provider,custom_link|string',
				'price'                       => 'nullable|numeric|min:0',
				'tags'                        => 'array',
				'tags.*'                      => 'id',
				'description'                 => 'html|max:1000',
				'feature_image_id'            => 'nullable|id',
				'enabled'                     => 'required|boolean',
				'payment_gateways'            => 'array|min:1',
				'payment_gateways.*'          => 'string',
			],
			$additional_rules
		);
	}

	/**
	 * Get form meta data.
	 *
	 * @since 1.0.0
	 * @since 1.2.1 Add availability settings URL to meta data.
	 * @since 1.3.1 Add availability schedule metadata to meta data.
	 *
	 * @return array
	 */
	protected function get_form_meta_data() {

		// TODO: Add limit and implement load more functionality in the select.
		$tags = TagModel::query()
			->where( 'object_type', '=', 'service', '%s' )
			->order_by( 'name' )
			->get();

		$locations = LocationModel::query()
			->order_by( 'name' )
			->get();

		$options = Plugin::get( Options::class );

		$data = [
			'tags'                          => array_map(
				fn( $tag ) => [
					'id'   => $tag->id,
					'name' => $tag->name,
				],
				$tags
			),
			'locations'                     => array_map(
				fn( $location ) => [
					'id'   => $location->id,
					'name' => $location->name,
				],
				$locations
			),
			'payment_gateways'              => array_map(
				function ( $gateway ) use ( $options ) {
					$gateway_id = $gateway::get_id();

					return [
						'id'          => $gateway_id,
						'name'        => $gateway::get_name(),
						'configured'  => $gateway->is_configured(),
						'recommended' => $gateway->is_recommended(),
						'enabled'     => (bool) $options->get( "payments.gateways.{$gateway_id}.enabled" ),
					];
				},
				array_values( Plugin::get( PaymentGatewayRegistry::class )->get_registered() )
			),
			'payment_gateway_setup_url'     => PaymentsSettingsTab::get_url(),
			'default_emails'                => EmailDefaults::to_array(),
			'email_types_config'            => Plugin::get( EmailService::class )->get_email_notifications(),
			'availability_settings_url'     => AvailabilitySettingsTab::get_url(),
			'email_settings_url'            => EmailsSettingsTab::get_url(),
			'meeting_providers'             => $this->get_meeting_providers(),

			// Availability schedule metadata for the Default Availability display.
			'default_availability_schedule' => $options->get( 'availability.working_schedule.regular' ),
			'default_timezone'              => WorkingScheduleDefaults::get_timezone(),
			'time_format'                   => $options->get( 'display.time_format' ),
		];

		return $data;
	}

	/**
	 * Get data for service creation form.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_create_form_data() {

		return [
			'meta' => $this->get_form_meta_data(),
		];
	}

	/**
	 * Get data for service edit form.
	 *
	 * @since 1.0.0
	 *
	 * @param ServiceModel $service Service model instance.
	 *
	 * @return array
	 */
	public function get_edit_form_data( $service ) {

		$locations = array_map(
			fn( $location ) => [
				'id'    => $location->id,
				'order' => (int) $location->pivot->location_order,
			],
			$service->locations()->select( [ 'id' ] )->get()
		);

		$tags = array_map( fn( $tag ) => $tag->id, $service->tags()->select( [ 'id' ] )->get() );

		$feature_image_id   = $service->feature_image_id;
		$feature_image_url  = null;
		$feature_image_name = null;

		if ( $feature_image_id ) {
			$feature_image      = wp_get_attachment_image_src( $feature_image_id );
			$feature_image_url  = $feature_image[0] ?? null;
			$feature_image_name = $feature_image !== false ? get_the_title( $feature_image_id ) : null;
		}

		$duration = Time::convert_duration( $service->duration );

		$data = [
			'id'                 => $service->id,
			'name'               => $service->name,
			'color'              => $service->color,
			'duration'           => $duration['value'],
			'duration_unit'      => $duration['unit'],
			'locations'          => $locations,
			'online_locations'   => $service->online_locations,
			'price'              => $service->price,
			'tags'               => $tags,
			'description'        => $service->description,
			'feature_image_id'   => $feature_image_id,
			'feature_image_url'  => $feature_image_url,
			'feature_image_name' => $feature_image_name,
			'enabled'            => $service->status === 'enabled',
			'payment_gateways'   => $service->payment_gateways,
		];

		return [
			'service' => $data,
			'meta'    => $this->get_form_meta_data(),
		];
	}

	/**
	 * Get available meeting providers for the service form.
	 *
	 * @since 1.4.0
	 *
	 * @return array
	 */
	private function get_meeting_providers(): array {

		$registry = Plugin::get( IntegrationCapabilityRegistry::class );

		$integrations_url = IntegrationsSettingsTab::get_url();

		return array_values(
			array_map(
				fn( MeetingProviderInterface $provider ) => [
					'slug'         => $provider->get_provider_slug(),
					'name'         => $provider->get_display_name(),
					'connected'    => $provider->is_available(),
					'settings_url' => add_query_arg( 'integration', $provider->get_provider_slug(), $integrations_url ),
				],
				$registry->get( MeetingProviderInterface::class )
			)
		);
	}
}
