<?php

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

use EaLab\WP_Deployer\vendor\Dakwamine\Component\ComponentBasedObject;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Exception\WpDeployException;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\File\KeyValueFile;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Import\Importability;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\Config\ConfigProviderManagerBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\TrackingManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\CommonDataStorage;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Database;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Date;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\FileManager;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Settings;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\UuidGenerator\PostsUuidGenerator;

require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/includes/helpers.php';

// @todo: this require_once seems unused here. Remove it later if true.
require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/admin/partials/utility/importability.php';

/**
 * Rest API Endpoint to check post changes.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/checkpost/(?P<uuid>[a-zA-Z0-9-]+)',
			array(
				'methods'             => 'POST',
				'callback'            => 'deploy_sync_content__check_post_changes',
				'permission_callback' => function ( $request ) {
					$dsc_settings_data = Settings::getInstance()->getAll();

					return ! empty( $dsc_settings_data['sync_prod'] )
						&& ! empty( $dsc_settings_data['access_token'] )
						&& $request->get_header( 'X-Deployer-Access-Token' ) === $dsc_settings_data['access_token'];
				},
			)
		);
	}
);

/**
 * Check post changes by post UUID
 *
 * @since    1.0.0
 */
function deploy_sync_content__check_post_changes( WP_REST_Request $request ) {

	$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

	if ( ! $tracking_manager->isProductionEnvironment() ) {
		exit();
	}

	$post_uuid      = $request->get_param( 'uuid' ) ?? '';
	$post_uuid      = ! empty( $post_uuid ) ? 'posts.' . $post_uuid : '';
	$incomming_item = json_decode( $request->get_body(), true ) ?? array();

	$item_tracker  = $tracking_manager->getItemTrackerByTrackerId( 'posts' );
	$importability = $item_tracker->checkImportability( $post_uuid, $incomming_item );

	$items_to_import = array();
	if ( ! ( $importability & Importability::BASED_ON_SAME_PRODUCTION_VALUES ) ) {
		$items_to_import = $tracking_manager->buildExportData( array( $post_uuid ) );
	}

	return array(
		'importability'   => $importability,
		'items_to_import' => $items_to_import ?? (object) [],
	);

}

