<?php
/**
 * Import/Export Utility Functions (V2)
 *
 * This file contains all utility functions used by the V2 import/export system.
 * These functions are completely independent from the old /app/ functions.
 *
 * @package SeedProd
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Check if theme templates exist.
 *
 * @return boolean True if theme templates exist, false otherwise.
 */
function seedprod_lite_v2_has_theme_templates() {
	$args = array(
		'post_type'      => 'seedprod',
		'posts_per_page' => 1,
		'post_status'    => 'any',
		'meta_query'     => array(
			array(
				'key'   => '_seedprod_is_theme_template',
				'value' => true,
			),
		),
		'fields'         => 'ids',
	);

	$query = new WP_Query( $args );
	return $query->have_posts();
}

/**
 * Delete all existing theme templates.
 *
 * @return integer Number of templates deleted.
 */
function seedprod_lite_v2_delete_theme_templates() {
	$args = array(
		'post_type'      => 'seedprod',
		'posts_per_page' => -1,
		'post_status'    => 'any',
		'meta_query'     => array(
			array(
				'key'   => '_seedprod_is_theme_template',
				'value' => true,
			),
		),
		'fields'         => 'ids',
	);

	$query = new WP_Query( $args );
	$count = 0;

	if ( $query->have_posts() ) {
		foreach ( $query->posts as $post_id ) {
				wp_delete_post( $post_id, true ); // Force delete.
			++$count;
		}
	}

	return $count;
}

/**
 * Recursively remove directory (V2 implementation).
 *
 * @param string $dir Directory path to remove.
 * @return boolean True on success, false on failure.
 */
