<?php



namespace Inpsyde\SearchReplace;



use Inpsyde\SearchReplace\Database\Exporter;

use Inpsyde\SearchReplace\Service\MaxExecutionTime;



/**

 * Class FileDownloader

 *

 * @package Inpsyde\SearchReplace

 */

class FileDownloader {



	/**

	 * @var string

	 */

	private $nonce_action = 'download_sql';



	/**

	 * @var string

	 */

	private $nonce_name = 'insr_nonce_download';



	/**

	 * @var Exporter

	 */

	private $dbe;



	/**

	 * @var MaxExecutionTime

	 */

	private $max_execution;



	/**

	 * Admin constructor.

	 *

	 * @param Exporter $dbe

	 * @param MaxExecutionTime $max_execution

	 */

	public function __construct( Exporter $dbe, MaxExecutionTime $max_execution ) {



		$this->dbe           = $dbe;

		$this->max_execution = $max_execution;

	}



	/**

	 * Renders to download modal.

	 *

	 * @param array $report

	 */

	public function show_modal( $report ) {



		// Set compress status.

		// @codingStandardsIgnoreLine

		$compress = ( isset( $_POST['compress'] ) && 'on' === $_POST['compress'] );



		if ( array_key_exists( 'changes', $report ) && ! empty( $report['changes'] ) ) :

			?>

			<div class="updated notice is-dismissible">

				<?php

				// Show changes if there are any.

				if ( count( $report['changes'] ) > 0 ) {

					$this->show_changes( $report );

				}



				// If no changes found report that.

				if ( 0 === count( $report ['changes'] ) ) {

					echo '<p>' . esc_html__( 'Search pattern not found.', 'search-and-replace' ) . '</p>';

				}

				?>

			</div>

		<?php

		endif;

		?>



		<div class="updated notice is-dismissible insr_sql_button_wrap">

			<p><?php esc_html_e( 'Your SQL file was created!', 'search-and-replace' ); ?> </p>

			<form action method="post">

				<?php wp_nonce_field( $this->nonce_action, $this->nonce_name ); ?>

				<input type="hidden" name="action" value="download_file"/>

				<input type="hidden" name="sql_file" value="<?php echo esc_attr( $report['filename'] ); ?>">

				<input type="hidden" name="compress" value="<?php echo esc_attr( $compress ); ?>">

				<input id="insr_submit" type="submit" value="<?php esc_attr_e(

					'Download SQL File', 'search-and-replace'

				) ?>" class="button"/>

			</form>

		</div>