/**
 * Add new Rest API Endpoint to send json file to prod
 *
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/sendtoprod',
			array(
				'methods'             => 'POST',
				'callback'            => 'deploy_sync_content__receive_exported_data',
				'permission_callback' => function ( WP_REST_Request $request ) {
					$dsc_settings_data = Settings::getInstance()->getAll();

					return ! empty( $dsc_settings_data['access_token'] )
						&& $request->get_header( 'X-Deployer-Access-Token' ) === $dsc_settings_data['access_token'];
				},
			)
		);
	}
);

/*
 * Call this to create and download a bundle.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment-bundle/download',
			array(
				'methods'             => 'POST',
				'callback'            => static function ( WP_REST_Request $request ) {
					$since = $request->get_param( 'since' );

					if ( empty( $since ) ) {
						// A "since" date is mandatory.
						return new WP_REST_Response(
							array(
								'message' => __( 'A "since" parameter is mandatory for this endpoint.', 'deploy-sync-content' ),
							),
							400
						);
					}

					// Parse the date into a DateTime object.
					// TODO: maybe limit the date to prevent dates too old that may lock the prod site during the export.
					$since_dt = Date::toDateTime( $since, new DateTimeZone( 'UTC' ) );

					if ( empty( $since_dt ) ) {
						return new WP_REST_Response(
							array(
								'message' => __( 'The given "since" parameter could not be parsed as a valid date.', 'deploy-sync-content' ),
							),
							400
						);
					}

					$file_manager = ComponentBasedObject::getRootComponentByClassName( FileManager::class, true );

					$original_definer_class = $file_manager->getBaseDirectoryDefinerClass();

					// Switch to the temp directory.
					$file_manager->setBaseDirectoryDefinerByClass( \EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\BaseDirectoryDefiner\TempBaseDirectoryDefiner::class );

					$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

					$deployment_bundle = $tracking_manager->buildExportData(
						null,
						array(
							'since' => $since_dt,
						)
					);

					$metadata = array(
						'from_date' => $since_dt->format( Date::ISO_MILLISECONDS ),
					);

					$maybe_bundle_label = $request->get_param( 'bundle_label' );

					if ( $maybe_bundle_label ) {
						$metadata['bundle_label'] = $maybe_bundle_label;
					}

					$bundle_id = $tracking_manager->writeExportData( $deployment_bundle, $metadata );

					// Archive the bundle.
					$zip_file_name = $file_manager->zipBundle( $bundle_id );

					// Final file name on download.
					$final_file_name = FileManager::getZipFileNameForExport( $bundle_id );

					// Send the response headers.
					header( 'Content-Type: application/zip' );
					header( 'Content-Disposition: attachment; filename="' . $final_file_name . '"' );
					header( 'Content-Length: ' . filesize( $zip_file_name ) );

					// Send the payload.
					// TODO: find the WP alternative to readfile if any.
					readfile( $zip_file_name );

					// Unlink the zip file; we don't need it anymore.
					wp_delete_file( $zip_file_name );

					// Unlink the temporary generated bundle.
					$file_manager->removeBundleDirectory( $bundle_id );

					// Reset to the previous definer class.
					$file_manager->setBaseDirectoryDefinerByClass( $original_definer_class );

					// Exit here because we already returned everything,
					// and we don't want WP to try to return something after.
					// If we ever need that WP continues after this callback,
					// we'll have to encapsulate the zip data and headers
					// with WP_Rest_Response.
					exit;
				},
				'permission_callback' => static function ( WP_REST_Request $request ) {
					$dsc_settings_data = Settings::getInstance()->getAll();

					if ( empty( $dsc_settings_data['allow_distant_retrieval'] ) ) {
						return false;
					}

					return ! empty( $dsc_settings_data['access_token'] )
						&& $request->get_header( 'X-Deployer-Access-Token' ) === $dsc_settings_data['access_token'];
				},
			)
		);
	}
);

/*
 * Retrieves a bundle from PROD.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/prod/deployment-bundle/download',
			array(
				'methods'             => 'POST',
				'callback'            => static function ( WP_REST_Request $request ) {
					$since = $request->get_param( 'since' );

					if ( empty( $since ) ) {
						// A "since" date is mandatory.
						return new WP_REST_Response(
							array(
								'message' => __( 'A "since" parameter is mandatory for this endpoint.', 'deploy-sync-content' ),
							),
							400
						);
					}

					// Parse the date into a DateTime object.
					$since_dt = Date::toDateTime( $since, new DateTimeZone( 'UTC' ) );

					if ( empty( $since_dt ) ) {
						return new WP_REST_Response(
							array(
								'message' => __( 'The given "since" parameter could not be parsed as a valid date.', 'deploy-sync-content' ),
							),
							400
						);
					}

					$maybe_bundle_label = $request->get_param( 'bundle_label' );

					try {
						$file_manager = ComponentBasedObject::getRootComponentByClassName( FileManager::class, true );

						$bundle_id = $file_manager->downloadBundleFromProd( $since, $maybe_bundle_label );
					} catch ( Throwable $t ) {
						$final_message = '';

						if ( $t->getPrevious() instanceof \EaLab\WP_Deployer\vendor\GuzzleHttp\Exception\BadResponseException ) {
							// Attempt to retrieve the original error message obtained by Guzzle from prod.
							/** @var \EaLab\WP_Deployer\vendor\GuzzleHttp\Exception\BadResponseException $guzzleError */
							$guzzleError   = $t->getPrevious();
							$inner_message = json_decode( $guzzleError->getResponse()->getBody(), true );
							$final_message = ! empty( $inner_message['message'] ) ? $inner_message['message'] : '';
						}

						if ( empty( $final_message ) ) {
							$final_message = $t->getMessage();
						}

						return new WP_REST_Response(
							array(
								'message' => $final_message,
							),
							$t->getPrevious()->getCode()
						);
					}

					$bundle_import_page_url = add_query_arg(
						array(
							'tab'            => 'import',
							'bundletoimport' => $bundle_id,
						),
						admin_url( DEPLOY_SYNC_CONTENT__BASIC_URI )
					);

					return new WP_REST_Response(
						array(
							'bundleId'            => $bundle_id,
							'bundleImportPageUrl' => $bundle_import_page_url,
						)
					);
				},
				'permission_callback' => static 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 );
				},
			)
		);
	}
);


