<?php
/**
 * Endpoint for the advanced settings interactive form.
 *
 * @package EaLab\WP_Deployer
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use EaLab\WP_Deployer\vendor\Dakwamine\Component\ComponentBasedObject;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\Config\Exclusions\ExclusionsConfigProviderManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\Config\TransmutationHandlerShortcodeReferences\TransmutationHandlerShortcodeReferencesConfigProviderManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\Config\ValueHandlerOption\ValueHandlerOptionConfigProviderManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ItemValueHandler\ItemValueHandlerBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ItemValueHandler\ValueProcessor\TransmutationHandler\TransmutationHandlerBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ItemValueHandler\ValueProcessor\ValueProcessorBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ScreenLabelFormatter\ScreenLabelFormatterBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\TrackerConfig\TrackerConfigProviderManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Database;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Plugin;
use EaLab\WP_Deployer\vendor\Symfony\Component\Yaml\Yaml;

add_action(
	'rest_api_init',
	static function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/advanced-settings/edit',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => function ( WP_REST_Request $request ) {
						$response    = array();
						$status_code = 200;

						try {
							$response = deploy_sync_content__advanced_settings_form__get_full_structure();
						} catch ( \Throwable $t ) {
							$status_code         = 500;
							$response['message'] = esc_html( $t->getMessage() );
						}

						return new WP_REST_Response( $response, $status_code );
					},
					'permission_callback' => function ( WP_REST_Request $request ) {
						// If the request is from the admin, we allow it.
						return in_array( 'administrator', wp_get_current_user()->roles ?? array(), true );
					},
				),
				array(
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => function ( WP_REST_Request $request ) {
						$response    = array();
						$status_code = 200;

						$submitted_data = json_decode( $request->get_body(), true );

						try {
							deploy_sync_content__advanced_settings_form__submit( $submitted_data );
						} catch ( \Throwable $t ) {
							// Rebuild the structure.
							$response = deploy_sync_content__advanced_settings_form__get_full_structure();

							// Set a dismissable error message.
							require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/includes/helpers.php';
							$alert_message_block = deploy_sync_content__form_structure__build_message_block( esc_html( $t->getMessage() ), 'danger' );

							$response['renderView'][1]['content'][1]['content'] = $alert_message_block;

							$status_code = 500;

							return new WP_REST_Response( $response, $status_code );
						}

						try {
							// Rebuild the structure.
							$response = deploy_sync_content__advanced_settings_form__get_full_structure();
						} catch ( \Throwable $t ) {
							// Set a dismissable error message.
							require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/includes/helpers.php';
							$alert_message_block = deploy_sync_content__form_structure__build_message_block( esc_html( $t->getMessage() ), 'danger' );

							$response['renderView'][1]['content'][1]['content'] = $alert_message_block;

							$status_code = 500;

							return new WP_REST_Response( $response, $status_code );
						}

						// Add the confirmation message.
						require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/includes/helpers.php';
						$alert_message_block = deploy_sync_content__form_structure__build_message_block( __( 'The configuration updated successfully.', 'deploy-sync-content' ), 'success' );

						$response['renderView'][1]['content'][1]['content'] = $alert_message_block;

						return new WP_REST_Response( $response, $status_code );
					},
					'permission_callback' => function ( WP_REST_Request $request ) {
						// If the request is from the admin, we allow it.
						return in_array( 'administrator', wp_get_current_user()->roles ?? array(), true );
					},
				),
			)
		);
	}
);

function deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_reactive_json_format( $field_name, &$field_definition_override ): void {
	// Add the field name as a value because reactive-json does not support data as property.
	$field_definition_override['field'] = $field_name;

	if ( 'reference:' === substr( $field_definition_override['value_handler'] ?? '', 0, 10 ) ) {
		// Transform the reference:<tracker_id> value handler
		// to "reference" + add a field containing the tracker ID.
		// This is the only exception to the common behaviour about ivh.
		// It could be a good thing to replace by a two field config
		// to simplify things.
		$reference_ivh_tracker_id = substr( $field_definition_override['value_handler'] ?? '', 10 );

		$field_definition_override['value_handler']        = 'reference';
		$field_definition_override['reference_tracker_id'] = $reference_ivh_tracker_id;
	}

	// Rework the processors to suit the reactive-json format.
	$processors                              = $field_definition_override['processors'] ?? array();
	$field_definition_override['processors'] = array();

	foreach ( $processors as $processor_definition ) {
		if ( empty( $processor_definition['id'] ) ) {
			// Ignore invalid processors.
			continue;
		}

		$field_definition_override['processors'][] = array(
			$processor_definition['id'] => $processor_definition,
		);
	}
}

function deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_config_format( array &$field_definition_override ): void {
	unset( $field_definition_override['field'] );

	if ( 'reference' === ( $field_definition_override['value_handler'] ?? null ) ) {
		$reference_tracker_id = $field_definition_override['reference_tracker_id'];

		$field_definition_override['value_handler'] = 'reference:' . $reference_tracker_id;

		unset( $field_definition_override['reference_tracker_id'] );
	}

	// Rework the processors to suit the config format.
	$processors                              = $field_definition_override['processors'] ?? array();
	$field_definition_override['processors'] = array();

	foreach ( $processors as $item ) {
		if ( empty( $item ) ) {
			continue;
		}

		$processor_id          = array_key_first( $item );
		$processor_value       = $item[ $processor_id ];
		$processor_value['id'] = $processor_id;

		ksort( $processor_value, SORT_NATURAL );

		$field_definition_override['processors'][] = $processor_value;
	}

	if ( empty( $field_definition_override['processors'] ) ) {
		// No processor set.
		unset( $field_definition_override['processors'] );
	}
}

/**
 * Gets the base structure of the advanced settings form.
 *
 * @return array The base structure.
 */