		<?php

	}



	/**

	 * Displays the changes made to the db

	 * echoes the changes in formatted html.

	 *

	 * @param array $report WP-Error Object if Errors

	 *

	 *      'tables' : Number of tables processed

	 *      'changes_count' : Number of changes made

	 *      'changes' : Array  with at least these elements:

	 *          'table_name'=> $[name of current table],

	 *          'changes'   => array('row'    => [row that has been changed ],

	 *          'column'    => [column that has been changed],

	 *          'from'      => ( old value ),

	 *          'to'        => ( $new value ),

	 * .

	 */

	public function show_changes( $report ) {



		// Get search & replace values in order to highlight them in the results.

		// @codingStandardsIgnoreStart

		$search  = isset( $_POST['search'] ) ?

			esc_html( filter_var( $_POST ['search'], FILTER_SANITIZE_STRING ) ) :

			'';

		$replace = isset( $_POST['replace'] ) ?

			esc_html( filter_var( $_POST ['replace'], FILTER_SANITIZE_STRING ) ) :

			'';

		// @codingStandardsIgnoreEnd



		$search_highlight  = '<span class="search-replace-search-value">' . $search . '</span>';

		$replace_highlight = '<span class ="search-replace-replace-value">' . $replace . '</span>';

		$delimiter         = [ ' ...', '...<br>' ];



		$this->show_message_by_report( $report );



		?>

		<p><a href="#" id="changes-modal-button"><?php esc_html_e( 'View details', 'search-and-replace' ); ?></a></p>

		<div id="changes-modal-background" class="search-replace-modal-background" style="display: none;"></div>

		<div id="changes-modal" class="search-replace-modal " style="display: none;">

			<div class="search-replace-modal-header">

				<button type="button" id="changes-modal-close" class="search-replace-modal-close-button"></button>

			</div>

			<div class="search-replace-changes-modal-content">

				<?php

				foreach ( $report['changes'] as $table_report ) :

					$changes      = $table_report['changes'];

					$changes_made = count( $changes );



					if ( $changes_made < 1 ) {

						continue;

					}



					$table = $table_report['table_name'];

					?>

					<h2 class="search-replace-modal-table-headline">

						<strong><?php esc_html_e( 'Table:', 'search-and-replace' ); ?></strong>

						<?php echo esc_html( $table ); ?>



						<strong><?php esc_html_e( 'Columns Changes:', 'search-and-replace' ); ?></strong>

						<?php echo esc_html( $changes_made ); ?>

					</h2>



					<table class="search-replace-modal-table">



						<thead>

						<tr>

							<th class="search-replace-row search-replace-narrow">

								<?php esc_html_e( 'Row', 'search-and-replace' ); ?>

							</th>

							<th class="search-replace-column">

								<?php esc_html_e( 'Column', 'search-and-replace' ); ?>

							</th>

							<th class="search-replace-old-value">

								<?php esc_html_e( 'Old value', 'search-and-replace' ); ?>

							</th>

							<th class="search-replace-new-value">

								<?php esc_html_e( 'New value', 'search-and-replace' ); ?>

							</th>

						</tr>

						</thead>



						<tbody>

						<?php

						foreach ( $changes as $change ) :

							// Trim results and wrap with highlight class.

							$old_value = esc_html( $change ['from'] );

							$old_value = $this->trim_search_results( $search, $old_value, $delimiter );

							$old_value = str_replace( $search, $search_highlight, $old_value );



							$new_value = esc_html( $change['to'] );

							$new_value = $this->trim_search_results( $replace, $new_value, $delimiter );

							$new_value = str_replace( $replace, $replace_highlight, $new_value );



							if ( $old_value && $new_value ) :

								?>

								<tr>

									<td class="search-replace-row search-replace-narrow">

										<?php echo esc_html( $change ['row'] ); ?>

									</td>

									<td class="search-replace-column">

										<?php echo esc_html( $change ['column'] ); ?>

									</td>

									<td class="search-replace-old-value">

										<?php echo wp_kses( $old_value, [ 'span' => [ 'class' => [] ] ] ); ?>

									</td>

									<td class="search-replace-new-value">

										<?php echo wp_kses( $new_value, [ 'span' => [ 'class' => [] ] ] ); ?>

									</td>

								</tr>

							<?php

							endif;

						endforeach;

						?>

						</tbody>



					</table>



				<?php endforeach; ?>



			</div>

		</div>

		<?php

	}



	/**

	 * Show Message by Report

	 *

	 * @param array $report The list of all the changes made.

	 *

	 * @return void

	 */

	private function show_message_by_report( $report ) {



		$msg = sprintf(

				// translators: %s is the count of tables.

			_n(

				'%s table was processed. ',

				'%s tables were processed. ',

				$report['tables'],

				'search-and-replace'

			),

			(int) $report['tables']

		);



		$msg .= sprintf(

				// translators: %s is the count of selected cells after search in tables.

			_n(

				'%s cell needs to be updated.',

				'%s cells need to be updated.',

				$report['changes_count'],

				'search-and-replace'

			),

			(int) $report['changes_count']

		);



		echo '<p>' . esc_html( $msg ) . '</p>';

	}



	/**

	 * Trims a given string to 50 chars before and after the search string, if the string is longer than 199 chars.

	 *

	 * @param string $needle

	 * @param string $haystack

	 * @param array  $delimiter $delimiter[0]=start delimiter, $delimiter[1] = end delimiter.

	 *

	 * @return string The trimmed $haystack

	 */

	public function trim_search_results( $needle, $haystack, $delimiter ) {



		// Ff result has <100 characters we return the whole string.

		if ( strlen( $haystack ) < 100 ) {

			return $haystack;

		}

		$trimmed_results = null;

		// Get all occurrences of $needle with up to 50 chars front & back.

		$matches      = preg_match_all( '@.{0,50}' . $needle . '.{0,50}@', $haystack, $trimmed_results );

		$return_value = '';



		// Don't need to perform any action if no matches.

		if ( ! $matches ) {

			return $return_value;

		}



		for ( $i = 0; $i < $matches; $i ++ ) {

			// Reset delimiter, might have been changed.

			$local_delimiter = $delimiter;



			// Check if the first trimmed result is the beginning of $haystack. if so remove leading delimiter.

			if ( $i === 0 ) {

				$pos = strpos( $haystack, $trimmed_results[0][ $i ] );

				if ( $pos === 0 ) {

					$local_delimiter[0] = '';

				}

			}



			// Check if the last trimmed result is the end of $haystack. if so, remove trailing delimiter.

			$last_index = $matches - 1;



			if ( $i === $last_index ) {

				$trimmed_result_length = strlen( $trimmed_results[0][ $i ] );

				$substring             = substr( $haystack, - $trimmed_result_length );



				if ( $substring === $trimmed_results[0][ $i ] ) {

					$local_delimiter[1] = '';

				}

			}

			$return_value .= $local_delimiter[0] . $trimmed_results[0][ $i ] . $local_delimiter[1];

		}



		return $return_value;

	}



	/**

	 * Calls the file delivery in Class DatabaseExporter.

	 *

	 * @wp-hook init

	 *

	 * @return bool

	 */

	public function deliver_backup_file() {



		// Retrieve the nonce value.

		// @codingStandardsIgnoreStart

		$nonce = isset( $_POST[ $this->nonce_name ] ) ?

			filter_var( $_POST[ $this->nonce_name ], FILTER_SANITIZE_STRING )

			: '';

		// @codingStandardsIgnoreEnd



		// If nonce has not been send, just return nothing else to do here.

		// The method may be hooked to a wp action, so it's executed on every page request.

		if ( ! $nonce ) {

			return false;

		}



		// Die in case the nonce has been passed but not a valid one.

		// @codingStandardsIgnoreLine

		if ( ! wp_verify_nonce( $_POST[ $this->nonce_name ], $this->nonce_action ) ) {

			wp_die( 'Cheating Uh?' );

		}



		$this->max_execution->set();



		// Get the action to perform.

		// @codingStandardsIgnoreLine

		$action = isset( $_POST['action'] ) ? filter_var( $_POST['action'], FILTER_SANITIZE_STRING ) : '';



		if ( 'download_file' !== $action ) {

			return false;

		}



		$sql_file = '';

		$compress = false;



		// @codingStandardsIgnoreLine

		if ( isset( $_POST['sql_file'] ) ) {

			// @codingStandardsIgnoreLine

			$sql_file = sanitize_file_name( $_POST['sql_file'] );

		}



		if ( ! $sql_file ) {

			wp_die( esc_html__( 'The file you are looking for doesn\'t exists.', 'search-and-replace' ) );

		}



		// If file name contains path or does not end with '.sql' exit.

		// @ToDo create a function to prevent traversal path.

		$ext = strrchr( $sql_file, '.' );

		if ( false !== strpos( $sql_file, '/' ) || '.sql' !== $ext ) {

			wp_die( 'Cheating Uh?' );

		}



		// @codingStandardsIgnoreLine

		if ( isset( $_POST['compress'] ) ) {

			// @codingStandardsIgnoreLine

			$compress = (bool) filter_var( $_POST['compress'], FILTER_VALIDATE_BOOLEAN );

		}



		// Download the file.

		$this->dbe->deliver_backup( $sql_file, $compress );



		$this->max_execution->restore();



		return true;

	}

}