/**
 * Endpoint retrieving progress of scheduled deploy-sync-content tracking-queue jobs.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/tracking-queue/progress',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response = array();

					$batch_id = 'tracking-queue';

					$pending_actions = as_get_scheduled_actions(
						array(
							'group'    => $batch_id,
							'hook'     => 'deploy_sync_content/track_item',
							// Retrieve all items.
							'per_page' => -1,
							'status'   => 'pending',
						),
						ARRAY_A
					);

					// Check if there is an action in progress.
					$in_progress_action = as_get_scheduled_actions(
						array(
							'group'    => $batch_id,
							'hook'     => 'deploy_sync_content/track_item',
							'per_page' => 1,
							'status'   => 'in-progress',
						),
						ARRAY_A
					);

					$queue_runner = ActionScheduler::runner();

					if ( ! empty( $pending_actions ) && empty( $in_progress_action ) && ! $queue_runner->has_maximum_concurrent_batches() ) {
						// If there are pending actions, but no in-progress action, we can start a new one.
						// Start the action of action scheduler.
						$action_id = key( $pending_actions );

						ActionScheduler::runner()->process_action( $action_id, 'Deploy & Sync Content UI' );
					}

					$response['progress']['pending'] = count( $pending_actions );

					// Retrieve result data from processed items.
					// Action scheduler returns only IDs of the actions, not their content.
					// That's why we rely on a custom data storage to retrieve the data.
					$database = Database::getDatabaseHandle();

					$to_prepare = 'SELECT * FROM %i WHERE `date_created` > %s AND `data_id` = \'tracking-queue\' AND `tag` = \'deploy_sync_content/track_item\'';
					$arguments  = array(
						Database::prependDatabasePrefix( CommonDataStorage::TABLE_NAME ),
						Date::getCurrentDateTime()->modify( '-1 hour' )->format( Date::DB_DATETIME_FORMAT_MILLISECONDS ),
					);

					$after_id = $request->get_param( 'after_id' ) ?? 0;

					if ( ! empty( $after_id ) ) {
						$to_prepare .= ' AND `id` > %d';

						$arguments[] = $after_id;
					}

					$prepared = $database->prepare( $to_prepare, $arguments );

					$items = $database->get_results( $prepared, ARRAY_A );

					$response['steps_data'] = $items;

					return new WP_REST_Response( $response, 200 );
				},
				'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 );
				},
			)
		);
	}
);

/**
 * Endpoint retrieving progress of any scheduled deploy-sync-content jobs identified by a batch_id.
 *
 * This is the standard batch processor for the generic deploy_sync_content/batch hook.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			// A batch ID is saved in the data_id 128 character-length column of the common data storage.
			'/batch/(?P<batch_id>[a-zA-Z0-9-_]{1,128})/step/process',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response = array();

					$batch_id = $request->get_param( 'batch_id' );

					$pending_actions = as_get_scheduled_actions(
						array(
							'group'    => $batch_id,
							'hook'     => 'deploy_sync_content/process_batch_step',
							// Retrieve all items.
							'per_page' => -1,
							'status'   => 'pending',
						),
						ARRAY_A
					);

					// Check if there is an action in progress.
					$in_progress_action = as_get_scheduled_actions(
						array(
							'group'    => $batch_id,
							'hook'     => 'deploy_sync_content/process_batch_step',
							'per_page' => 1,
							'status'   => 'in-progress',
						),
						ARRAY_A
					);

					$queue_runner = ActionScheduler::runner();

					if ( ! empty( $pending_actions ) && empty( $in_progress_action ) && ! $queue_runner->has_maximum_concurrent_batches() ) {
						// If there are pending actions, but no in-progress action, we can start a new one.
						// Start the action of action scheduler.
						$action_id = key( $pending_actions );

						ActionScheduler::runner()->process_action( $action_id, 'Deploy & Sync Content UI' );
					}

					$response['progress']['pending'] = count( $pending_actions ) + count( $in_progress_action );

					// Retrieve result data from processed items.
					// Action scheduler returns only IDs of the actions, not their content.
					// That's why we rely on a custom data storage to retrieve the data.
					$database = Database::getDatabaseHandle();

					$to_prepare = 'SELECT * FROM %i WHERE `data_id` = %s AND `tag` = \'deploy_sync_content/batch_step_result\'';
					$arguments  = array(
						Database::prependDatabasePrefix( CommonDataStorage::TABLE_NAME ),
						$batch_id,
					);

					$after_id = $request->get_param( 'after_id' ) ?? 0;

					if ( ! empty( $after_id ) ) {
						$to_prepare .= ' AND `id` > %d';

						$arguments[] = $after_id;
					}

					// Limit to the last 1000 steps results.
					$to_prepare .= ' LIMIT 1000';

					$prepared = $database->prepare( $to_prepare, $arguments );

					$items = $database->get_results( $prepared, ARRAY_A );

					$response['steps_data'] = $items;

					return new WP_REST_Response( $response, 200 );
				},
				'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 );
				},
			)
		);
	}
);

/**
 * Receive zip file function Rest API
 *
 * @since    1.0.0
 */
