<?php

/**
 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
 *
 * @package WPDataAccess\Utilities
 */

namespace WPDataAccess\Utilities {

	use WPDataAccess\Connection\WPDADB;

	/**
	 * Class WPDA_Import_File
	 *
	 * Loads the content of an import file and imports it.
	 *
	 * @author  Peter Schulz
	 * @since   1.0.0
	 */
	class WPDA_Import_File {

		/**
		 * Pointer to import file
		 *
		 * @var string
		 */
		protected $file_pointer;

		/**
		 * Content of import file
		 *
		 * @var string
		 */
		protected $file_content;

		/**
		 * WPDA_Import constructor
		 *
		 * Create file pointer.
		 *
		 * @param string $file_path Full path of script (import) file.
		 *
		 * @since   1.0.0
		 */
		public function __construct( $file_path ) {

			$this->file_pointer = fopen( $file_path, 'rb' );

		}

		/**
		 * Close file.
		 *
		 * @since   2.0.12
		 */
		public function __destruct() {
			fclose( $this->file_pointer );
		}

		/**
		 * Import file content
		 *
		 * Import method writes the content of the import file to the database. Security checks:
		 * + Only INSERT INTO is allowed: no other DML, DDL and DCL statements allowed
		 * + Only inserts into the table name provided are allowed
		 * + Use explain to check the number of tables affect: more than 1 looks like SQL injection
		 *
		 * Since wpdb-query() only processes one query at a time we only need to check the type of statement at the
		 * beginning of the script ($file_content).
		 *
		 * This method only return -1 if a failure occurs or the number of rows inserted. The number of probable error
		 * cause is to huge and complex to check all possibilities. Exports created from the WP Data Access table list
		 * should normally import without problems. For manually created imports responsibility is with the developer.
		 *
		 * @param string $schema_name Schema in which this import allowes inserts.
		 * @param string $table_name Table in which this import allowes inserts.
		 * @param string $hide_errors ON = hide errors, OFF = show errors.
		 *
		 * @since   1.0.0
		 */
		public function import( $schema_name, $table_name, $hide_errors ) {
			global $wpdb;

			$wpdadb = WPDADB::get_db_connection( $schema_name );
			if ( null === $wpdadb ) {
				wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $schema_name ) ) );
			}

			$suppress = $wpdadb->suppress_errors( 'on' === $hide_errors );

			$this->file_content = '';
			$rows               = 0;
			$rows_failed        = 0;

			if ( false !== $this->file_pointer ) {
				while ( ! feof( $this->file_pointer ) ) {
					$this->file_content .= fread( $this->file_pointer, 4096 );

					// Replace WP prefix and WPDA prefix.
					$this->file_content = str_replace( '{wp_schema}', $wpdb->dbname, $this->file_content );
					$this->file_content = str_replace( '{wp_prefix}', $wpdb->prefix, (string) $this->file_content );
					$this->file_content = str_replace( '{wpda_prefix}', 'wpda', (string) $this->file_content ); // for backward compatibility

					// Find and process SQL statements.
					$sql_end_unix    = strpos( $this->file_content, ";\n" );
					$sql_end_windows = strpos( $this->file_content, ";\r\n" );
					while ( false !== $sql_end_unix || false !== $sql_end_windows ) {
						if ( false === $sql_end_unix ) {
							$sql_end = $sql_end_windows;
						} elseif ( false === $sql_end_windows ) {
							$sql_end = $sql_end_unix;
						} else {
							$sql_end = min( $sql_end_unix, $sql_end_windows );
						}
						$sql = rtrim( substr( $this->file_content, 0, $sql_end ) );

						$this->file_content = substr( $this->file_content, strpos( $this->file_content, $sql ) + strlen( $sql ) + 1 );
						$rows ++;

						// Write file content to array for security check (150 characters is sufficient to check DML and table name).
						$dml_check = explode( ' ', substr( trim( $sql ), 0, 150 ) );//phpcs:ignore - 8.1 proof

						if ( ! isset( $dml_check[0] ) || ! isset( $dml_check[1] ) ) {
							// No content.
							$rows_failed ++;
						} else {
							// Check first two words (must be insert into, no other statements allowed).
							if ( strtolower( $dml_check[0] . $dml_check[1] ) !== 'insertinto' ) {
								// Only insert into is allowed.
								$rows_failed ++;
							} else {
								// Check table name (using stristr should cover backticks and schema_names as well).
								if ( ! stristr( $dml_check[2], $table_name ) ) {
									$rows_failed ++;
								} else {
									// Insert row.
									if ( false === $wpdadb->query( $sql ) ) {
										$rows_failed ++;
									}
								}
							}
						}

						// Find next SQL statement.
						$sql_end_unix    = strpos( $this->file_content, ";\n" );
						$sql_end_windows = strpos( $this->file_content, ";\r\n" );
					}
				}
			}

			$wpdadb->suppress_errors( $suppress );

			$msg = 'Imported ' . ( $rows - $rows_failed ) . ' rows';
			if ( $rows_failed > 0 ) {
				$msg .= " ($rows_failed failed).";
			} else {
				$msg .= '.';
			}
			$msg = new WPDA_Message_Box(
				array(
					'message_text' => $msg,
				)
			);
			$msg->box();

		}

	}

}
