<?php
/**
 * Import / Export tab.
 *
 * Exports: JSON (full fidelity) and CSV (flat, importable elsewhere).
 * Imports: JSON or CSV. Existing rules are either appended or replaced based
 * on a checkbox.
 *
 * Pro-gated. Free users see the real UI with the inputs disabled and a lock
 * overlay.
 *
 * @package EasySearchReplace
 */

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

/* ---------------------------------------------------------------------------
 * POST/GET handlers
 * ------------------------------------------------------------------------- */

/**
 * Route all actions for this tab.
 */
function esrn_import_export_dispatch() {
	// Export request is GET-driven so browsers treat the response as a download.
	if ( isset( $_GET['esrn_export'] ) ) {
		esrn_handle_export_request();
		return;
	}

	// phpcs:ignore WordPress.Security.NonceVerification.Missing
	if ( isset( $_POST['esrn_import_submit'] ) ) {
		esrn_handle_import_submit();
	}
}

/**
 * Export request handler. Streams JSON or CSV based on ?esrn_export=format.
 *
 * Uses admin_url() nonces to prevent drive-by downloads, although the data is
 * non-sensitive so it's mostly to stop CSRF-based spam.
 */
function esrn_handle_export_request() {
	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Permission denied.', 'easy-search-replace' ), '', array( 'response' => 403 ) );
	}
	if ( ! ESRN_Freemius_Gate::is_pro() ) {
		wp_die( esc_html__( 'Pro license required.', 'easy-search-replace' ), '', array( 'response' => 403 ) );
	}
	check_admin_referer( 'esrn_export' );

	$format  = isset( $_GET['esrn_export'] ) ? sanitize_key( wp_unslash( $_GET['esrn_export'] ) ) : 'json';
	$options = get_option( ESRN_OPTION_KEY, array() );
	$rules   = ( isset( $options['fields'] ) && is_array( $options['fields'] ) ) ? $options['fields'] : array();
	$stamp   = gmdate( 'Y-m-d-Hi' );

	// Normalize every rule so the export is schema-consistent.
	$clean = array();
	foreach ( $rules as $r ) {
		$clean[] = ESRN_Rule_Schema::normalize( $r );
	}

	if ( 'csv' === $format ) {
		esrn_stream_csv_export( $clean, "easy-search-replace-{$stamp}.csv" );
	} else {
		esrn_stream_json_export( $clean, "easy-search-replace-{$stamp}.json" );
	}
	exit;
}

/**
 * Stream a JSON export. Includes schema version so imports can migrate.
 *
 * @param array  $rules    Normalized rules.
 * @param string $filename Download filename.
 */
function esrn_stream_json_export( $rules, $filename ) {
	$payload = array(
		'plugin'        => 'easy-search-replace',
		'schema'        => ESRN_Rule_Schema::VERSION,
		'exported_at'   => gmdate( 'c' ),
		'rules_count'   => count( $rules ),
		'rules'         => $rules,
	);

	nocache_headers();
	header( 'Content-Type: application/json; charset=utf-8' );
	header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
	echo wp_json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
}

/**
 * Stream a CSV export. Flat columns; array/object fields are JSON-encoded
 * in their cells so the file round-trips cleanly on import.
 *
 * @param array  $rules    Normalized rules.
 * @param string $filename Download filename.
 */