function deploy_sync_content__receive_zip_file( WP_REST_Request $request ) {
	$incomming_zip_file = $request->get_file_params() ?? null;

	$unzip_result = 'no file selected.';

	if ( isset( $incomming_zip_file['zip_file'] ) && $incomming_zip_file['zip_file']['error'] === 0 ) {
		$base_directory = ComponentBasedObject::getRootComponentByClassName( FileManager::class, true )->getBaseDirectory();
		$uploaddir      = $base_directory . 'exported-data/';
		$uploadfile     = $uploaddir . basename( $incomming_zip_file['zip_file']['name'] );

		require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/helpers.php';
		deploy_sync_content__move_uploaded_file( $incomming_zip_file['zip_file']['tmp_name'], $uploadfile );

		// Extract Zip File to the destination : <dsc_file_base_dir>/exported-data/
		$zip = new \ZipArchive;
		$zip->open( $uploadfile );
		$unzip_result = $zip->extractTo( $uploaddir );
		$zip->close();

		// Remove the Zip File
		wp_delete_file( $uploadfile );
	}

	return array(
		'result' => json_encode( $unzip_result ),
	);
}

/**
 * Receive Exported Data function Rest API
 *
 * @$request : Data in json format
 *
 * @since    1.0.0
 */
function deploy_sync_content__receive_exported_data( WP_REST_Request $request ) {
	$incoming_json_data = json_decode( $request->get_body(), true ) ?? array();
	$file_manager       = ComponentBasedObject::getRootComponentByClassName( FileManager::class, true );
	$exported_data_dir  = $file_manager->getBaseDirectory() . 'exported-data/';

	if ( ! is_dir( $exported_data_dir ) ) {
		$file_manager->ensureDirectoryExists( $exported_data_dir );
	}

	$result = false;

	if ( ! empty( $incoming_json_data ) ) {
		$subdirectory      = $incoming_json_data['subdirectory'];
		$dir_path          = $exported_data_dir . $subdirectory;

		// Remove subdirectory if it exists.
		$file_manager->removeBundleDirectory( $subdirectory );

		// Ensure the plugin upload directory exists
		if ( ! file_exists( $exported_data_dir ) ) {
			wp_mkdir_p( $exported_data_dir );
		}

		// Create the subdirectory.
		$file_manager->ensureDirectoryExists( $dir_path );

		foreach ( $incoming_json_data['data'] as $item_data ) {
			$json_file_full_path = $dir_path . '/' . $item_data['filename'];

			if ( ! is_dir( dirname( $json_file_full_path ) ) ) {
				$file_manager->ensureDirectoryExists( dirname( $json_file_full_path ) );
			}

			/** @var WP_Filesystem_Base $wp_filesystem */
			global $wp_filesystem;
			$wp_filesystem->put_contents( $json_file_full_path, json_encode( $item_data['json_data'] ) );
//			$json_file = fopen( $json_file_full_path, 'w' );
//			fwrite( $json_file, json_encode( $item_data['json_data'] ) );
//			fclose( $json_file );
		}

		// Set the transfer date.
		$local_state = $file_manager->loadDeploymentBundleMetadata( $subdirectory, 'local-state.json' ) ?? new KeyValueFile();
		$local_state->setValue( 'transfer_date', Date::getCurrentDateTime()->format( Date::ISO_MILLISECONDS ) );
		$file_manager->saveDeploymentBundleMetadata( $subdirectory, 'local-state.json', $local_state );

		// Done.
		$result = true;
	}

	return $result;
}