function deploy_sync_content__advanced_settings_form__get_base_structure(): array {
	$yaml = file_get_contents( __DIR__ . '/advanced-settings--form-structure.yaml' );

	$parsed = Yaml::parse( $yaml );

	// Remove the eventual test data.
	$parsed['data'] = [];

	return $parsed;
}

function deploy_sync_content__advanced_settings_form__get_full_structure(): array {
	$build = deploy_sync_content__advanced_settings_form__get_base_structure();

	deploy_sync_content__advanced_settings_form__insert_exclusions_data( $build );
	deploy_sync_content__advanced_settings_form__insert_ivh_structures( $build );
	deploy_sync_content__advanced_settings_form__insert_plugin_data( $build );
	deploy_sync_content__advanced_settings_form__insert_processor_structures( $build );
	deploy_sync_content__advanced_settings_form__insert_screen_label_formatter_build( $build );
	deploy_sync_content__advanced_settings_form__insert_shortcodes_data( $build );
	deploy_sync_content__advanced_settings_form__insert_tracker_data( $build );
	deploy_sync_content__advanced_settings_form__insert_transmutation_handler_structures( $build );
	deploy_sync_content__advanced_settings_form__insert_value_handler_option_data( $build );

	$build['data']['submitEndpoint'] = get_rest_url(
		null,
		'deploy-sync-content/v1/advanced-settings/edit'
	);

	$build['data']['isProduction'] = \EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Environment::isProductionEnvironment();

	return $build;
}

function deploy_sync_content__advanced_settings_form__get_tracker_schema_item( $tracker_id, $maybe_tracker_config ): array {
	$data = array();

	// Will totally deconfigure the tracker if false on submit.
	$data['isEnabled'] = ! empty( $maybe_tracker_config );

	$data['tracker_id']             = $tracker_id;
	$data['plugin']                 = $maybe_tracker_config['plugin'] ?? '';
	$data['uuid_random_generation'] = $maybe_tracker_config['uuid_random_generation'] ?? false;

	if ( ! empty( $maybe_tracker_config['tracker_label'] ) ) {
		$data['tracker_label'] = $maybe_tracker_config['tracker_label'];
	}

	if ( ! empty( $maybe_tracker_config['excluded'] ) ) {
		$data['excluded'] = true;
	}

	if ( ! empty( $maybe_tracker_config['screen_label_formatter'] ) ) {
		$data['screen_label_formatter'] = $maybe_tracker_config['screen_label_formatter'];
	}

	// Add the unset option.
	$data['_meta']['column_names'][] = array(
		'label' => 'Select a field...',
		'value' => '',
	);

	// Get all the fields (columns) of this tracker (table).
	// We keep the field order given by the database.
	// It's only when saving the config that we sort by name in the final config.
	$tracker_field_names = Database::getAllColumnNames( Database::prependDatabasePrefix( $tracker_id ) );

	foreach ( $tracker_field_names as $tracker_field_name ) {
		// Used by select dynamic options.
		$data['_meta']['column_names'][] = array(
			'label' => $tracker_field_name,
			'value' => $tracker_field_name,
		);

		// Retrieve the existing config if any.
		if ( ! empty( $maybe_tracker_config['field_definitions_overrides'][ $tracker_field_name ] ) ) {
			$field_definition_override = $maybe_tracker_config['field_definitions_overrides'][ $tracker_field_name ];
		} else {
			$field_definition_override = array();
		}

		deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_reactive_json_format( $tracker_field_name, $field_definition_override );

		// Check if this field is a local ID.
		$local_id_field            = ! empty( $maybe_tracker_config['local_id_field']['name'] )
			? $maybe_tracker_config['local_id_field']['name']
			: '';
		$maybe_composite_id_fields = json_decode( $local_id_field, true );

		if ( ( is_array( $maybe_composite_id_fields ) && in_array( $tracker_field_name, $maybe_composite_id_fields, true )
			|| ( $local_id_field === $tracker_field_name ) ) ) {
			$field_definition_override['isLocalIdField'] = true;
		}

		// Check if this field is a UUID source field.
		$uuid_source_fields = $maybe_tracker_config['uuid_source_fields'] ?? array();

		if ( in_array( $tracker_field_name, $uuid_source_fields, true ) ) {
			$field_definition_override['isUuidSourceField'] = true;
		}

		// Check if this field is the one for the subitem definition.
		if ( ( $maybe_tracker_config['subordination']['primary_item_id_field'] ?? null ) === $tracker_field_name ) {
			$field_definition_override['isSubitemOf'] = true;
		}

		// Finally add the definition to the build.
		$data['field_definitions_overrides'][] = array(
			'field_definition_override' => $field_definition_override,
		);
	}

	return $data;
}

