<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
function drop_print_sku_migration_build_variation_row( int $variation_id ): ?array {
	$sku = get_post_meta( $variation_id, '_drop_print_sku', true );
	if ( empty( $sku ) || ! is_string( $sku ) ) {
		return null;
	}
	$variation_product = wc_get_product( $variation_id );
	if ( ! $variation_product ) {
		return null;
	}
	return array(
		'variation_id' => $variation_id,
		'size_label'   => get_the_title( $variation_id ),
		'old_sku'      => $sku,
		'price'        => $variation_product->get_regular_price(),
		'sizing'       => get_post_meta( $variation_id, '_drop_print_sizing', true ) ?: 'fillPrintArea',
	);
}
function drop_print_sku_migration_collect_variations(): array {
	$product_ids = get_posts(
		array(
			'post_type'              => 'product',
			'post_status'            => 'any',
			'posts_per_page'         => -1,
			'fields'                 => 'ids',
			'no_found_rows'          => true,
			'update_post_meta_cache' => false,
			'update_post_term_cache' => false,
			'meta_key'               => '_drop_print_meta',
		)
	);
	$products = array();
	foreach ( $product_ids as $product_id ) {
		$product_id = absint( $product_id );
		$product    = wc_get_product( $product_id );
		if ( ! $product || ! $product->is_type( 'variable' ) ) {
			continue;
		}
		$variation_ids = $product->get_children();
		if ( empty( $variation_ids ) ) {
			continue;
		}
		$rows = array();
		foreach ( $variation_ids as $variation_id ) {
			$row = drop_print_sku_migration_build_variation_row( absint( $variation_id ) );
			if ( null !== $row ) {
				$rows[] = $row;
			}
		}
		if ( ! empty( $rows ) ) {
			$products[ $product_id ] = array(
				'product_id'   => $product_id,
				'product_name' => $product->get_name(),
				'edit_url'     => get_edit_post_link( $product_id, 'raw' ),
				'variations'   => $rows,
			);
		}
	}
	return $products;
}
function drop_print_sku_migration_collect_variations_by_id( array $variation_ids ): array {
	$products = array();
	foreach ( $variation_ids as $variation_id ) {
		$variation_id = absint( $variation_id );
		if ( $variation_id <= 0 ) {
			continue;
		}
		$row = drop_print_sku_migration_build_variation_row( $variation_id );
		if ( null === $row ) {
			continue;
		}
		$variation_product = wc_get_product( $variation_id );
		$product_id         = $variation_product ? $variation_product->get_parent_id() : 0;
		if ( $product_id <= 0 ) {
			continue;
		}
		if ( ! isset( $products[ $product_id ] ) ) {
			$products[ $product_id ] = array(
				'product_id'   => $product_id,
				'product_name' => get_the_title( $product_id ),
				'edit_url'     => get_edit_post_link( $product_id, 'raw' ),
				'variations'   => array(),
			);
		}
		$products[ $product_id ]['variations'][] = $row;
	}
	return $products;
}
function drop_print_sku_migration_resolve( array $skus ) {
	if ( ! function_exists( 'drop_print_make_server_request_for_ajax' ) ) {
		return new WP_Error( 'dp_sku_migration_no_transport', __( 'The server connection is not available.', 'drop-print' ) );
	}
	if ( empty( $skus ) ) {
		return array();
	}
	$resolved   = array();
	$chunk_size = 500;
	for ( $offset = 0; $offset < count( $skus ); $offset += $chunk_size ) {
		$chunk   = array_slice( $skus, $offset, $chunk_size );
		$payload = array(
			'skus' => array_map(
				static function ( $sku ) {
					return array( 'sku' => $sku );
				},
				$chunk
			),
		);
		$response = drop_print_make_server_request_for_ajax( 'legacy-sku-resolve', $payload, 'POST', 45 );
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		$results = is_array( $response ) && isset( $response['results'] ) && is_array( $response['results'] )
			? $response['results']
			: array();
		foreach ( $results as $result ) {
			if ( ! is_array( $result ) || empty( $result['sku'] ) ) {
				continue;
			}
			$resolved[ (string) $result['sku'] ] = array(
				'canonical_id'        => isset( $result['canonical_id'] ) ? (int) $result['canonical_id'] : 0,
				'canonical_name'      => ! empty( $result['canonical_name'] ) ? (string) $result['canonical_name'] : null,
				'dp_sku_base'         => ! empty( $result['dp_sku_base'] ) ? (string) $result['dp_sku_base'] : null,
				'variation_sku'       => ! empty( $result['variation_sku'] ) ? (string) $result['variation_sku'] : null,
				'substitution_reason' => ! empty( $result['substitution_reason'] ) ? (string) $result['substitution_reason'] : null,
			);
		}
	}
	return $resolved;
}
function drop_print_sku_migration_fetch_alternative_papers() {
	if ( ! function_exists( 'drop_print_make_server_request_for_ajax' ) ) {
		return new WP_Error( 'dp_sku_migration_no_transport', __( 'The server connection is not available.', 'drop-print' ) );
	}
	$response = drop_print_make_server_request_for_ajax( 'builder/pricelist', array(), 'POST', 45 );
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	$entries = is_array( $response ) && isset( $response['entries'] ) && is_array( $response['entries'] )
		? $response['entries']
		: array();
	$papers = array();
	foreach ( $entries as $entry ) {
		if ( ! is_array( $entry ) || empty( $entry['canonical_id'] ) || empty( $entry['dp_sku_base'] ) ) {
			continue; 
		}
		$finish = $entry['finish'] ?? null;
		if ( is_array( $finish ) ) {
			$finish = ! empty( $finish['label'] ) ? (string) $finish['label'] : null;
		} elseif ( null !== $finish ) {
			$finish = (string) $finish;
		}
		$papers[] = array(
			'canonical_id' => (int) $entry['canonical_id'],
			'name'         => (string) ( $entry['name'] ?? '' ),
			'gsm'          => isset( $entry['gsm'] ) ? (int) $entry['gsm'] : null,
			'finish'       => $finish,
			'description'  => ! empty( $entry['description'] ) ? (string) $entry['description'] : null,
		);
	}
	return $papers;
}
function drop_print_sku_migration_resolve_with_override( string $sku, int $canonical_id ) {
	if ( ! function_exists( 'drop_print_make_server_request_for_ajax' ) ) {
		return new WP_Error( 'dp_sku_migration_no_transport', __( 'The server connection is not available.', 'drop-print' ) );
	}
	$response = drop_print_make_server_request_for_ajax(
		'legacy-sku-resolve',
		array( 'skus' => array( array( 'sku' => $sku, 'override_canonical_id' => $canonical_id ) ) ),
		'POST',
		45
	);
	if ( is_wp_error( $response ) ) {
		return $response;
	}
	$result = is_array( $response ) && isset( $response['results'][0] ) && is_array( $response['results'][0] )
		? $response['results'][0]
		: array();
	return array(
		'canonical_id'   => isset( $result['canonical_id'] ) ? (int) $result['canonical_id'] : 0,
		'canonical_name' => ! empty( $result['canonical_name'] ) ? (string) $result['canonical_name'] : null,
		'new_sku'        => ! empty( $result['variation_sku'] ) ? (string) $result['variation_sku'] : null,
	);
}
function drop_print_sku_migration_resolve_with_override_batch( array $skus, int $canonical_id ) {
	if ( ! function_exists( 'drop_print_make_server_request_for_ajax' ) ) {
		return new WP_Error( 'dp_sku_migration_no_transport', __( 'The server connection is not available.', 'drop-print' ) );
	}
	if ( empty( $skus ) ) {
		return array();
	}
	$resolved   = array();
	$chunk_size = 500; 
	for ( $offset = 0; $offset < count( $skus ); $offset += $chunk_size ) {
		$chunk   = array_slice( $skus, $offset, $chunk_size );
		$payload = array(
			'skus' => array_map(
				static function ( $sku ) use ( $canonical_id ) {
					return array( 'sku' => $sku, 'override_canonical_id' => $canonical_id );
				},
				$chunk
			),
		);
		$response = drop_print_make_server_request_for_ajax( 'legacy-sku-resolve', $payload, 'POST', 45 );
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		$results = is_array( $response ) && isset( $response['results'] ) && is_array( $response['results'] )
			? $response['results']
			: array();
		foreach ( $results as $result ) {
			if ( ! is_array( $result ) || empty( $result['sku'] ) ) {
				continue;
			}
			$resolved[ (string) $result['sku'] ] = array(
				'canonical_id'   => isset( $result['canonical_id'] ) ? (int) $result['canonical_id'] : 0,
				'canonical_name' => ! empty( $result['canonical_name'] ) ? (string) $result['canonical_name'] : null,
				'new_sku'        => ! empty( $result['variation_sku'] ) ? (string) $result['variation_sku'] : null,
			);
		}
	}
	return $resolved;
}
function drop_print_sku_migration_build_preview() {
	return drop_print_sku_migration_build_preview_for( drop_print_sku_migration_collect_variations() );
}
function drop_print_sku_migration_build_preview_for( array $products ) {
	$distinct_skus = array();
	foreach ( $products as $product ) {
		foreach ( $product['variations'] as $row ) {
			$distinct_skus[ $row['old_sku'] ] = true;
		}
	}
	$resolved = drop_print_sku_migration_resolve( array_keys( $distinct_skus ) );
	if ( is_wp_error( $resolved ) ) {
		return $resolved;
	}
	$counts = array(
		'products'   => 0,
		'variations' => 0,
		'ready'      => 0,
		'attention'  => 0,
		'current'    => 0,
	);
	$unresolved_families = array();
	foreach ( $products as $product_id => &$product ) {
		$product_has_change = false;
		foreach ( $product['variations'] as &$row ) {
			$match = $resolved[ $row['old_sku'] ] ?? array(
				'canonical_id'        => 0,
				'canonical_name'      => null,
				'dp_sku_base'         => null,
				'variation_sku'       => null,
				'substitution_reason' => null,
			);
			$row['canonical_id']        = $match['canonical_id'];
			$row['canonical_name']      = $match['canonical_name'];
			$row['substitution_reason'] = $match['substitution_reason'] ?? null;
			$row['new_sku']        = $match['variation_sku'];
			$has_usable_price = is_numeric( $row['price'] ) && (float) $row['price'] >= 0;
			if ( null === $row['new_sku'] ) {
				$row['status'] = 'attention';
				if ( $match['canonical_id'] > 0 ) {
					$row['note'] = __( 'Resolves to the right paper, but no size could be determined - re-save this variation once migrated, or set it manually.', 'drop-print' );
				} else {
					$row['note'] = __( "Doesn't resolve to any known paper yet - contact support before migrating this one.", 'drop-print' );
					$unresolved_families[ drop_print_sku_migration_sku_family( $row['old_sku'] ) ] = true;
				}
				$counts['attention']++;
			} elseif ( 0 === strcasecmp( (string) $row['new_sku'], (string) $row['old_sku'] ) ) {
				$row['status'] = 'current';
				$row['note']   = null;
				$counts['current']++;
			} elseif ( ! $has_usable_price ) {
				$row['status'] = 'attention';
				$row['note']   = __( 'Would resolve to a new SKU, but this variation has no usable price - migrating it would delete it with nothing to recreate. Set a regular price first.', 'drop-print' );
				$counts['attention']++;
			} else {
				$row['status'] = 'ready';
				$row['note']   = null;
				$counts['ready']++;
				$product_has_change = true;
			}
			$counts['variations']++;
		}
		unset( $row );
		$product['has_change'] = $product_has_change;
	}
	unset( $product );
	$counts['products'] = count( $products );
	$unresolved_families = array_keys( $unresolved_families );
	sort( $unresolved_families );
	$counts['unresolved_families'] = $unresolved_families;
	return array(
		'products' => $products,
		'counts'   => $counts,
	);
}
function drop_print_sku_migration_sku_family( string $sku ): string {
	$pos = strrpos( $sku, '-' );
	return false !== $pos ? substr( $sku, 0, $pos ) : $sku;
}
function drop_print_sku_migration_filter_stale_selections( array &$preview, array $selected_variation_ids, array $expected_old_sku_by_id, array $expected_new_sku_by_id, array $override_canonical_id_by_id = array() ): array {
	$by_variation = array();
	foreach ( $preview['products'] as $product_id => $product ) {
		foreach ( $product['variations'] as $index => $row ) {
			$by_variation[ $row['variation_id'] ] = array( 'product_id' => $product_id, 'index' => $index, 'row' => $row );
		}
	}
	$kept    = array();
	$dropped = array();
	foreach ( $selected_variation_ids as $variation_id ) {
		$located = $by_variation[ $variation_id ] ?? null;
		$row     = $located['row'] ?? null;
		if ( ! $row ) {
			$dropped[] = array(
				'variation_id' => $variation_id,
				'reason'       => __( 'No longer resolves to a migratable SKU - skipped for safety.', 'drop-print' ),
			);
			continue;
		}
		$expected_old = $expected_old_sku_by_id[ $variation_id ] ?? null;
		$expected_new = $expected_new_sku_by_id[ $variation_id ] ?? null;
		$override_id  = $override_canonical_id_by_id[ $variation_id ] ?? 0;
		if ( $override_id > 0 ) {
			$fresh = function_exists( 'drop_print_sku_migration_resolve_with_override' )
				? drop_print_sku_migration_resolve_with_override( $row['old_sku'], $override_id )
				: new WP_Error( 'dp_sku_migration_no_transport', __( 'The server connection is not available.', 'drop-print' ) );
			if ( is_wp_error( $fresh ) || empty( $fresh['new_sku'] ) || ( null !== $expected_new && 0 !== strcasecmp( (string) $expected_new, (string) $fresh['new_sku'] ) ) ) {
				$dropped[] = array(
					'variation_id'     => $variation_id,
					'old_sku'          => $row['old_sku'],
					'approved_new_sku' => $expected_new,
					'current_new_sku'  => is_wp_error( $fresh ) ? null : ( $fresh['new_sku'] ?? null ),
					'reason'           => __( 'The chosen paper no longer resolves the same way - skipped rather than writing something you did not approve.', 'drop-print' ),
				);
				continue;
			}
			$preview['products'][ $located['product_id'] ]['variations'][ $located['index'] ]['new_sku']        = $fresh['new_sku'];
			$preview['products'][ $located['product_id'] ]['variations'][ $located['index'] ]['canonical_id']   = $fresh['canonical_id'];
			$preview['products'][ $located['product_id'] ]['variations'][ $located['index'] ]['canonical_name'] = $fresh['canonical_name'];
			$preview['products'][ $located['product_id'] ]['variations'][ $located['index'] ]['status']         = 'ready';
			$kept[] = $variation_id;
			continue;
		}
		if ( 'ready' !== $row['status'] ) {
			$dropped[] = array(
				'variation_id' => $variation_id,
				'reason'       => __( 'No longer resolves to a migratable SKU - skipped for safety.', 'drop-print' ),
			);
			continue;
		}
		$old_mismatch = null !== $expected_old && 0 !== strcasecmp( (string) $expected_old, (string) $row['old_sku'] );
		$new_mismatch = null !== $expected_new && 0 !== strcasecmp( (string) $expected_new, (string) $row['new_sku'] );
		if ( $old_mismatch || $new_mismatch ) {
			$dropped[] = array(
				'variation_id'      => $variation_id,
				'old_sku'           => $row['old_sku'],
				'approved_new_sku'  => $expected_new,
				'current_new_sku'   => $row['new_sku'],
				'reason'            => __( 'The mapping changed since this was reviewed - skipped rather than writing a different SKU than what was approved.', 'drop-print' ),
			);
			continue;
		}
		$kept[] = $variation_id;
	}
	return array( $kept, $dropped );
}
function drop_print_sku_migration_write_csv_backup( array $rows ) {
	$dir = drop_print_sku_migration_backup_dir();
	if ( ! wp_mkdir_p( $dir ) ) {
		return new WP_Error( 'dp_sku_migration_backup_dir', __( 'Could not create the backup directory.', 'drop-print' ) );
	}
	drop_print_sku_migration_protect_backup_dir( $dir );
	$filename = 'sku-migration-' . gmdate( 'Y-m-d-His' ) . '.csv';
	$path     = trailingslashit( $dir ) . $filename;
	$handle = fopen( $path, 'w' ); 
	if ( ! $handle ) {
		return new WP_Error( 'dp_sku_migration_backup_write', __( 'Could not write the backup file.', 'drop-print' ) );
	}
	fputcsv( $handle, array( 'product_id', 'product_name', 'variation_id', 'size', 'old_sku', 'new_sku', 'price', 'sizing', 'target_paper' ) );
	foreach ( $rows as $row ) {
		fputcsv(
			$handle,
			array(
				$row['product_id'],
				$row['product_name'],
				$row['variation_id'],
				$row['size_label'],
				$row['old_sku'],
				$row['new_sku'],
				$row['price'] ?? '',
				$row['sizing'] ?? '',
				$row['canonical_name'] ?? '',
			)
		);
	}
	fclose( $handle ); 
	return $path;
}
function drop_print_sku_migration_backup_dir(): string {
	return untrailingslashit(
		trailingslashit( defined( 'DROP_PRINT_UPLOAD_BASE_DIR' ) ? DROP_PRINT_UPLOAD_BASE_DIR : trailingslashit( wp_upload_dir()['basedir'] ) . 'drop-print' ) . 'sku-migration-backups'
	);
}
function drop_print_sku_migration_protect_backup_dir( string $dir ): void {
	if ( ! file_exists( trailingslashit( $dir ) . 'index.html' ) ) {
		@file_put_contents( trailingslashit( $dir ) . 'index.html', '' ); 
	}
	if ( ! file_exists( trailingslashit( $dir ) . '.htaccess' ) ) {
		@file_put_contents( trailingslashit( $dir ) . '.htaccess', "Require all denied\n" ); 
	}
}
function drop_print_sku_migration_snapshot_variation( int $variation_id, array $row ): array {
	$variation = wc_get_product( $variation_id );
	if ( ! $variation ) {
		return array(
			'variation_id'     => $variation_id,
			'old_sku'          => $row['old_sku'],
			'missing_at_backup' => true,
			'new_variation_id' => null,
		);
	}
	return array(
		'variation_id'      => $variation_id,
		'old_sku'           => $row['old_sku'],
		'status_at_backup'  => $row['status'],
		'title'             => $row['size_label'],
		'sizing'            => $row['sizing'] ?? 'fillPrintArea',
		'regular_price'     => $variation->get_regular_price(),
		'sale_price'        => $variation->get_sale_price(),
		'manage_stock'      => $variation->get_manage_stock(),
		'stock_quantity'    => $variation->get_stock_quantity(),
		'stock_status'      => $variation->get_stock_status(),
		'description'       => $variation->get_description(),
		'image_id'          => $variation->get_image_id(),
		'menu_order'        => $variation->get_menu_order(),
		'post_status'       => $variation->get_status(),
		'attributes'        => $variation->get_attributes(),
		'new_variation_id'  => null,
	);
}
function drop_print_sku_migration_write_json_backup( array $products_snapshot ) {
	$dir = drop_print_sku_migration_backup_dir();
	if ( ! wp_mkdir_p( $dir ) ) {
		return new WP_Error( 'dp_sku_migration_backup_dir', __( 'Could not create the backup directory.', 'drop-print' ) );
	}
	drop_print_sku_migration_protect_backup_dir( $dir );
	$filename = 'sku-migration-' . gmdate( 'Y-m-d-His' ) . '.json';
	$path     = trailingslashit( $dir ) . $filename;
	$payload = array(
		'created_at' => gmdate( 'c' ),
		'products'   => $products_snapshot,
	);
	$written = @file_put_contents( $path, wp_json_encode( $payload, JSON_PRETTY_PRINT ) ); 
	if ( false === $written ) {
		return new WP_Error( 'dp_sku_migration_backup_write', __( 'Could not write the restore backup file.', 'drop-print' ) );
	}
	return $path;
}
function drop_print_sku_migration_finalize_json_backup( string $json_path, array $product_ids ): void {
	if ( ! file_exists( $json_path ) ) {
		return;
	}
	$raw     = file_get_contents( $json_path ); 
	$payload = is_string( $raw ) ? json_decode( $raw, true ) : null;
	if ( ! is_array( $payload ) || empty( $payload['products'] ) ) {
		return;
	}
	foreach ( $product_ids as $product_id ) {
		if ( ! isset( $payload['products'][ $product_id ] ) ) {
			continue;
		}
		$post_migration_meta = get_post_meta( $product_id, '_drop_print_meta', true );
		$new_ids_in_order    = array();
		if ( is_array( $post_migration_meta ) && ! empty( $post_migration_meta['variations'] ) ) {
			foreach ( $post_migration_meta['variations'] as $variation_data ) {
				$new_ids_in_order[] = isset( $variation_data['wc_variation_id'] ) ? absint( $variation_data['wc_variation_id'] ) : null;
			}
		}
		foreach ( $payload['products'][ $product_id ]['variations'] as $index => &$snapshot_row ) {
			$snapshot_row['new_variation_id'] = $new_ids_in_order[ $index ] ?? null;
		}
		unset( $snapshot_row );
	}
	@file_put_contents( $json_path, wp_json_encode( $payload, JSON_PRETTY_PRINT ) ); 
}
function drop_print_sku_migration_restore_from_backup( string $json_path ): array {
	$result = array(
		'products_restored'   => 0,
		'variations_restored' => 0,
		'errors'              => array(),
	);
	if ( ! file_exists( $json_path ) ) {
		$result['errors'][] = __( 'Backup file not found.', 'drop-print' );
		return $result;
	}
	$raw     = file_get_contents( $json_path ); 
	$payload = is_string( $raw ) ? json_decode( $raw, true ) : null;
	if ( ! is_array( $payload ) || empty( $payload['products'] ) ) {
		$result['errors'][] = __( 'Backup file is empty or unreadable.', 'drop-print' );
		return $result;
	}
	$taxonomy = 'pa_print_dimensions';
	foreach ( $payload['products'] as $product_id => $product_snapshot ) {
		$product_id = absint( $product_id );
		$product    = wc_get_product( $product_id );
		if ( ! $product ) {
			$result['errors'][] = "Product {$product_id} no longer exists - skipped.";
			continue;
		}
		foreach ( (array) ( $product_snapshot['variations'] ?? array() ) as $variation_snapshot ) {
			$new_id = absint( $variation_snapshot['new_variation_id'] ?? 0 );
			if ( $new_id > 0 && get_post( $new_id ) ) {
				wp_delete_post( $new_id, true );
			}
		}
		$attribute_term_ids       = array();
		$restored_meta_variations = array();
		$restored_count           = 0;
		foreach ( (array) ( $product_snapshot['variations'] ?? array() ) as $variation_snapshot ) {
			if ( ! empty( $variation_snapshot['missing_at_backup'] ) ) {
				continue;
			}
			$old_sku = (string) ( $variation_snapshot['old_sku'] ?? '' );
			$title   = (string) ( $variation_snapshot['title'] ?? $old_sku );
			$price   = $variation_snapshot['regular_price'] ?? null;
			if ( '' === $old_sku || null === $price || ! is_numeric( $price ) ) {
				$result['errors'][] = "Skipped a variation with no restorable price/sku on product {$product_id} ({$old_sku}).";
				continue;
			}
			$term_slug = ! empty( $variation_snapshot['attributes'][ $taxonomy ] )
				? (string) $variation_snapshot['attributes'][ $taxonomy ]
				: sanitize_title( $old_sku );
			$term = get_term_by( 'slug', $term_slug, $taxonomy );
			if ( ! $term ) {
				$term_result = wp_insert_term( $title, $taxonomy, array( 'slug' => $term_slug ) );
				if ( is_wp_error( $term_result ) ) {
					$result['errors'][] = "Could not recreate size term for {$old_sku}: " . $term_result->get_error_message();
					continue;
				}
				$term_id = $term_result['term_id'];
			} else {
				$term_id = $term->term_id;
			}
			$attribute_term_ids[ $term_id ] = $term_id;
			$sale_price = (string) ( $variation_snapshot['sale_price'] ?? '' );
			$variation = new WC_Product_Variation();
			$variation->set_parent_id( $product_id );
			$variation->set_status( ! empty( $variation_snapshot['post_status'] ) ? $variation_snapshot['post_status'] : 'publish' );
			$variation->set_menu_order( isset( $variation_snapshot['menu_order'] ) ? (int) $variation_snapshot['menu_order'] : $restored_count );
			$variation->set_attributes( array( $taxonomy => $term_slug ) );
			$variation->set_regular_price( $price );
			$variation->set_sale_price( $sale_price );
			$variation->set_price( '' !== $sale_price ? $sale_price : $price );
			$variation->set_manage_stock( ! empty( $variation_snapshot['manage_stock'] ) );
			if ( ! empty( $variation_snapshot['manage_stock'] ) ) {
				$variation->set_stock_quantity( $variation_snapshot['stock_quantity'] ?? null );
			}
			if ( ! empty( $variation_snapshot['stock_status'] ) ) {
				$variation->set_stock_status( $variation_snapshot['stock_status'] );
			}
			$variation->set_virtual( false );
			$variation->set_downloadable( false );
			$variation->set_description( ! empty( $variation_snapshot['description'] ) ? $variation_snapshot['description'] : $title );
			if ( ! empty( $variation_snapshot['image_id'] ) ) {
				$variation->set_image_id( absint( $variation_snapshot['image_id'] ) );
			}
			$variation_id_saved = $variation->save();
			if ( is_wp_error( $variation_id_saved ) || ! $variation_id_saved ) {
				$result['errors'][] = "Could not recreate variation for {$old_sku}.";
				continue;
			}
			wp_update_post( array( 'ID' => $variation_id_saved, 'post_title' => $title ) );
			update_post_meta( $variation_id_saved, '_drop_print_sku', $old_sku );
			update_post_meta( $variation_id_saved, '_drop_print_sizing', $variation_snapshot['sizing'] ?? 'fillPrintArea' );
			$restored_meta_variations[] = array(
				'sku'             => $old_sku,
				'price'           => $price,
				'title'           => $title,
				'sizing'          => $variation_snapshot['sizing'] ?? 'fillPrintArea',
				'wc_variation_id' => $variation_id_saved,
			);
			++$restored_count;
			++$result['variations_restored'];
		}
		if ( $restored_count > 0 ) {
			wp_set_object_terms( $product_id, array_values( $attribute_term_ids ), $taxonomy, false );
			$attributes        = $product->get_attributes();
			$attribute_object  = $attributes[ $taxonomy ] ?? new WC_Product_Attribute();
			if ( ! $attribute_object->get_id() && function_exists( 'wc_attribute_taxonomy_id_by_name' ) ) {
				$taxonomy_id = wc_attribute_taxonomy_id_by_name( $taxonomy );
				if ( $taxonomy_id ) {
					$attribute_object->set_id( $taxonomy_id );
				}
			}
			$attribute_object->set_name( $taxonomy );
			$attribute_object->set_options( array_values( $attribute_term_ids ) );
			$attribute_object->set_visible( true );
			$attribute_object->set_variation( true );
			$attributes[ $taxonomy ] = $attribute_object;
			$product->set_attributes( $attributes );
			$product->save();
		}
		$meta                          = is_array( $product_snapshot['drop_print_meta'] ?? null ) ? $product_snapshot['drop_print_meta'] : array();
		$meta['variations']            = $restored_meta_variations;
		$meta['variations_processed']  = true;
		update_post_meta( $product_id, '_drop_print_meta', $meta );
		if ( function_exists( 'wc_delete_product_transients' ) ) {
			wc_delete_product_transients( $product_id );
		}
		++$result['products_restored'];
	}
	return $result;
}
function drop_print_sku_migration_apply( array $preview, array $selected_variation_ids ): array {
	$selected = array_flip( array_map( 'absint', $selected_variation_ids ) );
	$result   = array(
		'backup_path'         => null,
		'backup_json_path'    => null,
		'products_migrated'   => 0,
		'variations_migrated' => 0,
		'errors'              => array(),
	);
	if ( ! function_exists( 'drop_print_save_product_meta' ) ) {
		$result['errors'][] = __( 'The product writer is not loaded - nothing was changed.', 'drop-print' );
		return $result;
	}
	$backup_rows   = array();
	$json_products = array();
	foreach ( $preview['products'] as $product_id => $product ) {
		$product_touched = false;
		foreach ( $product['variations'] as $row ) {
			if ( 'ready' === $row['status'] && isset( $selected[ $row['variation_id'] ] ) ) {
				$product_touched = true;
				break;
			}
		}
		if ( ! $product_touched ) {
			continue;
		}
		$variation_snapshots = array();
		foreach ( $product['variations'] as $row ) {
			if ( 'ready' === $row['status'] && isset( $selected[ $row['variation_id'] ] ) ) {
				$backup_rows[] = array(
					'product_id'     => $product['product_id'],
					'product_name'   => $product['product_name'],
					'variation_id'   => $row['variation_id'],
					'size_label'     => $row['size_label'],
					'old_sku'        => $row['old_sku'],
					'new_sku'        => $row['new_sku'],
					'price'          => $row['price'],
					'sizing'         => $row['sizing'],
					'canonical_name' => $row['canonical_name'],
				);
			}
			$variation_snapshots[] = drop_print_sku_migration_snapshot_variation( $row['variation_id'], $row );
		}
		$json_products[ $product_id ] = array(
			'product_id'      => $product['product_id'],
			'product_name'    => $product['product_name'],
			'drop_print_meta' => get_post_meta( $product_id, '_drop_print_meta', true ),
			'variations'      => $variation_snapshots,
		);
	}
	if ( empty( $backup_rows ) ) {
		return $result;
	}
	$backup_path = drop_print_sku_migration_write_csv_backup( $backup_rows );
	if ( is_wp_error( $backup_path ) ) {
		$result['errors'][] = $backup_path->get_error_message();
		return $result;
	}
	$result['backup_path'] = $backup_path;
	$backup_json_path = drop_print_sku_migration_write_json_backup( $json_products );
	if ( is_wp_error( $backup_json_path ) ) {
		$result['errors'][] = $backup_json_path->get_error_message();
		return $result;
	}
	$result['backup_json_path'] = $backup_json_path;
	foreach ( $preview['products'] as $product_id => $product ) {
		if ( ! isset( $json_products[ $product_id ] ) ) {
			continue;
		}
		$new_variations = array();
		foreach ( $product['variations'] as $row ) {
			$use_new = 'ready' === $row['status'] && isset( $selected[ $row['variation_id'] ] );
			if ( $use_new ) {
				$result['variations_migrated']++;
			}
			$new_variations[] = array(
				'sku'    => $use_new ? $row['new_sku'] : $row['old_sku'],
				'price'  => $row['price'],
				'title'  => $row['size_label'],
				'sizing' => $row['sizing'],
			);
		}
		$meta = get_post_meta( $product_id, '_drop_print_meta', true );
		if ( ! is_array( $meta ) ) {
			$meta = array();
		}
		$meta['variations']           = $new_variations;
		$meta['variations_processed'] = false;
		$meta['active']               = 'yes';
		update_post_meta( $product_id, '_drop_print_meta', $meta );
		drop_print_save_product_meta( $product_id );
		$result['products_migrated']++;
	}
	drop_print_sku_migration_finalize_json_backup( $backup_json_path, array_keys( $json_products ) );
	return $result;
}
function drop_print_sku_migration_apply_chunk( array $variation_ids, array $selected_variation_ids, array $expected_old_sku_by_id, array $expected_new_sku_by_id, array $override_canonical_id_by_id ) {
	$products = drop_print_sku_migration_collect_variations_by_id( $variation_ids );
	$preview  = drop_print_sku_migration_build_preview_for( $products );
	if ( is_wp_error( $preview ) ) {
		return $preview;
	}
	list( $selected, $dropped_stale ) = drop_print_sku_migration_filter_stale_selections(
		$preview,
		$selected_variation_ids,
		$expected_old_sku_by_id,
		$expected_new_sku_by_id,
		$override_canonical_id_by_id
	);
	$result = drop_print_sku_migration_apply( $preview, $selected );
	return array(
		'apply'         => $result,
		'dropped_stale' => $dropped_stale,
	);
}