/**
 * Checks importability on Prod for multiple items.
 */
add_action(
	'rest_api_init',
	static function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/checkimportability',
			array(
				'methods'             => 'POST',
				'callback'            =>
					static function ( WP_REST_Request $request ): array {
						try {
							$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

							if ( ! $tracking_manager->isProductionEnvironment() ) {
								// Someone called this endpoint, but this is not a production instance.
								exit();
							}

							$body = json_decode( $request->get_body(), true ) ?? array();

							$incoming_items         = $body['deployment_items'] ?? array();
							$with_deployment_config = $body['deployment_config'] ?? null;

							// Use the deployment config provided by the caller.
							ConfigProviderManagerBase::setActiveDeploymentConfigOverride( $with_deployment_config ?? null );

							$importability = $tracking_manager->checkImportability( $incoming_items );

							ConfigProviderManagerBase::unsetDeploymentActiveConfigOverride();

							return array(
								'importability' => $importability,
							);
						} catch ( \Exception $e ) {
							return array(
								'error' => $e->getMessage(),
							);
						}
					},
				'permission_callback' => static function ( $request ) {
					$dsc_settings_data = Settings::getInstance()->getAll();

					return ! empty( $dsc_settings_data['sync_prod'] )
						&& ! empty( $dsc_settings_data['access_token'] )
						&& $request->get_header( 'X-Deployer-Access-Token' ) === $dsc_settings_data['access_token'];
				},
			)
		);
	}
);

/**
 * Checks importability on Prod for multiple items.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/diff',
			array(
				'methods'             => 'POST',
				'callback'            => 'deploy_sync_content__diff',
				'permission_callback' => function ( $request ) {
					$dsc_settings_data = Settings::getInstance()->getAll();

					return ! empty( $dsc_settings_data['sync_prod'] )
						&& ! empty( $dsc_settings_data['access_token'] )
						&& $request->get_header( 'X-Deployer-Access-Token' ) === $dsc_settings_data['access_token'];
				},
			)
		);
	}
);

/**
 * Callback on Diff endpoint.
 *
 * @param WP_REST_Request $request The request.
 *
 * @since    1.0.0
 */
function deploy_sync_content__diff( WP_REST_Request $request ): array {
	try {
		$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

		if ( ! $tracking_manager->isProductionEnvironment() ) {
			// Someone called this endpoint, but this is not a production instance.
			exit();
		}

		$body = json_decode( $request->get_body(), true ) ?? array();

		$incoming_item = $body['incoming_item'] ?? array();
		$options       = $body['options'] ?? array();

		return array(
			'diff' => $tracking_manager->getDiff( $incoming_item, $options ),
		);
	} catch ( \Exception $e ) {
		return array(
			'error' => $e->getMessage(),
		);
	}
}