function deploy_sync_content__advanced_settings_form__insert_exclusions_data( &$build ): void {
	// Reset the data.
	$build['data']['exclusions'] = array();

	// Get the raw config.
	$collected = ComponentBasedObject::getRootComponentByClassName( ExclusionsConfigProviderManager::class, true )->getActiveConfig();

	foreach ( $collected as $plugin => $config_per_tracker ) {
		foreach ( $config_per_tracker as $tracker_id => $config_per_field ) {
			foreach ( $config_per_field as $full_field_name => $item_exclusion_patterns ) {
				$item_to_insert = array();

				$item_to_insert['plugin']  = $plugin;
				$item_to_insert['tracker'] = $tracker_id;
				$item_to_insert['field']   = $full_field_name;

				foreach ( $item_exclusion_patterns as $pattern ) {
					$item_to_insert['conditions'][] = array(
						'condition' => array(
							'value' => $pattern,
						),
					);
				}

				if ( empty( $item_to_insert['conditions'] ) ) {
					continue;
				}

				$build['data']['exclusions'][] = array(
					'exclusion_item' => $item_to_insert,
				);
			}
		}
	}
}

function deploy_sync_content__advanced_settings_form__insert_ivh_structures( &$build ): void {
	/**
	 * @var $value_handlers ItemValueHandlerBase[]|class-string<ItemValueHandlerBase>[]
	 */
	$value_handlers = apply_filters( 'deploy_sync_content__item_value_handler_manager__get_class_names', array() );

	// Reset the ivh structures in build.
	$build['templates']['item_value_handler_edit']['content'] = Yaml::parse(
		"label:\n" .
		"  load: field_label\n" .
		"  content: Value handler\n" .
		"select:\n" .
		"  type: SelectField\n" .
		"  dataLocation: ~.value_handler\n" .
		"  options:\n" .
		"    - label: Default\n" .
		"      value: \"\""
	);

	foreach ( $value_handlers as $value_handler_id => $value_handler ) {
		if ( 'scalar' === $value_handler_id ) {
			// Ignore this one; the 'scalar' ivh will be used when Default is selected.
			continue;
		}

		// Append the option.
		$build['templates']['item_value_handler_edit']['content']['select']['options'][] = array(
			'label' => $value_handler::getLabel(),
			'value' => $value_handler_id,
		);

		if ( 'option' !== $value_handler_id ) {
			// Add in the option ivh variant of the item_value_handler_edit.
			// This is needed because we don't want to be able to select the option ivh while already in option.
			$build['templates']['option_item_value_handler_edit']['content']['select']['options'][] = array(
				'label' => $value_handler::getLabel(),
				'value' => $value_handler_id,
			);
		}

		// Optional additional subform.
		$maybe_form_build = $value_handler::getFormBuild();

		if ( empty( $maybe_form_build ) ) {
			// No additional subform.
			continue;
		}

		// Append the additional subform.
		$subform_template_id = 'item_value_handler__' . $value_handler_id . '__edit';

		$build['templates']['item_value_handler_edit']['content'][ $value_handler_id . '_ivh' ] = array(
			'load'                => $subform_template_id,
			'keepTemplateContext' => true,
		);

		if ( 'option' !== $value_handler_id ) {
			// Don't recurse on the option ivh.
			$build['templates']['option_item_value_handler_edit']['content'][ $value_handler_id . '_ivh' ] = array(
				'load'                => $subform_template_id,
				'keepTemplateContext' => true,
			);
		}

		$build['templates'][ $subform_template_id ] = array(
			'type'    => 'div',
			'actions' => array(
				array(
					'what'  => 'hide',
					'when'  => '~.value_handler',
					'isNot' => $value_handler_id,
				),
			),
			'content' => $maybe_form_build,
		);
	}

	usort(
		$build['templates']['item_value_handler_edit']['content']['select']['options'],
		fn( $a, $b ) => $a['label'] <=> $b['label']
	);
}

function deploy_sync_content__advanced_settings_form__insert_plugin_data( &$build ): void {
	// Reset the plugin build.
	$build['data']['pluginList'] = array(
		array(
			'label' => 'Core',
			'value' => '',
		),
	);

	// We use installed plugins instead of active plugins so that we can still display
	// the plugin name for outdated (or not yet active) config.
	$installed_plugins = Plugin::getInstalledPlugins( array( 'include_deployer' => true ) );

	foreach ( $installed_plugins as $installed_plugin ) {
		$is_active = Plugin::isPluginActive( $installed_plugin['_Basename'] );

		$build['data']['pluginList'][] = array(
			'label' => $installed_plugin['Name'] . ( $is_active ? '' : __( ' (inactive)', 'deploy-sync-content' ) ),
			'value' => $installed_plugin['_Slug'],
		);
	}
}