function esrn_stream_csv_export( $rules, $filename ) {
	$columns = array(
		'enabled', 'search', 'replace', 'ignore_case',
		'post_types', 'post_ids', 'urls',
		'is_regex', 'selector',
		'schedule_enabled', 'start_date', 'end_date', 'start_time', 'end_time', 'days',
		'roles', 'logged_in', 'device',
		'exclude_post_types', 'exclude_post_ids', 'exclude_urls',
	);

	nocache_headers();
	header( 'Content-Type: text/csv; charset=utf-8' );
	header( 'Content-Disposition: attachment; filename="' . $filename . '"' );

	$out = fopen( 'php://output', 'w' );
	fputcsv( $out, $columns );

	foreach ( $rules as $r ) {
		$sched = $r['schedule'];
		$row   = array(
			$r['enabled'],
			$r['search'],
			$r['replace'],
			$r['ignore_case'],
			implode( '|', (array) $r['post_types'] ),
			$r['post_ids'],
			str_replace( "\n", '|', (string) $r['urls'] ),
			$r['is_regex'],
			$r['selector'],
			$sched['enabled'],
			$sched['start_date'],
			$sched['end_date'],
			$sched['start_time'],
			$sched['end_time'],
			implode( '|', array_map( 'intval', (array) $sched['days'] ) ),
			implode( '|', (array) $r['roles'] ),
			$r['logged_in'],
			$r['device'],
			implode( '|', (array) $r['exclude_post_types'] ),
			$r['exclude_post_ids'],
			str_replace( "\n", '|', (string) $r['exclude_urls'] ),
		);
		fputcsv( $out, $row );
	}

	fclose( $out );
}

/**
 * Import submit handler.
 */
function esrn_handle_import_submit() {
	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Permission denied.', 'easy-search-replace' ), '', array( 'response' => 403 ) );
	}
	if ( ! ESRN_Freemius_Gate::is_pro() ) {
		wp_die( esc_html__( 'Pro license required.', 'easy-search-replace' ), '', array( 'response' => 403 ) );
	}
	check_admin_referer( 'esrn_import' );

	if ( empty( $_FILES['esrn_import_file']['tmp_name'] ) ) {
		esrn_flash( 'error', __( 'Please choose a file to import.', 'easy-search-replace' ) );
		return;
	}

	$tmp      = $_FILES['esrn_import_file']['tmp_name']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
	$filename = isset( $_FILES['esrn_import_file']['name'] ) ? sanitize_file_name( wp_unslash( $_FILES['esrn_import_file']['name'] ) ) : 'import';
	$ext      = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );

	$contents = file_get_contents( $tmp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
	if ( false === $contents ) {
		esrn_flash( 'error', __( 'Could not read the uploaded file.', 'easy-search-replace' ) );
		return;
	}

	// Limit file size defensively (2 MB).
	if ( strlen( $contents ) > 2 * 1024 * 1024 ) {
		esrn_flash( 'error', __( 'File is too large (max 2 MB).', 'easy-search-replace' ) );
		return;
	}

	$imported = array();
	if ( 'csv' === $ext ) {
		$imported = esrn_parse_csv_import( $contents );
	} else {
		$imported = esrn_parse_json_import( $contents );
	}

	if ( is_wp_error( $imported ) ) {
		esrn_flash( 'error', $imported->get_error_message() );
		return;
	}

	if ( empty( $imported ) ) {
		esrn_flash( 'error', __( 'The file contained no rules.', 'easy-search-replace' ) );
		return;
	}

	// phpcs:ignore WordPress.Security.NonceVerification.Missing
	$replace = ! empty( $_POST['esrn_import_replace'] );

	$options  = get_option( ESRN_OPTION_KEY, array() );
	$existing = ( isset( $options['fields'] ) && is_array( $options['fields'] ) ) ? $options['fields'] : array();

	if ( $replace ) {
		$options['fields'] = $imported;
	} else {
		$options['fields'] = array_merge( $existing, $imported );
	}

	update_option( ESRN_OPTION_KEY, $options );

	esrn_flash(
		'success',
		sprintf(
			/* translators: 1: rules imported, 2: total rules after import */
			__( 'Imported %1$d rule(s). Total: %2$d.', 'easy-search-replace' ),
			count( $imported ),
			count( $options['fields'] )
		)
	);
}

/**
 * Parse a JSON import payload.
 *
 * @param string $contents Raw file contents.
 * @return array|WP_Error
 */