/**
 * REST API endpoint to update configuration method.
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/settings/configuration-method',
			array(
				'methods'             => 'POST',
				'callback'            => 'deploy_sync_content__update_configuration_method',
				'permission_callback' => function () {
					// Only administrators can change configuration method
					return current_user_can( 'manage_options' );
				},
			)
		);
	}
);

/**
 * Update configuration method setting.
 *
 * @param WP_REST_Request $request The REST request.
 * @return WP_REST_Response The response.
 */
function deploy_sync_content__update_configuration_method( $request ) {
	try {
		$body = json_decode( $request->get_body(), true );

		if ( ! isset( $body['configuration_method'] ) ) {
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => 'Configuration method is required'
				),
				400
			);
		}

		$configuration_method = $body['configuration_method'];

		// Validate configuration method
		if ( ! in_array( $configuration_method, [ 'auto', 'manual' ], true ) ) {
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => 'Invalid configuration method'
				),
				400
			);
		}

		// Save to settings
		Settings::getInstance()->set( 'configuration_method', $configuration_method );

		return new WP_REST_Response(
			array(
				'success' => true,
				'message' => 'Configuration method updated successfully',
				'configuration_method' => $configuration_method
			),
			200
		);

	} catch ( Exception $e ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'message' => 'Error updating configuration method: ' . $e->getMessage()
			),
			500
		);
	}
}


/**
 * Endpoint refreshing the deployment config from the EALab repository (and other sources).
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment/config/refresh',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response    = array();
					$status_code = 200;

					try {
						do_action( 'deploy_sync_content__refresh_config' );
					} 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 );
				},
			)
		);
	}
);


/**
 * Endpoint for enqueueing items for re-indexation (re-tracking).
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment/items/reindex',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function () {
					$response    = array();
					$status_code = 200;

					try {
						do_action( 'deploy_sync_content__reindex_items' );
					} 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 );
				},
			)
		);
	}
);

/**
 * Endpoint for the export interactive form.
 */
add_action(
	'rest_api_init',
	static function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment-bundle/export-form-data',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response    = array();
					$status_code = 200;

					try {
						$bundle_id = $request->get_param( 'bundle_id' );

						require_once __DIR__ . '/interactive-form--get-data--regeneration.php';
						$response = deploy_sync_content__interactive_form__get_data__regeneration( $bundle_id );
					} 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 );
				},
			)
		);
	}
);


/**
 * Endpoint switching the deployment config from the EALab repository (and other sources).
 */
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment/config/bundle-ui/switch',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response    = array();
					$status_code = 200;

					try {
						$new_value = ! ( get_option( '_transient_' . DEPLOY_SYNC_CONTENT__TRANSIENT_BUNDLE_FORM_V2, null ) ?? true );
						set_transient( DEPLOY_SYNC_CONTENT__TRANSIENT_BUNDLE_FORM_V2, $new_value );
					} 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 );
				},
			)
		);
	}
);

/**
 * Endpoint for refreshing a bundle from the interactive form.
 *
 * @todo déplacer dans pro
 */