function deploy_sync_content__advanced_settings_form__insert_processor_structures( &$build ): void {
	/**
	 * @var $value_processors ValueProcessorBase[]|class-string<ValueProcessorBase>[]
	 */
	$value_processors = apply_filters( 'deploy_sync_content__value_processor_manager__get_class_names', array() );

	// Reset the processor build.
	$build['templates']['add_processor']['main']['content']['content']['select']['options'] = array(
		array(
			'label' => __( 'Select a processor...', 'deploy-sync-content' ),
			'value' => '',
		),
	);

	$build['templates']['add_processor']['main']['content']['content']['button']['content']['enabledButton']['actions'] = array(
		array(
			'what'    => 'hide',
			'when'    => '~._selectedProcessor',
			'isEmpty' => null,
		),
	);

	$build['templates']['processors_edit']['content']['table']['content'][1]['content']['options'] = array();

	foreach ( $value_processors as $value_processor_id => $value_processor_class ) {
		// Append the edit form of the processor to the table.
		$build['templates']['processors_edit']['content']['table']['content'][1]['content']['options'][ $value_processor_id ] = array(
			'load'    => 'processor_item_row',
			'content' => $value_processor_class::getFormBuild(),
		);

		// Append the option in the add form.
		$build['templates']['add_processor']['main']['content']['content']['select']['options'][] = array(
			'label' => $value_processor_class::getLabel(),
			'value' => $value_processor_id,
		);

		// Append to the button actions.
		$build['templates']['add_processor']['main']['content']['content']['button']['content']['enabledButton']['actions'][] = array(
			'what'  => 'addData',
			'when'  => '~._selectedProcessor',
			'is'    => $value_processor_id,
			'on'    => 'click',
			'path'  => '~.processors',
			'value' => array(
				$value_processor_id => null,
			),
		);
	}

	usort(
		$build['templates']['add_processor']['main']['content']['content']['select']['options'],
		static function ( $a, $b ) {
			if ( '' === $a['value'] ) {
				// Keep the default value first.
				return -1;
			}

			return $a['label'] <=> $b['label'];
		}
	);
}

function deploy_sync_content__advanced_settings_form__insert_screen_label_formatter_build( &$build ): void {
	// Reset the build.
	$build['templates']['screen_label_formatter__edit']['content']['additionalSubForms'] = array();

	// Reset the data.
	$build['data']['screenLabelFormatterList'] = array();

	/**
	 * @var $formatters ScreenLabelFormatterBase[]|class-string<ScreenLabelFormatterBase>[]
	 */
	$formatters = apply_filters( 'deploy_sync_content__screen_label_formatter_manager__get_class_names', array() );

	foreach ( $formatters as $formatter_id => $formatter ) {
		$build['data']['screenLabelFormatterList'][] = array(
			'label' => $formatter::getLabel(),
			'value' => $formatter_id,
		);

		$build['templates']['screen_label_formatter__edit']['content']['additionalSubForms'][ $formatter_id . '__slf' ] = array(
			'type'    => 'Phantom',
			'actions' => array(
				array(
					'what'  => 'hide',
					'when'  => '~.screen_label_formatter.id',
					'isNot' => $formatter_id,
				),
			),
			'content' => $formatter::getFormBuild(),
		);
	}

	// Sort by public name, and keep the default one first.
	usort(
		$build['data']['screenLabelFormatterList'],
		static function ( $a, $b ) {
			if ( 'base' === $a['value'] ) {
				return -1;
			}

			return $a['label'] <=> $b['label'];
		}
	);

	// Set the ID of the default formatter to empty string,
	// so that it will be selected by default.
	$build['data']['screenLabelFormatterList'][0]['value'] = '';
}

function deploy_sync_content__advanced_settings_form__insert_shortcodes_data( &$build ): void {
	// Reset the data.
	$build['data']['shortcodes'] = array();

	// Get the raw config.
	$collected = ComponentBasedObject::getRootComponentByClassName( TransmutationHandlerShortcodeReferencesConfigProviderManager::class, true )->getActiveConfig();

	foreach ( $collected as $plugin => $config_per_tracker ) {
		foreach ( $config_per_tracker as $shortcode_name => $shortcode_attributes ) {
			foreach ( $shortcode_attributes as $shortcode_attribute => $shortcode_definition ) {
				$item_to_insert = array();

				$item_to_insert['plugin']    = $plugin;
				$item_to_insert['name']      = $shortcode_name;
				$item_to_insert['attribute'] = $shortcode_attribute;

				$item_to_insert['tracker'] = $shortcode_definition['tracker'];

				if ( empty( $item_to_insert['tracker'] ) ) {
					continue;
				}

				$build['data']['shortcodes'][] = array(
					'shortcode_item' => $item_to_insert,
				);
			}
		}
	}
}