function esrn_parse_json_import( $contents ) {
	$data = json_decode( $contents, true );
	if ( null === $data || ! is_array( $data ) ) {
		return new WP_Error( 'json_parse', __( 'The JSON file is invalid.', 'easy-search-replace' ) );
	}

	// Accept both the full export envelope AND a bare array of rules.
	$rules = isset( $data['rules'] ) && is_array( $data['rules'] ) ? $data['rules'] : $data;
	if ( ! is_array( $rules ) ) {
		return new WP_Error( 'json_shape', __( 'The JSON file does not contain a rules array.', 'easy-search-replace' ) );
	}

	$out = array();
	foreach ( $rules as $r ) {
		if ( ! is_array( $r ) ) {
			continue;
		}
		$out[] = ESRN_Rule_Schema::normalize( $r );
	}
	return $out;
}

/**
 * Parse a CSV import. Expects the header row to match our export columns.
 *
 * @param string $contents Raw CSV contents.
 * @return array|WP_Error
 */
function esrn_parse_csv_import( $contents ) {
	$lines = preg_split( '/\r\n|\r|\n/', $contents );
	if ( empty( $lines ) ) {
		return new WP_Error( 'csv_empty', __( 'The CSV file appears to be empty.', 'easy-search-replace' ) );
	}

	$header = str_getcsv( array_shift( $lines ) );
	if ( empty( $header ) ) {
		return new WP_Error( 'csv_header', __( 'The CSV file is missing a header row.', 'easy-search-replace' ) );
	}

	$header = array_map( 'strtolower', array_map( 'trim', $header ) );
	$out    = array();

	foreach ( $lines as $line ) {
		if ( '' === trim( $line ) ) {
			continue;
		}
		$cells = str_getcsv( $line );
		if ( count( $cells ) !== count( $header ) ) {
			continue; // Skip malformed rows silently.
		}
		$row = array_combine( $header, $cells );
		if ( false === $row ) {
			continue;
		}

		$rule = ESRN_Rule_Schema::defaults();
		foreach ( array( 'enabled', 'search', 'replace', 'ignore_case', 'post_ids', 'urls', 'is_regex', 'selector', 'logged_in', 'device', 'exclude_post_ids', 'exclude_urls' ) as $k ) {
			if ( isset( $row[ $k ] ) ) {
				$rule[ $k ] = $row[ $k ];
			}
		}
		foreach ( array( 'enabled', 'ignore_case', 'is_regex' ) as $b ) {
			$val        = isset( $row[ $b ] ) ? $row[ $b ] : '';
			$rule[ $b ] = ( '' === $val || '0' === $val ) ? 0 : 1;
		}
		foreach ( array( 'post_types', 'roles', 'exclude_post_types' ) as $list_k ) {
			$rule[ $list_k ] = isset( $row[ $list_k ] ) && '' !== $row[ $list_k ]
				? array_filter( array_map( 'trim', explode( '|', $row[ $list_k ] ) ) )
				: array();
		}
		// URL fields use '|' in CSV, newlines internally.
		$rule['urls']         = str_replace( '|', "\n", (string) $rule['urls'] );
		$rule['exclude_urls'] = str_replace( '|', "\n", (string) $rule['exclude_urls'] );

		$rule['schedule'] = array(
			'enabled'    => ! empty( $row['schedule_enabled'] ) && '0' !== $row['schedule_enabled'] ? 1 : 0,
			'start_date' => isset( $row['start_date'] ) ? (string) $row['start_date'] : '',
			'end_date'   => isset( $row['end_date'] )   ? (string) $row['end_date']   : '',
			'start_time' => isset( $row['start_time'] ) ? (string) $row['start_time'] : '',
			'end_time'   => isset( $row['end_time'] )   ? (string) $row['end_time']   : '',
			'days'       => isset( $row['days'] ) && '' !== $row['days']
				? array_map( 'intval', array_filter( array_map( 'trim', explode( '|', $row['days'] ) ) ) )
				: array(),
		);

		$out[] = ESRN_Rule_Schema::normalize( $rule );
	}

	return $out;
}

/* ---------------------------------------------------------------------------
 * Render
 * ------------------------------------------------------------------------- */

/**
 * Render the Import / Export tab.
 */
