<?php

namespace EaLab\WP_Deployer\includes\Rest\DeploymentBundle;

use EaLab\WP_Deployer\includes\Rest\RestBase;
use EaLab\WP_Deployer\vendor\Dakwamine\Component\ComponentBasedObject;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Batch\BatchScheduler;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Exception\WpDeployException;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Import\Importer;
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\Uuid;

class ImportRest extends RestBase {
	static public function getRegisterRestRouteDefinitions(): array {
		$args = array();

		$args[] = array(
			'route' => '/deployment-bundle/import',
			'args'  => array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( static::class, 'handle' ),
				'permission_callback' => static function () {
					// If the request is from the admin, we allow it.
					return in_array( 'administrator', wp_get_current_user()->roles ?? array(), true );
				},
			),
		);

		return $args;
	}

	static public function handle( \WP_REST_Request $request ): \WP_REST_Response {
		$response = array();

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

		$display = $request->get_param( 'display' ) ?? 'default';

		$context = array(
			'form_context_tags' => array(
				'display:' . $display,
			),
		);

		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 );
			$importer         = ComponentBasedObject::getRootComponentByClassName( Importer::class, true );

			// The user clicked on the "import" or "force import" button.
			$force_import = $request->get_param( 'force-import' ) === '1';

			// TODO: for now, we import all when forced because some items from unavailable plugins
			// are not selected in the item selection list.
			$selected_item_full_uuids = $force_import
				? null
				: deploy_sync_content__get_item_full_uuids_for_export_import( $form_data );

			$count = $importer->countImportItems( $bundle_id, $selected_item_full_uuids );

			// Arbitrary count. Maybe allow this to be configured.
			$import_batch_step_size = 50;

			if ( $import_batch_step_size > $count['total'] ) {
				// No need to rely on the batch system for this much of items to import.
				// Do the complete import process right now.
				$importer->import( $bundle_id, $selected_item_full_uuids, $force_import );

				// Reload the import data and return this to update the form.
				require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/interactive-form--get-data--import.php';
				$final_build = deploy_sync_content__interactive_form__get_data__import( $bundle_id, $context );

				require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/interactive-form--get-data.php';
				$response = deploy_sync_content__form_structure__export_import__set_message(
					$final_build,
					esc_html__( 'The selected items have been imported successfully.', 'deploy-sync-content' ),
					'success'
				);

				return new \WP_REST_Response( $response );
			}

			// Prepare the batch steps.
			// To prepare those steps, they are prepared in the reverse order of call.
			// They are dependent: call "finalize import" after "import items" after "prepare import".
			// "finalize import" is called when all "import items" steps have processed.
			// "import items" is called when "prepare import" has been executed successfully.
			// This batch ID must be given to the UI so that it can load the right batch.
			$batch_id = Uuid::getInstance()->generateUuidV4();

			$batch_scheduler = ComponentBasedObject::getRootComponentByClassName( BatchScheduler::class, true );

			$complete_list = $count['complete_full_uuid_list'];

			// Args of the finalization import step.
			// This is for the final step called when all "processImportBatchStep" steps have been processed.
			$finalize_step_args = array(
				$batch_id,
				array(
					\EaLab\WP_Deployer\includes\controller\Import_Form_Handler::class,
					'finalizeImportBatch',
				),
				array( $batch_id, $bundle_id ),
				array( 'delay' => 0 ),
			);

			// Enqueue the real items imports,
			// far in the future so that the preparations have time to finish.
			$chunks = array_chunk( $complete_list, $import_batch_step_size );

			$enqueue_on_successful_preparation = array();

			foreach ( $chunks as $chunk ) {
				$enqueue_on_successful_preparation[] = array(
					$batch_id,
					array(
						\EaLab\WP_Deployer\includes\controller\Import_Form_Handler::class,
						'processImportBatchStep',
					),
					array( $batch_id, $bundle_id, $chunk, $force_import, $finalize_step_args ),
					array( 'delay' => 0 ),
				);
			}

			$decoded = array(
				'remaining_import_steps_count' => count( $chunks ),
			);

			$common_data_storage = ComponentBasedObject::getRootComponentByClassName( CommonDataStorage::class, true );

			$common_data_storage->store( $batch_id, json_encode( $decoded ), 'deploy_sync_content/import_meta', true );

			// Enqueue the import preparation.
			// It must be run before the other; this is the reason of the delay 0,
			// and the deferred enqueuing via steps_to_enqueue_on_success.
			$batch_scheduler->enqueueStepToBatch(
				$batch_id,
				array(
					\EaLab\WP_Deployer\includes\controller\Import_Form_Handler::class,
					'prepareImportBatch',
				),
				array( $batch_id, $bundle_id, $complete_list, $force_import ),
				array(
					'delay'                       => 0,
					'steps_to_enqueue_on_success' => $enqueue_on_successful_preparation,
				)
			);

			// Reload the import data and return this to update the form.
			require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/interactive-form--get-data--import.php';
			$final_build = deploy_sync_content__interactive_form__get_data__import( $bundle_id, $context );

			// Unset the submit endpoints to prevent resubmissions.
			unset( $final_build['data']['submitEndpoint'] );
			unset( $final_build['data']['submitEndpointForceAll'] );

			// Redirect to the batch progress page.
			$final_build['data']['_redirect'] = add_query_arg(
				array( 'batch_id' => $batch_id ),
				admin_url( 'admin.php?page=deploy-sync-content&tab=batch-progress' )
			);

			require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/interactive-form--get-data.php';
			$response = deploy_sync_content__form_structure__export_import__set_message(
				$final_build,
				esc_html__( 'The import has been launched. Please wait', 'deploy-sync-content' ),
				'success'
			);
		} catch ( \Throwable $t ) {
			require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/interactive-form--get-data--import.php';
			$final_build = deploy_sync_content__interactive_form__get_data__import( $bundle_id, $context );

			require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'includes/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' ) . ' ' . $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 );
	}
}