function deploy_sync_content__advanced_settings_form__insert_tracker_data( &$build ): void {
	$tracker_config_provider_manager = ComponentBasedObject::getRootComponentByClassName( TrackerConfigProviderManager::class, true );

	// Get the WP core tables.
	$tracker_groups = array(
		array(
			'group_label' => 'Core',
			'trackers'    => Database::getWpCoreTables( array( 'without_prefix' => true ) ),
		),
	);

	// Per plugin tables.
	$active_plugins_and_tables = Plugin::getActivePluginsNamesWithTables();

	usort(
		$active_plugins_and_tables,
		static function ( $a, $b ) {
			return $a['Name'] <=> $b['Name'];
		}
	);

	foreach ( $active_plugins_and_tables as $table_group_entry ) {
		if ( empty( $table_group_entry['Tables'] ) ) {
			continue;
		}

		$plugin_group = array(
			'group_label' => $table_group_entry['Name'],
		);

		sort( $table_group_entry['Tables'], SORT_NATURAL );

		foreach ( $table_group_entry['Tables'] as $plugin_tables ) {
			$plugin_group['trackers'][] = Database::removeDatabasePrefix( $plugin_tables );
		}

		$tracker_groups[] = $plugin_group;
	}

	$other_plugins_tables = Plugin::getOthersPluginsTables();

	sort( $other_plugins_tables, SORT_NATURAL );

	if ( ! empty( $other_plugins_tables ) ) {
		// Unknown plugin tables ("Others" group).
		$plugin_group = array(
			'group_label' => __( 'Others', 'deploy-sync-content' ),
		);

		foreach ( $other_plugins_tables as $other_plugin_table ) {
			$plugin_group['trackers'][] = Database::removeDatabasePrefix( $other_plugin_table );
		}

		$tracker_groups[] = $plugin_group;
	}

	// Reset.
	$build['data']['enabledTrackers'] = array(
		array(
			'label' => __( 'Please select a value...', 'deploy-sync-content' ),
			'value' => '',
		),
	);

	// Get the current configuration.
	$active_tracker_config = $tracker_config_provider_manager->getActiveConfig();

	foreach ( $tracker_groups as $tracker_group ) {
		$group_data = array(
			'group_label' => $tracker_group['group_label'],
			'schema'      => array(),
		);

		foreach ( $tracker_group['trackers'] as $tracker_id ) {
			$tracker_config = $active_tracker_config[ $tracker_id ] ?? null;

			// ID for the dynamic options of selects.
			$build['data']['enabledTrackers'][] = array(
				'label' => ! empty( $tracker_config['tracker_label'] ) ? $tracker_config['tracker_label'] : $tracker_id,
				'value' => $tracker_id,
			);

			$group_data['schema'][] = array(
				'tracker_schema_item' => deploy_sync_content__advanced_settings_form__get_tracker_schema_item(
					$tracker_id,
					$tracker_config ?? array()
				),
			);
		}

		$build['data']['tracker_group_list'][] = array(
			'tracker_group' => $group_data,
		);
	}
}

function deploy_sync_content__advanced_settings_form__insert_transmutation_handler_structures( &$build ): void {
	/**
	 * @var $transmutation_handlers TransmutationHandlerBase[]|class-string<TransmutationHandlerBase>[]
	 */
	$transmutation_handlers = apply_filters( 'deploy_sync_content__transmutation_handler_manager__get_class_names', array() );

	// Reset the transmutation handler build.
	$build['data']['transmutationHandlers'] = array(
		array(
			'label' => __( 'Select a handler...', 'deploy-sync-content' ),
			'value' => '',
		),
	);

	$build['templates']['processors_edit']['content']['table']['content'][1]['content']['options']['transmutation']['content'][1]['content']['modal']['body']['transmutation_handler_options'] = array();

	foreach ( $transmutation_handlers as $transmutation_handler_id => $transmutation_handler_class ) {
		// Append the option in the add form.
		$build['data']['transmutationHandlers'][] = array(
			'label' => $transmutation_handler_class::getPublicName(),
			'value' => $transmutation_handler_id,
		);

		$maybe_form_build = $transmutation_handler_class::getFormBuild();

		if ( empty( $maybe_form_build ) ) {
			continue;
		}

		$build['templates']['processors_edit']['content']['table']['content'][1]['content']['options']['transmutation']['content'][1]['content']['modal']['body']['transmutation_handler_options'][ $transmutation_handler_id ] = array(
			'type'    => 'div',
			'actions' => array(
				array(
					'what'  => 'hide',
					'when'  => '~.transmutation_handler',
					'isNot' => $transmutation_handler_id,
				),
			),
			'content' => $maybe_form_build,
		);
	}
}