function esrn_render_tab_import_export() {
	esrn_import_export_dispatch();

	$is_pro = ESRN_Freemius_Gate::is_pro();

	$options     = get_option( ESRN_OPTION_KEY, array() );
	$rules_count = isset( $options['fields'] ) && is_array( $options['fields'] ) ? count( $options['fields'] ) : 0;

	$export_json_url = wp_nonce_url( admin_url( 'options-general.php?page=easy_search_replace&tab=import-export&esrn_export=json' ), 'esrn_export' );
	$export_csv_url  = wp_nonce_url( admin_url( 'options-general.php?page=easy_search_replace&tab=import-export&esrn_export=csv' ),  'esrn_export' );
	?>

	<div class="esr-panel">
		<div class="esr-panel-head">
			<h2><?php esc_html_e( 'Export', 'easy-search-replace' ); ?></h2>
			<p class="esr-muted">
				<?php
				echo esc_html(
					sprintf(
						/* translators: %d: number of rules */
						_n( 'You have %d rule ready to export.', 'You have %d rules ready to export.', $rules_count, 'easy-search-replace' ),
						$rules_count
					)
				);
				?>
			</p>
		</div>
		<div class="esr-panel-body">
			<?php esrn_open_locked_block( $is_pro, 'export' ); ?>
				<div class="esr-inline-actions">
					<a href="<?php echo esc_url( $export_json_url ); ?>" class="esr-btn esr-btn-secondary <?php echo $is_pro ? '' : 'esr-disabled'; ?>">
						<span class="dashicons dashicons-download" aria-hidden="true"></span>
						<?php esc_html_e( 'Download JSON', 'easy-search-replace' ); ?>
					</a>
					<a href="<?php echo esc_url( $export_csv_url ); ?>" class="esr-btn esr-btn-secondary <?php echo $is_pro ? '' : 'esr-disabled'; ?>">
						<span class="dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>
						<?php esc_html_e( 'Download CSV', 'easy-search-replace' ); ?>
					</a>
					<span class="esr-muted">
						<?php esc_html_e( 'JSON keeps full fidelity. CSV is easier to edit in a spreadsheet.', 'easy-search-replace' ); ?>
					</span>
				</div>
			<?php esrn_close_locked_block( $is_pro, 'export' ); ?>
		</div>
	</div>

	<div class="esr-panel">
		<div class="esr-panel-head">
			<h2><?php esc_html_e( 'Import', 'easy-search-replace' ); ?></h2>
			<p class="esr-muted">
				<?php esc_html_e( 'Upload a JSON or CSV file exported from this plugin. Max 2 MB.', 'easy-search-replace' ); ?>
			</p>
		</div>
		<div class="esr-panel-body">
			<?php esrn_open_locked_block( $is_pro, 'import' ); ?>
				<form method="post" action="" enctype="multipart/form-data" class="esr-import-form">
					<?php wp_nonce_field( 'esrn_import' ); ?>

					<div class="esr-field">
						<label for="esrn_import_file"><?php esc_html_e( 'File', 'easy-search-replace' ); ?></label>
						<input type="file" id="esrn_import_file" name="esrn_import_file" accept=".json,.csv,application/json,text/csv" <?php disabled( ! $is_pro ); ?> />
					</div>

					<div class="esr-field">
						<label class="esr-checkbox">
							<input type="checkbox" name="esrn_import_replace" value="1" <?php disabled( ! $is_pro ); ?> />
							<span><?php esc_html_e( 'Replace existing rules (instead of appending)', 'easy-search-replace' ); ?></span>
						</label>
					</div>

					<div class="esr-inline-actions">
						<button type="submit" name="esrn_import_submit" class="esr-btn esr-btn-primary" <?php disabled( ! $is_pro ); ?>>
							<span class="dashicons dashicons-upload" aria-hidden="true"></span>
							<?php esc_html_e( 'Import rules', 'easy-search-replace' ); ?>
						</button>
					</div>
				</form>
			<?php esrn_close_locked_block( $is_pro, 'import' ); ?>
		</div>
	</div>
	<?php
}
