<?php
namespace SabaiApps\Directories\Component\System\Controller\Admin;

use SabaiApps\Directories\Context;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Component\System\Tool\ITool;
use SabaiApps\Directories\Exception\RuntimeException;

/**
 * RunToolWithProgress controller for running tools with progress tracking and multi-step forms.
 */
class RunToolWithProgress extends Form\AbstractMultiStepController {

	protected $_storageAsOption = false, $_ajaxSubmitStepForm = true;

	/**
	 * Gets the form settings for the current step.
	 *
	 * @param Context $context
	 * @param array   $formStorage
	 * @return array
	 */
	protected function _doGetFormSettings( Context $context, array &$formStorage ) {
		$settings = parent::_doGetFormSettings( $context, $formStorage );
		if ( $context->currentStep !== 'run'
			&& ! $this->_ajaxSubmitStepForm
		) {
			$this->_ajaxSubmit = false;
		}
		return $settings;
	}

	protected function _getTool( Context $context ) {
		if ( ! $tool_name = $this->_getToolName( $context ) ) {
			throw new RuntimeException( 'Invalid tool specified.' );
		}
		$tool = $this->_application->System_Tools_impl( $tool_name );
		if ( ! $tool->systemToolSupports() ) {
			throw new RuntimeException( 'The specified tool is not supported in this environment: ' . $tool_name );
		}

		return $tool;
	}

	/**
	 * Gets the tool name from the context or request.
	 *
	 * @param Context $context
	 * @return string|null
	 */
	protected function _getToolName( Context $context ) {
		return $context->getRequest()->asStr( 'tool' );
	}

	/**
	 * Gets the steps for the multi-step tool form.
	 *
	 * @param Context $context
	 * @param array   $formStorage
	 * @return array
	 */
	protected function _getSteps( Context $context, array &$formStorage ) {
		$steps = array();
		if ( $_steps = $this->_getTool( $context )->systemToolInfo( 'steps' ) ) {
			foreach ( $_steps as $step ) {
				$steps[ $step ] = array(
					'callback' => array( $this, '_getFormForStepRun' ),
					// 'submit_callback' => [$this, '_submitFormForStepRun'],
				);
			}
		}
		$steps['run'] = array();
		return $steps;
	}

	/**
	 * Gets the initial message for the tool.
	 *
	 * @param ITool $tool
	 * @return string
	 */
	protected function _getInitMessage( ITool $tool ) {
		if ( ! $msg = $tool->systemToolInfo( 'init_message' ) ) {
			$msg = sprintf(
				__( 'Running tool (%s)...', 'directories-pro' ),
				$tool->systemToolInfo( 'label' )
			);
		}
		return $msg;
	}

	/**
	 * Gets the form for the "run" step of the tool.
	 *
	 * @param Context $context
	 * @param array   $formStorage
	 * @return array
	 */
	public function _getFormForStepRun( Context $context, array &$formStorage ) {
		$tool = $this->_getTool( $context );
		$this->_initProgress( $context, $this->_getInitMessage( $tool ) );

		$form = array(
			'#header'  => array(),
			'tool'     => array(
				'#type'          => 'hidden',
				'#default_value' => (string) $tool,
			),
			'redirect' => array(
				'#type'          => 'hidden',
				'#default_value' => $context->getRequest()->asStr( 'redirect' ),
			),
			'settings' => array(
				'#tree' => true,
			),
		);
		if ( $context->currentStep === 'run' ) {
			$form['#header'] = array(
				array(
					'level'   => 'info',
					'message' => __( 'This may take a while to complete, please do not close the window or click other buttons while running the tool.', 'directories-pro' ),
				),
			);
		}

		$all_step_values = array();
		if ( $steps = $tool->systemToolInfo( 'steps' ) ) {
			foreach ( $steps as $step ) {
				$all_step_values[ $step ] = empty( $formStorage['values'][ $step ]['settings'] ) ? array() : $formStorage['values'][ $step ]['settings'];
			}
		}

		if ( false === $settings_form = $tool->systemToolSettingsForm( $context->currentStep, $all_step_values, array( 'settings' ), $context ) ) {
			$this->_submitable = false;
			return array(
				'#header' => array(
					array(
						'level'   => 'warning',
						'message' => __( 'There is no task to run. step: ' . $context->currentStep, 'directories-pro' ),
					),
				),
			);
		}

		if ( ! $settings_form = $this->Filter( 'system_tool_settings_form', (array) $settings_form, array( $tool, array( 'settings' ), $context->currentStep ) ) ) {
			if ( $context->currentStep !== 'run' ) {
				return $this->_skipStepAndGetForm( $context, $formStorage );
			}
		} else {
			if ( ! empty( $settings_form['#header'] ) ) {
				$form['#header'] += $settings_form['#header'];
				unset( $settings_form['#header'] );
			}
			$form['settings'] += $settings_form;
		}

		if ( $context->currentStep === 'run' ) {
			if ( ! $button_label = $tool->systemToolInfo( 'btn_label' ) ) {
				$button_label = __( 'Run Tool', 'directories-pro' );
			}
		} else {
			$button_label = __( 'Next &raquo;', 'directories-pro' );
		}
		$this->_submitButtons[] = array(
			'#btn_label'  => $button_label,
			'#btn_color'  => 'primary',
			'#btn_size'   => 'lg',
			'#attributes' => array( 'data-modal-title' => 'false' ),
		);

		return $form;
	}