function deploy_sync_content__advanced_settings_form__insert_value_handler_option_data( &$build ): void {
	// Reset the data.
	$build['data']['options'] = array();

	// Get the raw config.
	$collected = ComponentBasedObject::getRootComponentByClassName( ValueHandlerOptionConfigProviderManager::class, true )->getActiveConfig();

	foreach ( $collected as $plugin => $config_per_tracker ) {
		foreach ( $config_per_tracker as $tracker_id => $config_per_option_name_field ) {
			foreach ( $config_per_option_name_field as $option_name_full_field_name => $config_per_option ) {
				$item_to_insert = array();

				$item_to_insert['plugin']  = $plugin;
				$item_to_insert['tracker'] = $tracker_id;
				$item_to_insert['field']   = $option_name_full_field_name;

				foreach ( $config_per_option['fields'] as $option_name => $overrides ) {
					deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_reactive_json_format( $option_name, $overrides );

					$item_to_insert['field_definitions_overrides'][] = array(
						'options_field_definition_override' => $overrides,
					);
				}

				if ( empty( $item_to_insert['field_definitions_overrides'] ) ) {
					continue;
				}

				$build['data']['options'][] = array(
					'options_item' => $item_to_insert,
				);
			}
		}
	}
}

function deploy_sync_content__advanced_settings_form__normalize_field_definition_override_for_config_format( ?array $field_definition_override = null ): ?array {
	if ( empty( $field_definition_override ) ) {
		return null;
	}

	$new_override = array();

	// Check if this field is excluded.
	if ( ! empty( $field_definition_override['exclude'] ) ) {
		$new_override['exclude'] = true;
	}

	if ( ! empty( $field_definition_override['processors'] ) ) {
		// Copy the processor definitions.
		$new_override['processors'] = $field_definition_override['processors'];
	}

	if ( ! empty( $field_definition_override['value_handler'] ) ) {
		// Load the item value handler to let it filter data.
		$ivh_manager = ComponentBasedObject::getRootComponentByClassName( \EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ItemValueHandler\ItemValueHandlerManager::class, true );
		$ivh_class   = $ivh_manager->getHandlerClassNameById( $field_definition_override['value_handler'] );

		if ( is_subclass_of( $ivh_class, ItemValueHandlerBase::class ) ) {
			$ivh_data = $ivh_class::normalizeFormSubmittedData( $field_definition_override );

			if ( ! empty( $ivh_data ) ) {
				$new_override = array_merge( $new_override, $ivh_data );
			}
		}
	}

	return $new_override;
}

function deploy_sync_content__advanced_settings_form__submit( &$build ): void {
	deploy_sync_content__advanced_settings_form__submit_exclusions( $build );
	deploy_sync_content__advanced_settings_form__submit_options_data( $build );
	deploy_sync_content__advanced_settings_form__submit_shortcodes_data( $build );
	deploy_sync_content__advanced_settings_form__submit_tracker_data( $build );
}

function deploy_sync_content__advanced_settings_form__submit_exclusions( &$build ): void {
	$config_to_save = array();

	foreach ( $build['data']['exclusions'] ?? array() as $item ) {
		$exclusion_item = $item['exclusion_item'] ?? null;

		if ( empty( $exclusion_item ) ) {
			continue;
		}

		$required_values = array(
			'tracker',
			'field',
			'conditions',
		);

		foreach ( $required_values as $required_value ) {
			if ( empty( $exclusion_item[ $required_value ] ) ) {
				continue 2;
			}
		}

		$plugin          = $exclusion_item['plugin'];
		$tracker         = $exclusion_item['tracker'];
		$full_field_name = $exclusion_item['field'];
		$conditions      = $exclusion_item['conditions'];

		$item_exclusion_patterns = array();

		foreach ( $conditions as $condition_item ) {
			$value = $condition_item['condition']['value'];

			if ( empty( $value ) ) {
				continue;
			}

			$item_exclusion_patterns[] = $value;
		}

		if ( empty( $item_exclusion_patterns ) ) {
			continue;
		}

		sort( $item_exclusion_patterns, SORT_NATURAL );

		$config_to_save[ $plugin ][ $tracker ][ $full_field_name ] = $item_exclusion_patterns;

		// This is not the most efficient way to sort, but that works.
		ksort( $config_to_save[ $plugin ][ $tracker ], SORT_NATURAL );
		ksort( $config_to_save[ $plugin ], SORT_NATURAL );
		ksort( $config_to_save, SORT_NATURAL );
	}

	ComponentBasedObject::getRootComponentByClassName( ExclusionsConfigProviderManager::class, true )->updateCurrentConfig(
		false,
		$config_to_save,
		array( 'keep_existing' => false )
	);
}