function seedprod_lite_v2_recursive_rmdir( $dir ) {
	if ( ! is_dir( $dir ) ) {
		return false;
	}

	$files = array_diff( scandir( $dir ), array( '.', '..' ) );
	foreach ( $files as $file ) {
		$path = "$dir/$file";
		if ( is_dir( $path ) ) {
			seedprod_lite_v2_recursive_rmdir( $path );
		} else {
			wp_delete_file( $path );
		}
	}
	return rmdir( $dir ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- No WP alternative for rmdir, used within controlled recursive delete function.
}

/**
 * Validate import ZIP file (V2 implementation).
 * Checks ZIP file structure and contents for security.
 *
 * @param string  $zip_file Path to ZIP file.
 * @param boolean $is_theme Whether this is a theme import (vs landing page).
 * @return boolean|WP_Error True if valid, WP_Error on failure.
 */
function seedprod_lite_v2_validate_import_zip( $zip_file, $is_theme = false ) {
	if ( ! class_exists( 'ZipArchive' ) ) {
		return new WP_Error( 'missing_ziparchive', __( 'ZipArchive class not available', 'coming-soon' ) );
	}

	$zip = new ZipArchive();

	if ( $zip->open( $zip_file ) !== true ) {
		return new WP_Error( 'invalid_zip', __( 'Unable to open zip file', 'coming-soon' ) );
	}

	// Check for required JSON file.
	$required_file     = $is_theme ? 'export_theme.json' : 'export_page.json';
	$has_required_file = false;

	// Validate file structure.
	$allowed_extensions = array( 'json', 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'css', 'ico', 'bmp', 'tiff' );
	$max_file_size      = 100 * 1024 * 1024; // 100MB max per file.

	for ( $i = 0; $i < $zip->numFiles; $i++ ) {
		$stat     = $zip->statIndex( $i );
		$filename = $stat['name'];

		// Check for directory traversal attempts.
		if ( false !== strpos( $filename, '..' ) || 0 === strpos( $filename, '/' ) ) {
			$zip->close();
			return new WP_Error( 'security_risk', __( 'Invalid file path detected in ZIP', 'coming-soon' ) );
		}

		// Check for required file.
		if ( basename( $filename ) === $required_file ) {
			$has_required_file = true;
		}

		// Skip directories.
		if ( '/' === substr( $filename, -1 ) ) {
			continue;
		}

		// Check file extension.
		$ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
		if ( ! in_array( $ext, $allowed_extensions, true ) ) {
			$zip->close();
			/* translators: %s: File extension */
			return new WP_Error( 'invalid_file_type', sprintf( __( 'Invalid file type: %s', 'coming-soon' ), $ext ) );
		}

		// Check file size.
		if ( $stat['size'] > $max_file_size ) {
			$zip->close();
			/* translators: %s: Filename */
			return new WP_Error( 'file_too_large', sprintf( __( 'File too large: %s', 'coming-soon' ), $filename ) );
		}
	}

	$zip->close();

	if ( ! $has_required_file ) {
		$error_msg = $is_theme ?
			__( 'Theme data file (export_theme.json) not found in ZIP', 'coming-soon' ) :
			__( 'Landing page data file (export_page.json) not found in ZIP', 'coming-soon' );
		return new WP_Error( 'missing_data_file', $error_msg );
	}

	return true;
}

/**
 * Process image filenames for export (V2 implementation).
 * Extracts and processes images from page data.
 *
 * @param string $data Page data (JSON string).
 * @param string $html Page HTML.
 * @return array Processed data with images array.
 */
function seedprod_lite_v2_process_image_filenames( $data, $html ) {
	// Check for exclusion domains but log what we find.
	$has_unsplash        = false !== strpos( $data, 'unsplash.com' );
	$has_placehold       = false !== strpos( $data, 'placehold.co' );
	$has_assets_seedprod = false !== strpos( $data, 'assets.seedprod.com' );

	// Enhanced regex pattern matching the old function.
	$regex = '/(http)[^\s\'"]+?\.(png|jpg|jpeg|gif|ico|svg|bmp|tiff|webp)[^\s\'"]*?(?=[\'"])/i';

	// if this is a template return - but only if it's ONLY these domains.
	if ( $has_unsplash && ! $has_placehold && ! $has_assets_seedprod ) {
		// Check if there are any other images besides unsplash.
		preg_match_all( $regex, $data, $temp_matches );
		$non_unsplash_images = array_filter(
			$temp_matches[0],
			function ( $url ) {
				return strpos( $url, 'unsplash.com' ) === false;
			}
		);

		if ( empty( $non_unsplash_images ) ) {
			return array(
				'data'   => $data,
				'html'   => $html,
				'images' => array(),
			);
		}
	}

	if ( $has_placehold && ! $has_unsplash && ! $has_assets_seedprod ) {
		// Check if there are any other images besides placehold.
		preg_match_all( $regex, $data, $temp_matches );
		$non_placehold_images = array_filter(
			$temp_matches[0],
			function ( $url ) {
				return false === strpos( $url, 'placehold.co' );
			}
		);

		if ( empty( $non_placehold_images ) ) {
			return array(
				'data'   => $data,
				'html'   => $html,
				'images' => array(),
			);
		}
	}

	$output = array(
		'data'   => '',
		'html'   => '',
		'images' => array(),
	);

	$img_srcs_data = array();
	$img_srcs_html = array();

	preg_match_all( $regex, $data, $img_srcs_data );
	preg_match_all( $regex, $html, $img_srcs_html );

	$img_srcs    = array();
	$img_srcs[0] = array_merge( $img_srcs_data[0], $img_srcs_html[0] );
	$img_srcs[2] = array_merge( $img_srcs_data[2], $img_srcs_html[2] );

	// Eliminate duplicates & pair with extension match from above.
	$unique_img_srcs_extensions = array();
	foreach ( $img_srcs[0] as $index => $img_src ) {
		$unique_img_srcs_extensions[ $img_src ] = $img_srcs[2][ $index ];
	}

	// Need to decode data as WordPress is encoding special characters such as & to &amp; which is.
	// interfering with Unsplash URLs & making it hard to find / replace URLs in strings.
	$processed_data = wp_specialchars_decode( $data );
	$processed_html = wp_specialchars_decode( $html );

	$upload_dir = wp_upload_dir();
	$contentdir = trailingslashit( $upload_dir['baseurl'] ) . 'seedprod-themes-exports/';

	foreach ( $unique_img_srcs_extensions as $old_url => $extension ) {
		// Skip specific excluded domains.
		if ( false !== strpos( $old_url, 'unsplash.com' ) ) {
			continue;
		}

		if ( false !== strpos( $old_url, 'placehold.co' ) ) {
			continue;
		}

		if ( false !== strpos( $old_url, 'assets.seedprod.com' ) ) {
			continue;
		}
		if ( false !== strpos( $old_url, 'w3.org' ) ) {
			continue;
		}

		$prefix = 'theme-builder';

		// Likewise, decode search string.
		$old_url_decoded = wp_specialchars_decode( $old_url );

		// Fix URL mismatch: Replace old domain with current WordPress domain.
		$current_upload_baseurl = $upload_dir['baseurl'];

		// Extract the file path from the old URL (everything after /wp-content/uploads/).
		if ( preg_match( '#/wp-content/uploads/(.+)$#', $old_url_decoded, $matches ) ) {
			$file_path         = $matches[1];
			$corrected_old_url = $current_upload_baseurl . '/' . $file_path;
			$old_url_decoded   = $corrected_old_url;
		}

		$alphanumeric_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		$random_chars       = substr( str_shuffle( $alphanumeric_chars ), 0, 16 );

		$filename = $prefix . '-' . $random_chars . '.' . $extension;
		$new_url  = $contentdir . $filename;

		// Replace URL for local preview.
		$processed_data = str_replace( $old_url_decoded, $new_url, $processed_data );
		$processed_html = str_replace( $old_url_decoded, $new_url, $processed_html );

		$output['images'][] = array(
			'prefix'    => $prefix,
			'extension' => $extension,
			'filename'  => $filename,
			'old_url'   => $old_url_decoded,
			'new_url'   => $new_url,
		);
	}

	$output['data'] = $processed_data;
	$output['html'] = $processed_html;

	return $output;
}

/**
 * Process image filenames for import (V2 implementation).
 * Replaces image placeholders with actual URLs.
 *
 * @param string $data Page data with image placeholders.
 * @param string $html Page HTML with image placeholders.
 * @return array Processed data and HTML.
 */
function seedprod_lite_v2_process_image_filenames_import( $data, $html ) {
	// Skip if template/placeholder content.
	if ( false !== strpos( $data, 'unsplash.com' ) ) {
		return array(
			'data' => $data,
			'html' => $html,
		);
	}

	$upload_dir = wp_upload_dir();
	$import_url = trailingslashit( $upload_dir['baseurl'] ) . 'seedprod-themes-imports/';

	// Find and replace image placeholders.
	$pattern = '/{{image}}([^{}]+){{\/image}}/';
	preg_match_all( $pattern, $data, $matches );

	if ( ! empty( $matches[0] ) ) {
		foreach ( $matches[0] as $index => $placeholder ) {
			$filename = $matches[1][ $index ];
			$new_url  = $import_url . $filename;

			$data = str_replace( $placeholder, $new_url, $data );
			$html = str_replace( $placeholder, $new_url, $html );
		}
	}

	return array(
		'data' => $data,
		'html' => $html,
	);
}

/**
 * Save images locally (V2 implementation).
 * Downloads remote images and saves them locally.
 *
 * @param array $img_arr Array of image data.
 * @return array Array of images that failed to download.
 */
function seedprod_lite_v2_save_images_locally( $img_arr ) {
	$failed_images = array();

	if ( empty( $img_arr ) ) {
		return $failed_images;
	}

	$upload_dir = wp_upload_dir();
	$export_dir = trailingslashit( $upload_dir['basedir'] ) . 'seedprod-themes-exports/';

	// Ensure export directory exists.
	if ( ! file_exists( $export_dir ) ) {
		wp_mkdir_p( $export_dir );
	}

	foreach ( $img_arr as $index => $image ) {
		if ( empty( $image['old_url'] ) || empty( $image['filename'] ) ) {
			continue;
		}

		// Download image.
		$response = wp_remote_get(
			$image['old_url'],
			array(
				'sslverify' => false,
			)
		);

		if ( is_wp_error( $response ) ) {
			$failed_images[] = $image;
			continue;
		}

		$response_code = wp_remote_retrieve_response_code( $response );

		if ( 200 !== $response_code ) {
			$failed_images[] = $image;
			continue;
		}

		$image_data = wp_remote_retrieve_body( $response );
		if ( empty( $image_data ) ) {
			$failed_images[] = $image;
			continue;
		}

		// Save image to file.
		global $wp_filesystem;
		if ( empty( $wp_filesystem ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();
		}

		$save_path = $export_dir . $image['filename'];
		$saved     = $wp_filesystem->put_contents( $save_path, $image_data, FS_CHMOD_FILE );

		if ( ! $saved ) {
			$failed_images[] = $image;
		}
	}

	return $failed_images;
}

/**
 * Recursively add directory contents to ZIP (V2 implementation).
 *
 * @param string     $source      Source directory path.
 * @param ZipArchive $zip         ZIP archive object.
 * @param integer    $path_length Length of base path to remove.
 * @return void
 */
function seedprod_lite_v2_zipdir( $source, $zip, $path_length ) {
	if ( ! is_dir( $source ) ) {
		return;
	}

	$files = scandir( $source );
	foreach ( $files as $file ) {
		if ( in_array( $file, array( '.', '..' ), true ) ) {
			continue;
		}

		// Skip ZIP files to prevent self-archiving.
		if ( pathinfo( $file, PATHINFO_EXTENSION ) === 'zip' ) {
			continue;
		}

		$file_path  = $source . '/' . $file;
		$local_path = substr( $file_path, $path_length );

		if ( is_dir( $file_path ) ) {
			$zip->addEmptyDir( $local_path );
			seedprod_lite_v2_zipdir( $file_path, $zip, $path_length );
		} else {
			$zip->addFile( $file_path, $local_path );
		}
	}
}

/**
 * Import theme JSON data (V2 implementation).
 * Processes and imports theme data.
 *
 * @param object $json_content Theme data object.
 * @return array Per-image warnings collected from the sideload step.
 */
function seedprod_lite_v2_theme_import_json( $json_content = null ) {

	$warnings = array();

	// Validate input data.
	if ( null === $json_content || ! is_object( $json_content ) ) {
		return $warnings;
	}

	$full_code = $json_content;

	$theme            = isset( $full_code->theme ) ? $full_code->theme : array();
	$shortcode_update = isset( $full_code->mapped ) ? $full_code->mapped : array();
	$old_home_url     = isset( $full_code->current_home_url ) ? $full_code->current_home_url : '';
	$new_home_url     = home_url();

	$imports = array();
	if ( is_array( $theme ) && count( $theme ) > 0 ) {
		foreach ( $theme as $k => $v ) {
			// Browsers do not unescape "\/" inside HTML attributes; normalize so <img src> renders.
			$content          = str_replace( '\\/', '/', base64_decode( $v->post_content ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
			$content_filtered = str_replace( '\\/', '/', base64_decode( $v->post_content_filtered ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
			if ( function_exists( 'seedprod_lite_heal_import_pcf' ) ) {
				$content_filtered = seedprod_lite_heal_import_pcf( $content_filtered );
			}
			$imports[] = array(
				'post_content'          => $content,
				'post_content_filtered' => $content_filtered,
				'post_title'            => base64_decode( $v->post_title ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
				'meta'                  => json_decode( base64_decode( $v->meta ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
				'order'                 => $v->order,
				// New fields for distinguishing theme templates from edited pages.
				'page_category'         => isset( $v->page_category ) ? base64_decode( $v->page_category ) : 'theme_template', // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
				'post_type'             => isset( $v->post_type ) ? base64_decode( $v->post_type ) : 'seedprod', // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
				'post_status'           => isset( $v->post_status ) ? base64_decode( $v->post_status ) : 'publish', // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
			);
		}
	}

	$shortcode_array = array();
	if ( is_array( $shortcode_update ) && count( $shortcode_update ) > 0 ) {
		foreach ( $shortcode_update as $k => $t ) {
			$shortcode_array[] = array(
				'shortcode'  => base64_decode( $t->shortcode ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
				'page_title' => $t->page_title,
			);
		}
	}

	$import_page_array = array();
	if ( count( $imports ) > 0 ) {
		foreach ( $imports as $k1 => $v1 ) {

			$meta = $v1['meta'];

			// Determine if this is an edited page or theme template.
			$page_category      = isset( $v1['page_category'] ) ? $v1['page_category'] : 'theme_template';
			$import_post_type   = isset( $v1['post_type'] ) ? $v1['post_type'] : 'seedprod';
			$import_post_status = isset( $v1['post_status'] ) ? $v1['post_status'] : 'publish';

			if ( 'edited_page' === $page_category ) {
				// Import as WordPress page with _seedprod_edited_with_seedprod meta.
				$data = array(
					'comment_status' => 'closed',
					'menu_order'     => $v1['order'],
					'ping_status'    => 'closed',
					'post_status'    => $import_post_status,
					'post_title'     => $v1['post_title'],
					'post_type'      => 'page',
					'meta_input'     => array(
						'_seedprod_edited_with_seedprod' => '1',
						'_seedprod_page_uuid'            => wp_generate_uuid4(),
					),
				);
			} else {
				// Existing theme template logic.
				$data = array(
					'comment_status' => 'closed',
					'menu_order'     => $v1['order'],
					'ping_status'    => 'closed',
					'post_status'    => 'publish',
					'post_title'     => $v1['post_title'],
					'post_type'      => 'seedprod',
					'meta_input'     => array(
						'_seedprod_page'               => true,
						'_seedprod_is_theme_template'  => true,
						'_seedprod_page_uuid'          => wp_generate_uuid4(),
						'_seedprod_page_template_type' => isset( $meta->_seedprod_page_template_type[0] ) ? $meta->_seedprod_page_template_type[0] : '',
					),
				);
			}

			$id = wp_insert_post(
				$data,
				true
			);

			$import_page_array[] = array(
				'id'                    => $id,
				'title'                 => $v1['post_title'],
				'post_content'          => $v1['post_content'],
				'post_content_filtered' => $v1['post_content_filtered'],
				'page_category'         => $page_category,
			);

			// reinsert settings because wp_insert screws up json.
			$post_content_filtered = $v1['post_content_filtered'];
			$post_content          = $v1['post_content'];

			// For CSS templates, ensure page_type is set in the JSON.
			if ( 'theme_template' === $page_category && isset( $meta->_seedprod_page_template_type[0] ) && 'css' === $meta->_seedprod_page_template_type[0] ) {
				$json_data = json_decode( $post_content_filtered, true );
				if ( null !== $json_data ) {
					// Ensure page_type is set at the root level.
					$json_data['page_type'] = 'css';
					$post_content_filtered  = wp_json_encode( $json_data );
				}
			}

			global $wpdb;
			$tablename = esc_sql( $wpdb->prefix . 'posts' );
			$sql       = "UPDATE $tablename SET post_content_filtered = %s,post_content = %s WHERE id = %d";
			$safe_sql  = $wpdb->prepare( $sql, $post_content_filtered, $post_content, $id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
			$wpdb->query( $safe_sql );

			// Handle edited pages differently - they don't have theme template conditions.
			if ( 'edited_page' === $page_category ) {
				// Find and replace preview urls for edited pages.
				$new_post_content = str_replace( $old_home_url, $new_home_url, $v1['post_content'] );
				$new_post_content = str_replace( 'seedprod-themes-exports', 'seedprod-themes-imports', $new_post_content );
				// Extract CSS from page content.
				if ( function_exists( 'seedprod_lite_extract_page_css' ) ) {
					$code = seedprod_lite_extract_page_css( $new_post_content, $id );
					update_post_meta( $id, '_seedprod_css', $code['css'] );
					update_post_meta( $id, '_seedprod_html', $code['html'] );

					if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
						seedprod_lite_generate_css_file( $id, $code['css'] );
					}
				}
				// Don't continue - fall through to shortcode processing loop below.
			} else {
				// Add meta for theme templates.
				if ( isset( $meta->_seedprod_page_template_type[0] ) && 'css' === $meta->_seedprod_page_template_type[0] ) {
				// Set css file.
				// Find and replace url.
				$css = str_replace( $old_home_url, $new_home_url, $v1['post_content'] );
				$css = str_replace( 'seedprod-themes-exports', 'seedprod-themes-imports', $css );
				// Custom CSS is intentionally set to empty (following old logic).
				$custom_css  = '';
				$builder_css = isset( $meta->_seedprod_builder_css[0] ) ? $meta->_seedprod_builder_css[0] : '';

				update_post_meta( $id, '_seedprod_css', $css );
				update_post_meta( $id, '_seedprod_custom_css', $custom_css );
				update_post_meta( $id, '_seedprod_builder_css', $builder_css );
				update_option( 'global_css_page_id', $id );
				// Generate CSS with proper @import handling.
				$css = seedprod_lite_merge_global_custom_css( $css, $custom_css );

				// Trash current css file and set css file pointer.
				$current_css_file = get_option( 'seedprod_global_css_page_id' );
				if ( ! empty( $current_css_file ) ) {
					wp_trash_post( $current_css_file );
				}

				update_option( 'seedprod_global_css_page_id', $id );

				// Generate CSS file.
				if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
					seedprod_lite_generate_css_file( $id, $css );
				}
			} else {
				// Find and replace preview urls.
				$new_post_content = str_replace( $old_home_url, $new_home_url, $v1['post_content'] );
				$new_post_content = str_replace( 'seedprod-themes-exports', 'seedprod-themes-imports', $new_post_content );
				// Extract CSS from page content.
				if ( function_exists( 'seedprod_lite_extract_page_css' ) ) {
					$code = seedprod_lite_extract_page_css( $new_post_content, $id );
					update_post_meta( $id, '_seedprod_css', $code['css'] );
					update_post_meta( $id, '_seedprod_html', $code['html'] );

					if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
						seedprod_lite_generate_css_file( $id, $code['css'] );
					}
				}
				$template_condition = isset( $meta->_seedprod_theme_template_condition[0] ) ? seedprod_lite_normalize_conditions_json( $meta->_seedprod_theme_template_condition[0] ) : '';
				update_post_meta( $id, '_seedprod_theme_template_condition', $template_condition );
				// Process conditon to see if we need to create a placeholder page.
				$conditions = $template_condition;

				if ( ! empty( $conditions ) ) {

					$conditions = json_decode( $conditions );
					if ( is_array( $conditions ) ) {
						if ( 1 === count( $conditions ) && 'include' === $conditions[0]->condition && 'is_page(x)' === $conditions[0]->type && ! empty( $conditions[0]->value ) && ! is_numeric( $conditions[0]->value ) ) {
							// Check if slug exists.
							$slug_tablename = esc_sql( $wpdb->prefix . 'posts' );
							$sql            = "SELECT id FROM $slug_tablename WHERE post_name = %s AND post_type = 'page' AND post_status != 'trash'";
							$safe_sql       = $wpdb->prepare( $sql, $conditions[0]->value ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
							// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
							$this_slug_exist = $wpdb->get_var( $safe_sql );
							if ( empty( $this_slug_exist ) ) {
								// Create page with content.
								$page_details                  = array(
									'post_title'   => $v1['post_title'],
									'post_name'    => $conditions[0]->value,
									'post_content' => $new_post_content,
									'post_status'  => 'publish',
									'post_type'    => 'page',
								);
								$seedprod_remove_page_template = apply_filters( 'seedprod_remove_page_template', true );
								if ( $seedprod_remove_page_template ) {
									$new_page_id = wp_insert_post( $page_details );
									if ( ! empty( $new_page_id ) ) {
										// Add meta.
										update_post_meta( $new_page_id, '_seedprod_edited_with_seedprod', '1' );
										// Reinsert settings because wp_insert screws up json.
										$post_content_filtered_new_page = $v1['post_content_filtered'];
										global $wpdb;
										$tablename = esc_sql( $wpdb->prefix . 'posts' );
										$sql       = "UPDATE $tablename SET post_content_filtered = %s,post_content = %s WHERE id = %d";
										$safe_sql  = $wpdb->prepare( $sql, $post_content_filtered_new_page, $new_post_content, $new_page_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
										// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
										$wpdb->query( $safe_sql );
										// Update import array map with new id.
										foreach ( $import_page_array as $k5 => $v5 ) {
											if ( $id == $v5['id'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison -- Intentional loose comparison for mixed types.
												$import_page_array[ $k5 ]['id'] = $new_page_id;
											}
										}

										// Remove template page.
										wp_delete_post( $id, true );

									}
								} else {
									// Add place holder page.
									wp_insert_post( $page_details );
								}
							}
						}
					}
				}
			}
			} // End else block for edited_page check.
		}
	}

	// Find and replace shortcodes.
	if ( count( $import_page_array ) > 0 ) {
		foreach ( $import_page_array as $t => $val ) {
			if ( 'Global CSS' !== $val['title'] ) {
				$post_content          = $val['post_content'];
				$post_content_filtered = $val['post_content_filtered'];
				$post_id               = $val['id'];

				// Process image filenames.
				if ( function_exists( 'seedprod_lite_process_image_filenames_import_theme' ) ) {
					$processed_data_import = seedprod_lite_process_image_filenames_import_theme( $post_content_filtered, $post_content );
					$post_content          = $processed_data_import['html'];
					$post_content_filtered = $processed_data_import['data'];
					if ( ! empty( $processed_data_import['warnings'] ) ) {
						$warnings = array_merge( $warnings, $processed_data_import['warnings'] );
					}
				}

				// Extract and generate CSS for non-Global CSS templates.
				if ( function_exists( 'seedprod_lite_extract_page_css' ) ) {
					$code = seedprod_lite_extract_page_css( $post_content, $post_id );
					update_post_meta( $post_id, '_seedprod_css', $code['css'] );
					update_post_meta( $post_id, '_seedprod_html', $code['html'] );

					if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
						seedprod_lite_generate_css_file( $post_id, $code['css'] );
					}
				}

				if ( count( $shortcode_array ) > 0 ) {
					foreach ( $shortcode_array as $k => $t ) {
						$shortcode_page_title = $shortcode_array[ $k ]['page_title'];
						$fetch_shortcode_key  = array_search( $shortcode_page_title, array_column( $import_page_array, 'title' ), true ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- Handled by third parameter.
						$fetch_shortcode_id   = $import_page_array[ $fetch_shortcode_key ]['id'];

						$shortcode_page_sc = $shortcode_array[ $k ]['shortcode'];
						$shortcode_page_sc = str_replace( '[sp_template_part id="', '', $shortcode_page_sc );
						$shortcode_page_sc = str_replace( '"]', '', $shortcode_page_sc );

						if ( $fetch_shortcode_id ) {
							$shortcode_array[ $k ]['updated_shortcode'] = '[sp_template_part id="' . $fetch_shortcode_id . '"]';
							$post_content                               = str_replace( $shortcode_array[ $k ]['shortcode'], $shortcode_array[ $k ]['updated_shortcode'], $post_content );

							$shortcode_array[ $k ]['updated_shortcode_filtered'] = '"templateparts":"' . $fetch_shortcode_id . '"';
							$shortcode_array[ $k ]['shortcode_filtered_id']      = $shortcode_page_sc;
							$shortcode_array[ $k ]['shortcode_filtered']         = '"templateparts":"' . $shortcode_page_sc . '"';

							$post_content_filtered = str_replace( $shortcode_array[ $k ]['shortcode_filtered'], $shortcode_array[ $k ]['updated_shortcode_filtered'], $post_content_filtered );

							// update generated html.
							$generate_html = get_post_meta( $post_id, '_seedprod_html', true );
							$generate_html = str_replace( $shortcode_array[ $k ]['shortcode'], $shortcode_array[ $k ]['updated_shortcode'], $generate_html );
							update_post_meta( $post_id, '_seedprod_html', $generate_html );
						}
					}
				}

				// Replace any remaining URLs from the source domain.
				if ( ! empty( $old_home_url ) ) {
					$post_content          = str_replace( $old_home_url, $new_home_url, $post_content );
					$post_content_filtered = str_replace( $old_home_url, $new_home_url, $post_content_filtered );
				}

				global $wpdb;
				$tablename = esc_sql( $wpdb->prefix . 'posts' );
				$sql       = "UPDATE $tablename SET post_content_filtered = %s,post_content = %s WHERE id = %d";
				$safe_sql  = $wpdb->prepare( $sql, $post_content_filtered, $post_content, absint( $post_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
				$wpdb->query( $safe_sql );
			}
		}
	}

	return $warnings;
}

/**
 * Import landing page JSON data (V2 implementation).
 * Processes and imports landing page data.
 *
 * @param object $json_content Landing page data object.
 * @return array {
 *     Result of the import.
 *
 *     @type int[] $imported_pages IDs of pages created during the import.
 *     @type array $warnings       Per-image warnings collected from the sideload step.
 * }
 */
function seedprod_lite_v2_landing_import_json( $json_content = null ) {
	$imported_pages = array();
	$warnings       = array();

	// Validate input.
	if ( null === $json_content || ! is_object( $json_content ) ) {
		return array(
			'imported_pages' => $imported_pages,
			'warnings'       => $warnings,
		);
	}

	if ( empty( $json_content->theme ) || ! is_array( $json_content->theme ) ) {
		return array(
			'imported_pages' => $imported_pages,
			'warnings'       => $warnings,
		);
	}

	global $wpdb;
	$tablename = $wpdb->prefix . 'posts';

	$old_home_url = isset( $json_content->current_home_url ) ? $json_content->current_home_url : '';
	$new_home_url = home_url();

	// Get existing special page IDs.
	$csp_id    = get_option( 'seedprod_coming_soon_page_id' );
	$mmp_id    = get_option( 'seedprod_maintenance_mode_page_id' );
	$p404_id   = get_option( 'seedprod_404_page_id' );
	$loginp_id = get_option( 'seedprod_login_page_id' );

	// Track shortcode mappings.
	$shortcode_array = array();
	if ( ! empty( $json_content->mapped ) && is_array( $json_content->mapped ) ) {
		foreach ( $json_content->mapped as $k => $t ) {
			$shortcode_array[] = array(
				'id'         => isset( $t->id ) ? $t->id : '',
				'shortcode' => base64_decode( $t->shortcode ), // phpcs:ignore
				'page_title' => $t->page_title,
			);
		}
	}

	$import_page_array = array();

	// Process each landing page.
	foreach ( $json_content->theme as $v ) {
		// Browsers do not unescape "\/" inside HTML attributes; normalize so <img src> renders.
		$post_content          = ! empty( $v->post_content ) ? str_replace( '\\/', '/', base64_decode( $v->post_content ) ) : '';
		$post_content_filtered = ! empty( $v->post_content_filtered ) ? str_replace( '\\/', '/', base64_decode( $v->post_content_filtered ) ) : '';
		$post_title            = ! empty( $v->post_title ) ? base64_decode( $v->post_title ) : '';
		$post_type             = ! empty( $v->post_type ) ? base64_decode( $v->post_type ) : 'page';
		$post_status           = ! empty( $v->post_status ) ? base64_decode( $v->post_status ) : 'draft';
		$ptype                 = ! empty( $v->ptype ) ? base64_decode( $v->ptype ) : '';
		$meta                  = ! empty( $v->meta ) ? json_decode( base64_decode( $v->meta ), true ) : array();

		if ( function_exists( 'seedprod_lite_heal_import_pcf' ) ) {
			$post_content_filtered = seedprod_lite_heal_import_pcf( $post_content_filtered );
		}

		// Create post.
		$post_data = array(
			'post_title'            => $post_title,
			'post_content'          => $post_content,
			'post_content_filtered' => $post_content_filtered,
			'post_status'           => $post_status,
			'post_type'             => $post_type,
			'menu_order'            => ! empty( $v->order ) ? $v->order : 0,
		);

		$post_id = wp_insert_post( $post_data );

		if ( ! is_wp_error( $post_id ) ) {
			// For CSS templates, ensure page_type is set in the JSON.
			if ( ! empty( $meta['_seedprod_page_template_type'] ) && 'css' === $meta['_seedprod_page_template_type'] ) {
				$json_data = json_decode( $post_content_filtered, true );
				if ( null !== $json_data ) {
					// Ensure page_type is set at the root level.
					$json_data['page_type'] = 'css';
					$post_content_filtered  = wp_json_encode( $json_data );
				}
			}

			// Reinsert settings because wp_insert screws up json (following old working logic).
			if ( ! empty( $post_content_filtered ) ) {
				global $wpdb;
				$tablename = esc_sql( $wpdb->prefix . 'posts' );
				$sql       = "UPDATE $tablename SET post_content_filtered = %s, post_content = %s WHERE id = %d";
				$safe_sql  = $wpdb->prepare( $sql, $post_content_filtered, $post_content, $post_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
				$wpdb->query( $safe_sql );
			}

			$imported_pages[] = $post_id;

			// Store for shortcode mapping.
			$import_page_array[] = array(
				'id'                    => $post_id,
				'title'                 => $post_title,
				'post_content'          => $post_content,
				'post_content_filtered' => $post_content_filtered,
			);

			// Reinsert content to preserve JSON integrity using direct database update.
			$sql      = "UPDATE $tablename SET post_content_filtered = %s, post_content = %s WHERE id = %d";
			$safe_sql = $wpdb->prepare( $sql, $post_content_filtered, $post_content, absint( $post_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
			$wpdb->query( $safe_sql );

			// Add post meta.
			if ( ! empty( $meta ) ) {
				foreach ( $meta as $meta_key => $meta_value ) {
					if ( is_array( $meta_value ) && count( $meta_value ) === 1 ) {
						$meta_value = maybe_unserialize( $meta_value[0] );
					}
					update_post_meta( $post_id, $meta_key, $meta_value );
				}
			}

			// Generate UUID if needed.
			if ( empty( $meta['_seedprod_page_uuid'] ) ) {
				update_post_meta( $post_id, '_seedprod_page_uuid', wp_generate_uuid4() );
			}

			// Set page type if specified.
			if ( ! empty( $ptype ) ) {
				update_post_meta( $post_id, '_seedprod_page_type', $ptype );

				// Update special page options based on ptype.
				if ( 'cs' === $ptype ) {
					update_option( 'seedprod_coming_soon_page_id', $post_id );
				}
				if ( 'mm' === $ptype ) {
					update_option( 'seedprod_maintenance_mode_page_id', $post_id );
				}
				if ( 'p404' === $ptype ) {
					update_option( 'seedprod_404_page_id', $post_id );
				}
				if ( 'loginp' === $ptype ) {
					update_option( 'seedprod_login_page_id', $post_id );
				}
			}
		}
	}

	// Process shortcode remapping, image import, and CSS extraction.
	foreach ( $import_page_array as $t => $val ) {
		$post_content          = $val['post_content'];
		$post_content_filtered = $val['post_content_filtered'];
		$post_id               = $val['id'];

		// Replace old home URL with new home URL.
		if ( ! empty( $old_home_url ) ) {
			$post_content          = str_replace( $old_home_url, $new_home_url, $post_content );
			$post_content_filtered = str_replace( $old_home_url, $new_home_url, $post_content_filtered );
		}

		// Replace export path with import path.
		$post_content          = str_replace( 'seedprod-themes-exports', 'seedprod-themes-imports', $post_content );
		$post_content_filtered = str_replace( 'seedprod-themes-exports', 'seedprod-themes-imports', $post_content_filtered );

		// Import images into the WordPress media library.
		if ( function_exists( 'seedprod_lite_process_image_filenames_import_theme' ) ) {
			$processed_data_import = seedprod_lite_process_image_filenames_import_theme( $post_content_filtered, $post_content );
			$post_content          = $processed_data_import['html'];
			$post_content_filtered = $processed_data_import['data'];
			if ( ! empty( $processed_data_import['warnings'] ) ) {
				$warnings = array_merge( $warnings, $processed_data_import['warnings'] );
			}
		}

		// Replace shortcodes if we have mappings.
		if ( count( $shortcode_array ) > 0 ) {
			foreach ( $shortcode_array as $k => $sc ) {
				$shortcode_page_title = $sc['page_title'];
				$fetch_shortcode_key  = array_search( $shortcode_page_title, array_column( $import_page_array, 'title' ), true ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- Handled by third parameter.

				if ( false !== $fetch_shortcode_key ) {
					$fetch_shortcode_id = $import_page_array[ $fetch_shortcode_key ]['id'];

					$shortcode_page_sc = $sc['shortcode'];
					$shortcode_page_sc = str_replace( '[sp_template_part id="', '', $shortcode_page_sc );
					$shortcode_page_sc = str_replace( '"]', '', $shortcode_page_sc );

					if ( $fetch_shortcode_id ) {
						// Replace in HTML.
						$updated_shortcode = '[sp_template_part id="' . $fetch_shortcode_id . '"]';
						$post_content      = str_replace( $sc['shortcode'], $updated_shortcode, $post_content );

						// Replace in JSON data.
						$old_templateparts     = '"templateparts":"' . $shortcode_page_sc . '"';
						$new_templateparts     = '"templateparts":"' . $fetch_shortcode_id . '"';
						$post_content_filtered = str_replace( $old_templateparts, $new_templateparts, $post_content_filtered );
					}
				}
			}
		}

		// Extract and generate CSS (but not for CSS templates).
		$is_css_template = isset( $meta['_seedprod_page_template_type'] ) &&
							'css' === $meta['_seedprod_page_template_type'];

		if ( $is_css_template ) {
			// For CSS templates (Global CSS), handle special CSS meta.
			// Get CSS from meta if available (following old import logic exactly).
			$css         = '';
			$custom_css  = '';
			$builder_css = '';

			if ( isset( $meta['_seedprod_css'] ) ) {
				$css = str_replace( 'TO_BE_REPLACED', home_url(), $meta['_seedprod_css'] );
			} else {
				// Fallback to post_content if no meta.
				$css = str_replace( 'TO_BE_REPLACED', home_url(), $post_content );
			}

			if ( isset( $meta['_seedprod_custom_css'] ) ) {
				$custom_css = str_replace( 'TO_BE_REPLACED', home_url(), $meta['_seedprod_custom_css'] );
			}
			// Old logic explicitly sets custom_css to empty string.
			$custom_css = '';

			if ( isset( $meta['_seedprod_builder_css'] ) ) {
				$builder_css = str_replace( 'TO_BE_REPLACED', home_url(), $meta['_seedprod_builder_css'] );
			}

			// Update all CSS meta fields.
			update_post_meta( $post_id, '_seedprod_css', $css );
			update_post_meta( $post_id, '_seedprod_custom_css', $custom_css );
			update_post_meta( $post_id, '_seedprod_builder_css', $builder_css );

			// Set BOTH option names (old system uses both).
			update_option( 'global_css_page_id', $post_id );

			// Trash current CSS file and set new pointer (following old logic).
			$current_css_file = get_option( 'seedprod_global_css_page_id' );
			if ( ! empty( $current_css_file ) ) {
				wp_trash_post( $current_css_file );
			}
			update_option( 'seedprod_global_css_page_id', $post_id );

			// Generate CSS file with combined CSS and proper @import handling.
			$combined_css = seedprod_lite_merge_global_custom_css( $css, $custom_css );
			if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
				seedprod_lite_generate_css_file( $post_id, $combined_css );
			}
		} elseif ( function_exists( 'seedprod_lite_extract_page_css' ) ) {
			// For regular templates, extract CSS from HTML.
			$code = seedprod_lite_extract_page_css( $post_content, $post_id );
			update_post_meta( $post_id, '_seedprod_css', $code['css'] );
			update_post_meta( $post_id, '_seedprod_html', $code['html'] );

			if ( function_exists( 'seedprod_lite_generate_css_file' ) ) {
				seedprod_lite_generate_css_file( $post_id, $code['css'] );
			}
		}

		// Update database with remapped content.
		$sql      = "UPDATE $tablename SET post_content_filtered = %s, post_content = %s WHERE id = %d";
		$safe_sql = $wpdb->prepare( $sql, $post_content_filtered, $post_content, $post_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name escaped with esc_sql(), dynamic SQL assembled for prepare().
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name escaped with esc_sql(), values prepared with wpdb->prepare().
		$wpdb->query( $safe_sql );
	}

	return array(
		'imported_pages' => $imported_pages,
		'warnings'       => $warnings,
	);
}

/**
 * Prepare ZIP file for download (V2 implementation).
 * Creates a ZIP file with exported data.
 *
 * @param array  $filenames   Array of filenames to include.
 * @param string $export_json JSON data to include.
 * @param string $type        Export type ('theme' or 'page').
 * @return array Download information including URL and file size.
 * @throws Exception If ZIP operations fail.
 */
function seedprod_lite_v2_prepare_zip( $filenames, $export_json, $type = 'theme' ) {
	global $wp_filesystem;

	if ( empty( $wp_filesystem ) ) {
		require_once ABSPATH . 'wp-admin/includes/file.php';
		WP_Filesystem();
	}

	$upload_dir = wp_upload_dir();
	$export_dir = trailingslashit( $upload_dir['basedir'] ) . 'seedprod-themes-exports/';

	// Determine JSON filename based on type.
	$json_filename = ( 'theme' === $type ) ? 'export_theme.json' : 'export_page.json';

	// Save JSON file.
	$json_path  = $export_dir . $json_filename;
	$json_saved = $wp_filesystem->put_contents( $json_path, $export_json, FS_CHMOD_FILE );

	if ( ! $json_saved ) {
		throw new Exception( esc_html__( 'Failed to save export JSON file.', 'coming-soon' ) );
	}

	// Create ZIP file.
	if ( ! class_exists( 'ZipArchive' ) ) {
		throw new Exception( esc_html__( 'ZipArchive class not available. Please contact your host.', 'coming-soon' ) );
	}

	$zip          = new ZipArchive();
	$zip_filename = 'seedprod-' . $type . '-export-' . gmdate( 'Y-m-d-His' ) . '.zip';
	$zip_path     = $export_dir . $zip_filename;

	$zip_open_result = $zip->open( $zip_path, ZipArchive::CREATE );
	if ( true !== $zip_open_result ) {
		// translators: %s: ZipArchive error code.
		throw new Exception( sprintf( esc_html__( 'Cannot create ZIP file. Error code: %s', 'coming-soon' ), esc_html( $zip_open_result ) ) );
	}

	// Add JSON file.
	if ( ! $zip->addFile( $json_path, $json_filename ) ) {
		$zip->close();
		throw new Exception( esc_html__( 'Failed to add JSON file to ZIP.', 'coming-soon' ) );
	}

	// Add image files.
	foreach ( $filenames as $filename ) {
		$file_path = $export_dir . $filename;
		if ( file_exists( $file_path ) ) {
			$zip->addFile( $file_path, $filename );
		}
	}

	$zip->close();

	// Verify ZIP was created.
	if ( ! file_exists( $zip_path ) ) {
		throw new Exception( esc_html__( 'ZIP file was not created successfully.', 'coming-soon' ) );
	}

	$zip_size = filesize( $zip_path );

	// Clean up temporary files (keep ZIP for download).
	wp_delete_file( $json_path );
	foreach ( $filenames as $filename ) {
		wp_delete_file( $export_dir . $filename );
	}

	// Read ZIP and base64-encode for inline delivery.
	// This ensures downloads work in all environments including WordPress Playground.
	$zip_contents = file_get_contents( $zip_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
	if ( false === $zip_contents ) {
		throw new Exception( esc_html__( 'Failed to read export file.', 'coming-soon' ) );
	}
	$zip_base64 = base64_encode( $zip_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
	unset( $zip_contents );

	// Clean up ZIP file since it is delivered inline.
	wp_delete_file( $zip_path );

	return array(
		'success'  => true,
		'filedata' => $zip_base64,
		'filename' => $zip_filename,
	);
}