add_action(
	'rest_api_init',
	static function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment-bundle/refresh',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response = array();

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

					try {
						$bundle_id = $request->get_param( 'bundle_id' );

						if ( empty( $bundle_id ) ) {
							throw new WpDeployException( __( 'No "bundle ID" given.', 'deploy-sync-content' ) );
						}

						$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

						// Extract the UUIDs.
						$items_to_refresh = deploy_sync_content__get_item_full_uuids_for_export_import( $form_data );

						// Refresh, and discard any unselected item from the bundle
						// (if not retrieved again as a required dependency).
						$tracking_manager->refreshDeploymentBundle( $bundle_id, $items_to_refresh, array( 'writeExportData' => array( 'full_item_uuids_to_keep' => $items_to_refresh ) ) );

						// Reload the regeneration data and return this to update the form.
						require_once __DIR__ . '/interactive-form--get-data--regeneration.php';
						$final_build = deploy_sync_content__interactive_form__get_data__regeneration( $bundle_id );

						require_once __DIR__ . '/interactive-form--get-data.php';
						$response = deploy_sync_content__form_structure__export_import__set_message(
							$final_build,
							esc_html__( 'The deployment bundle has been updated.', 'deploy-sync-content' ),
							'success'
						);
					} catch ( \Throwable $t ) {
						if ( empty( $bundle_id ) ) {
							throw $t;
						}

						require_once __DIR__ . '/interactive-form--get-data--regeneration.php';
						$final_build = deploy_sync_content__interactive_form__get_data__regeneration( $bundle_id );

						require_once __DIR__ . '/interactive-form--get-data.php';
						$response = deploy_sync_content__form_structure__export_import__set_message(
							$final_build,
							esc_html__( 'An error occurred:', 'deploy-sync-content' ) . ' ' . esc_html( $t->getMessage() ),
							'danger'
						);
					}

					// Do not change the status code, because the UI will not update if code is not 2xx.
					return new WP_REST_Response( $response );
				},
				'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 );
				},
			)
		);
	}
);

/**
 * Endpoint for the import interactive form.
 *
 * @TODO déplacer dans wp pro.
 */
add_action(
	'rest_api_init',
	static function () {
		register_rest_route(
			'deploy-sync-content/v1',
			'/deployment-bundle/import-form-data',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => function ( WP_REST_Request $request ) {
					$response    = array();
					$status_code = 200;

					try {
						$bundle_id = $request->get_param( 'bundle_id' );

						require_once __DIR__ . '/interactive-form--get-data--import.php';
						$response = deploy_sync_content__interactive_form__get_data__import( $bundle_id );
					} 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 );
				},
			)
		);
	}
);

function deploy_sync_content__get_item_full_uuids_for_export_import( $form_data ) {
	// Extract the UUIDs.
	$items_to_import = array();

	/**
	 * Specific function to process sub-items.
	 *
	 * @param array $items Array of sub-items.
	 */
	$process_sub_items = static function ( array $items ) use ( &$items_to_import ): void {
		// Sub-items are all at the same level (no bundled/notBundled keys).
		foreach ( $items as $item ) {
			if ( empty( $item['deployment-item']['isSelected'] ) ) {
				continue;
			}

			$item_full_uuid = $item['deployment-item']['itemFullUuid'] ?? null;

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

			$items_to_import[ $item_full_uuid ] = $item_full_uuid;
		}
	};

	foreach ( $form_data['data']['_bundleTrackers'] as $bundle_tracker ) {
		foreach ( $bundle_tracker['bundleTracker'] as $bundled_state => $items ) {
			// Dev note: the structure is similar in export and import forms.
			// That's why this function works for both contexts.
			if ( ! in_array( $bundled_state, array( 'bundled', 'notBundled' ), true ) ) {
				continue;
			}

			foreach ( $items as $item ) {
				! empty( $item['deployment-item']['subItems'] ) && $process_sub_items( $item['deployment-item']['subItems'] );

				if ( empty( $item['deployment-item']['isSelected'] ) ) {
					continue;
				}

				$item_full_uuid = $item['deployment-item']['itemFullUuid'] ?? null;

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

				$items_to_import[ $item_full_uuid ] = $item_full_uuid;
			}
		}
	}

	return $items_to_import;
}

require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . '/includes/rest-api/advanced-settings/advanced-settings-form.php';

\EaLab\WP_Deployer\includes\Rest\dashboard\DashboardRest::registerRestHooks();
\EaLab\WP_Deployer\includes\Rest\DeploymentBundle\SynchronizeRest::registerRestHooks();
\EaLab\WP_Deployer\includes\Rest\DeploymentBundle\SynchronizeFormDataRest::registerRestHooks();
PostsUuidGenerator::registerRestHooks();
Settings::registerRestHooks();