function deploy_sync_content__advanced_settings_form__submit_options_data( &$build ): void {
	$config_to_save = array();

	foreach ( $build['data']['options'] ?? array() as $item ) {
		$options_item = $item['options_item'] ?? null;

		if ( empty( $options_item ) ) {
			continue;
		}

		$required_values = array(
			'tracker',
			'field',
			'field_definitions_overrides',
		);

		foreach ( $required_values as $required_value ) {
			if ( empty( $options_item[ $required_value ] ) ) {
				continue 2;
			}
		}

		$plugin                      = $options_item['plugin'] ?? '';
		$tracker                     = $options_item['tracker'];
		$full_field_name             = $options_item['field'];
		$field_definitions_overrides = $options_item['field_definitions_overrides'];

		$options_definitions = array();

		foreach ( $field_definitions_overrides as $field_definition_override_wrapper ) {
			$field_definition_override = $field_definition_override_wrapper['options_field_definition_override'];

			if ( empty( $field_definition_override ) ) {
				continue;
			}
			$option_name = $field_definition_override['field'];

			deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_config_format( $field_definition_override );

			$normalized = deploy_sync_content__advanced_settings_form__normalize_field_definition_override_for_config_format( $field_definition_override );

			if ( ! empty( $normalized ) ) {
				$options_definitions[ $full_field_name ]['fields'][ $option_name ] = $normalized;

				ksort( $options_definitions[ $full_field_name ]['fields'], SORT_NATURAL );
			}
		}

		if ( empty( $options_definitions ) ) {
			continue;
		}

		$config_to_save[ $plugin ][ $tracker ] = $options_definitions;

		// This is not the most efficient way to sort, but that works.
		ksort( $config_to_save[ $plugin ][ $tracker ], SORT_NATURAL );
		ksort( $config_to_save[ $plugin ][ $tracker ], SORT_NATURAL );
		ksort( $config_to_save[ $plugin ], SORT_NATURAL );
		ksort( $config_to_save, SORT_NATURAL );
	}

	$config_provider_manager = ComponentBasedObject::getRootComponentByClassName( ValueHandlerOptionConfigProviderManager::class, true );
	$config_provider_manager->updateCurrentConfig( false, $config_to_save, array( 'keep_existing' => false ) );
}

function deploy_sync_content__advanced_settings_form__submit_shortcodes_data( &$build ): void {
	$config_to_save = array();

	foreach ( $build['data']['shortcodes'] ?? array() as $item ) {
		$shortcode_item = $item['shortcode_item'] ?? null;

		if ( empty( $shortcode_item ) ) {
			continue;
		}

		$required_values = array(
			'attribute',
			'name',
			'tracker',
		);

		foreach ( $required_values as $required_value ) {
			if ( empty( $shortcode_item[ $required_value ] ) ) {
				continue 2;
			}
		}

		$name      = $shortcode_item['name'];
		$plugin    = $shortcode_item['plugin'] ?? '';
		$attribute = $shortcode_item['attribute'];
		$tracker   = $shortcode_item['tracker'];

		$config_to_save[ $plugin ][ $name ][ $attribute ]['tracker'] = $tracker;

		// This is not the most efficient way to sort, but that works.
		ksort( $config_to_save[ $plugin ][ $name ][ $attribute ], SORT_NATURAL );
		ksort( $config_to_save[ $plugin ][ $name ], SORT_NATURAL );
		ksort( $config_to_save[ $plugin ], SORT_NATURAL );
		ksort( $config_to_save, SORT_NATURAL );
	}

	$config_provider_manager = ComponentBasedObject::getRootComponentByClassName( TransmutationHandlerShortcodeReferencesConfigProviderManager::class, true );
	$config_provider_manager->updateCurrentConfig( false, $config_to_save, array( 'keep_existing' => false ) );
}

function deploy_sync_content__advanced_settings_form__submit_tracker_data( &$build ): void {
	$config_to_save = array();

	foreach ( $build['data']['tracker_group_list'] as $tracker_group_wrapper ) {
		foreach ( $tracker_group_wrapper['tracker_group']['schema'] as $tracker_schema_item_wrapper ) {
			$tracker_item_schema = $tracker_schema_item_wrapper['tracker_schema_item'];

			deploy_sync_content__advanced_settings_form__submit_tracker_schema_item( $config_to_save, $tracker_item_schema );
		}
	}

	$tracker_config_provider_manager = ComponentBasedObject::getRootComponentByClassName( TrackerConfigProviderManager::class, true );
	$tracker_config_provider_manager->updateCurrentConfig( false, $config_to_save, array( 'keep_existing' => false ) );
}