	/**
	 * Handles the form submission for the "run" step, executes tool tasks, and tracks progress.
	 *
	 * @param Context   $context
	 * @param Form\Form $form
	 */
	public function _submitFormForStepRun( Context $context, Form\Form $form ) {
		$this->Action( 'system_tool_run_task', array( $this->_getToolName( $context ) ) );

		error_reporting( 0 );
		@set_time_limit( 0 );
		$tool = $this->_getTool( $context );

		$logs     = array(
			'error'   => array(),
			'warning' => array(),
			'info'    => array(),
			'success' => array(),
		);
		$settings = array();
		if ( $steps = $tool->systemToolInfo( 'steps' ) ) {
			foreach ( $steps as $step ) {
				$settings[ $step ] = empty( $form->storage['values'][ $step ]['settings'] ) ? array() : $form->storage['values'][ $step ]['settings'];
			}
			$settings[ $context->currentStep ] = empty( $form->values['settings'] ) ? array() : (array) $form->values['settings'];
		} else {
			$settings = empty( $form->values['settings'] ) ? array() : (array) $form->values['settings'];
		}

		// Init tasks
		if ( ! isset( $form->storage['tasks'] )
			|| ! isset( $form->storage['tasks_in_progress'] )
		) {
			$tool_storage = array();
			$tasks        = $tool->systemToolInit( $settings, $tool_storage, $logs, $context );
			if ( ! $tasks = array_filter( (array) $this->Filter( 'system_tool_tasks', $tasks, array( $tool, $settings, $context->currentStep, &$tool_storage, &$logs ) ) ) ) {
				$form->setError( __( 'There is no task to run.', 'directories-pro' ) );
				return;
			}

			$form->storage['tasks']        = $form->storage['tasks_in_progress'] = $tasks;
			$form->storage['tool_storage'] = $tool_storage;

			$this->logDebug( $this->_getInitMessage( $tool ) );
		}

		// Init current task and iteration
		if ( ! isset( $form->storage['current_task'] )
			|| ! isset( $form->storage['current_task_iteration'] )
		) {
			$form->storage['current_task']           = current( array_keys( $form->storage['tasks_in_progress'] ) );
			$form->storage['current_task_iteration'] = 0;
		}
		$current_task = $form->storage['current_task'];
		++$form->storage['current_task_iteration'];

		// Run task
		$result = $tool->systemToolRunTask(
			$current_task,
			$settings,
			$form->storage['current_task_iteration'],
			$form->storage['tasks'][ $current_task ],
			$form->storage['tool_storage'],
			$logs,
			$context
		);
		$done   = (int) $this->Filter(
			'system_tool_task_result',
			$result,
			array( $tool, $current_task, $settings, $form->storage['current_task_iteration'], $form->storage['tasks'][ $current_task ], $context->currentStep, &$form->storage['tool_storage'], &$logs )
		);
		if ( empty( $done ) ) {
			$form->setError( 'An error occurred while executing task: ' . $current_task );
			return;
		}

		// Proceed to next task if current task done
		$done = intval( $done );
		if ( $done > $form->storage['tasks_in_progress'][ $current_task ] ) {
			$done = $form->storage['tasks_in_progress'][ $current_task ];
		}
		$form->storage['tasks_in_progress'][ $current_task ] -= $done;
		if ( $form->storage['tasks_in_progress'][ $current_task ] === 0 ) {
			unset(
				$form->storage['tasks_in_progress'][ $current_task ],
				$form->storage['current_task'],
				$form->storage['current_task_iteration']
			);
		}

		// Continue if there are still remaining tasks
		if ( ! empty( $form->storage['tasks_in_progress'] ) ) {
			$total      = array_sum( $form->storage['tasks'] );
			$total_done = $total - array_sum( $form->storage['tasks_in_progress'] );
			if ( ! $message = $tool->systemToolInfo( 'progress_message' ) ) {
				if ( ! $message = $tool->systemToolInfo( 'init_message' ) ) {
					$message = __( 'Running tool...', 'directories-pro' );
				}
				$message .= ' (%d/%d)';
			}
			$message = sprintf( $message, $total_done, $total );
			$this->_isInProgress( $context, $total_done, $total, $message, $logs );
			return;
		}

		// Store logs to storage for the _complete() method.
		$form->storage['logs'] = $logs;
	}

	/**
	 * Handles completion of the tool run, displays message and handles download if needed.
	 *
	 * @param Context $context
	 * @param array   $formStorage
	 */
	protected function _complete( Context $context, array $formStorage ) {
		$tool    = $this->_getTool( $context );
		$message = sprintf(
			__( 'The selected tool (%s) was run successfully.', 'directories-pro' ),
			$tool->systemToolInfo( 'label' )
		);
		if ( empty( $formStorage['logs']['error'] )
			&& ( $redirect = $context->getRequest()->asStr( 'redirect' ) )
		) {
			$url = $this->Url( $redirect, array( '_drts_form_current_tab' => 'tools' ) );
		} elseif ( ! $url = $context->getSuccessUrl() ) {
				$url = false; // false prevents redirection

		}

		$download_file = null;
		$tool->systemToolComplete( $formStorage['tool_storage'], $formStorage['logs'], $download_file, $context );

		$this->_completeProgress( $context, $url, $message, $formStorage['logs'], $download_file );
	}
}