function deploy_sync_content__advanced_settings_form__submit_tracker_schema_item( &$config_to_save, $tracker_item_schema ) {
	if ( empty( $tracker_item_schema['isEnabled'] ) ) {
		// Deconfigure it by not including it in the final config.
		return;
	}

	$item_config_to_save = array();

	$tracker_id                        = $tracker_item_schema['tracker_id'];
	$item_config_to_save['tracker_id'] = $tracker_id;
	$item_config_to_save['plugin']     = $tracker_item_schema['plugin'] ?? '';

	if ( ! empty( $tracker_item_schema['uuid_random_generation'] ) ) {
		// Only add this config when the value is true.
		$item_config_to_save['uuid_random_generation'] = true;
	}

	if ( ! empty( $tracker_item_schema['tracker_label'] ) ) {
		$item_config_to_save['tracker_label'] = $tracker_item_schema['tracker_label'];
	}

	if ( ! empty( $tracker_item_schema['excluded'] ) ) {
		$item_config_to_save['excluded'] = true;
	}

	if ( ! empty( $tracker_item_schema['screen_label_formatter'] ) ) {
		$item_config_to_save['screen_label_formatter'] = $tracker_item_schema['screen_label_formatter'];
	}

	$local_id_fields = array();

	$uuid_source_fields = array();

	$maybe_subordination = null;

	foreach ( $tracker_item_schema['field_definitions_overrides'] as $field_definition_override_wrapper ) {
		$field_definition_override = $field_definition_override_wrapper['field_definition_override'];

		// Get the field name here because it's not part of the config format.
		$field_name = $field_definition_override['field'];

		// Extract the subordination info if any; its real location is at the tracker config level.
		$maybe_subordination = $field_definition_override['subordination'] ?? null;
		unset( $field_definition_override['subordination'] );

		$field_definition_override__reactive_json_format = $field_definition_override;

		// Convert to the config format.
		deploy_sync_content__advanced_settings_form__convert_field_definitions_overrides_to_config_format( $field_definition_override );

		// Check if this field is a local ID.
		if ( ! empty( $field_definition_override['isLocalIdField'] ) ) {
			// This field serves as a local ID.
			$local_id_fields[] = $field_name;
		}

		// Check if this field is a UUID source field.
		if ( ! empty( $field_definition_override['isUuidSourceField'] ) ) {
			// This field serves as a UUID source field.
			$uuid_source_fields[] = $field_name;
		}

		// Check if this field is the one for the subitem definition.
		if ( ! empty( $field_definition_override['isSubitemOf'] ) ) {
			// This field indicates the content of the primary tracker.
			// This field must have a reference item value handler.
			if ( 'reference' === $field_definition_override__reactive_json_format['value_handler']
				&& ! empty( $field_definition_override__reactive_json_format['reference_tracker_id'] ) ) {
				// Only one primary tracker is allowed; the last valid definition will overwrite the previous definitions.
				$item_config_to_save['subordination']['primary_item_id_field'] = $field_name;
				$item_config_to_save['subordination']['primary_tracker_id']    = $field_definition_override__reactive_json_format['reference_tracker_id'];
			}
		}

		$new_override = deploy_sync_content__advanced_settings_form__normalize_field_definition_override_for_config_format( $field_definition_override );

		if ( empty( $new_override ) ) {
			// No configuration for this field.
			continue;
		}

		ksort( $new_override, SORT_NATURAL );

		// Finally add the definition to the build.
		$item_config_to_save['field_definitions_overrides'][ $field_name ] = $new_override;
	}

	// Build the local ID config.
	if ( count( $local_id_fields ) > 1 ) {
		// It's a composite key.
		sort( $local_id_fields, SORT_NATURAL );

		$item_config_to_save['local_id_field'] = array(
			'name' => json_encode( $local_id_fields ),
			'type' => 'text',
		);
	} elseif ( 1 === count( $local_id_fields ) ) {
		// This is a single local ID.
		$local_id_field_name = reset( $local_id_fields );

		// Check if the field is an autoincremented and primary one.
		$table_name = Database::prependDatabasePrefix( $tracker_id );
		$definition = Database::getPrimaryKeysColumnsDefinition( $table_name );

		if ( 1 === count( $definition ) ) {
			// We only support single primary key tables.
			if ( 'auto_increment' === $definition[0]['EXTRA'] ) {
				// We only support auto increment primary keys.
				// Get the column type from database.
				$database = Database::getDatabaseHandle();

				$local_id_field_type = $database->get_var(
					$database->prepare(
						'SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = %s AND COLUMN_NAME = %s LIMIT 1;',
						array(
							$table_name,
							$local_id_field_name,
						)
					)
				);

				$item_config_to_save['local_id_field'] = array(
					'name' => $local_id_field_name,
					'type' => $local_id_field_type,
				);
			}
		}

		if ( empty( $item_config_to_save['local_id_field'] ) ) {
			// Last attempt to set something: insert the field as a single composite key field.
			$item_config_to_save['local_id_field'] = array(
				'name' => json_encode( array( $local_id_field_name ) ),
				'type' => 'text',
			);
		}
	}

	if ( ! empty( $uuid_source_fields ) ) {
		sort( $uuid_source_fields, SORT_NATURAL );
		$item_config_to_save['uuid_source_fields'] = $uuid_source_fields;
	}

	if ( ! empty( $maybe_subordination ) ) {
		// Replace the subordination in the tracker.
		$item_config_to_save['subordination'] = $maybe_subordination;
	}

	! empty( $item_config_to_save['field_definitions_overrides'] ) && ksort( $item_config_to_save['field_definitions_overrides'], SORT_NATURAL );
	ksort( $item_config_to_save, SORT_NATURAL );

	$config_to_save[ $tracker_id ] = $item_config_to_save;
}
