<?php
/**
 * Responsible for saving recipes.
 *
 * @link       https://bootstrapped.ventures
 * @since      1.0.0
 *
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/public
 */

/**
 * Responsible for saving recipes.
 *
 * @since      1.0.0
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/public
 * @author     Brecht Vandersmissen <brecht@bootstrapped.ventures>
 */
class WPRM_Recipe_Saver {

	/**
	 * Register actions and filters.
	 *
	 * @since    1.0.0
	 */
	public static function init() {
		add_action( 'save_post', array( __CLASS__, 'update_post' ), 10, 2 );
		add_action( 'admin_init', array( __CLASS__, 'update_recipes_check' ) );

		add_filter( 'wp_insert_post_data', array( __CLASS__, 'post_type_switcher_fix' ), 20, 2 );
	}

	/**
	 * Create a new recipe.
	 *
	 * @since    1.0.0
	 * @param		 array $recipe Recipe fields to save.
	 */
	public static function create_recipe( $recipe ) {
		$post = array(
			'post_type' => WPRM_POST_TYPE,
			'post_status' => 'draft',
		);

		$recipe_id = wp_insert_post( $post );
		WPRM_Recipe_Saver::update_recipe( $recipe_id, $recipe, true );

		WPRM_Changelog::log( 'recipe_created', $recipe_id );

		return $recipe_id;
	}

	/**
	 * Save recipe fields.
	 *
	 * @since	1.0.0
	 * @param	int		$id Post ID of the recipe.
	 * @param	array	$recipe Recipe fields to save.
	 * @param	boolean $ignore_log Whether this edit should be ignored for the changelog.
	 */
	public static function update_recipe( $id, $recipe, $ignore_log = false ) {
		$meta = array();

		// Featured Image.
		if ( isset( $recipe['image_id'] ) ) {
			if ( $recipe['image_id'] ) {
				set_post_thumbnail( $id, $recipe['image_id'] );
			} else {
				delete_post_thumbnail( $id );
			}
		}

		// Make sure language is set first, before setting taxonomies.
		$post_type_structure = WPRM_Settings::get( 'post_type_structure' );
		$language_is_set = array_key_exists( 'language', $recipe );

		if ( 'public' === $post_type_structure ) {
			if ( $language_is_set ) {
				WPRM_Compatibility::set_language_for( $id, $recipe['language'] );
			}
		} else {
			if ( $language_is_set ) {
				WPRM_Compatibility::set_language_for( $id, $recipe['language'] );
			} else {
				// Not manually setting the language, so default to the current admin language.
				$admin_language = WPRM_Compatibility::get_current_admin_language();
				if ( $admin_language ) {
					WPRM_Compatibility::set_language_for( $id, $admin_language );
				}
			}
		}

		// Recipe Taxonomies.
		if ( isset( $recipe['tags'] ) ) {
			$taxonomies = WPRM_Taxonomies::get_taxonomies();

			$term_language = false;
			if ( isset( $recipe['language'] ) && $recipe['language'] ) {
				$term_language = $recipe['language'];
			} else {
				$term_language = WPRM_Compatibility::get_language_for( $id );
			}

			if ( $term_language ) {
				WPRM_Compatibility::set_new_term_language_context( $term_language );
			}

			foreach ( $taxonomies as $taxonomy => $options ) {
				$key = substr( $taxonomy, 5 ); // Get rid of wprm_.
				$terms = isset( $recipe['tags'][ $key ] ) ? $recipe['tags'][ $key ] : array();
				$terms = is_array( $terms ) ? $terms : array( $terms );
				$terms = array_map( array( 'WPRM_Recipe_Sanitizer', 'sanitize_tags' ), $terms );

				wp_set_object_terms( $id, $terms, $taxonomy, false );
			}

			WPRM_Compatibility::clear_new_term_language_context();
		}

		// Recipe Equipment.
		if ( isset( $recipe['equipment'] ) ) {
			$equipment_ids = array();
			foreach ( $recipe['equipment'] as $equipment ) {
				$equipment_ids[] = intval( $equipment['id'] );
			}
			$equipment_ids = array_unique( $equipment_ids );

			$meta['wprm_equipment'] = $recipe['equipment'];
			wp_set_object_terms( $id, $equipment_ids, 'wprm_equipment', false );
		}

		// Recipe Ingredients.
		if ( isset( $recipe['ingredients'] ) ) {
			$ingredient_ids = array();
			$unit_ids = array();

			foreach ( $recipe['ingredients'] as $ingredient_group ) {
				foreach ( $ingredient_group['ingredients'] as $ingredient ) {
					$ingredient_ids[] = intval( $ingredient['id'] );
					
					// Unit ID from regular ingredient.
					if ( isset( $ingredient['unit_id'] ) && $ingredient['unit_id'] ) {
						$unit_ids[] = intval( $ingredient['unit_id'] );
					}

					// Unit ID from converted ingredients.
					if ( isset( $ingredient['converted'] ) ) {
						foreach ( $ingredient['converted'] as $system => $conversion ) {
							if ( isset( $ingredient['converted'][ $system ]['unit_id'] ) && $ingredient['converted'][ $system ]['unit_id'] ) {
								$unit_ids[] = intval( $ingredient['converted'][ $system ]['unit_id'] );
							}
						}
					}
				}
			}
			$ingredient_ids = array_unique( $ingredient_ids );
			$unit_ids = array_unique( $unit_ids );

			$meta['wprm_ingredients'] = $recipe['ingredients'];
			wp_set_object_terms( $id, $ingredient_ids, 'wprm_ingredient', false );
			wp_set_object_terms( $id, $unit_ids, 'wprm_ingredient_unit', false );
		}

		// Video fields (always clear metadata).
		$meta['wprm_video_metadata'] = '';
		if ( isset( $recipe['video_id'] ) )	{
			$meta['wprm_video_id'] = $recipe['video_id'];
		}
		if ( isset( $recipe['video_embed'] ) ) {
			$meta['wprm_video_embed'] = $recipe['video_embed'];
		}

		// Nutrition fields.
		if ( isset( $recipe['nutrition'] ) ) {
			foreach ( $recipe['nutrition'] as $nutrient => $value ) {
				$meta[ 'wprm_nutrition_' . $nutrient ] = $value;
			}
		}

		// Meta Fields.
		if ( isset( $recipe['type'] ) )							{ $meta['wprm_type'] = $recipe['type']; }
		if ( isset( $recipe['pin_image_id'] ) )					{ $meta['wprm_pin_image_id'] = $recipe['pin_image_id']; }
		if ( isset( $recipe['pin_image_repin_id'] ) )			{ $meta['wprm_pin_image_repin_id'] = $recipe['pin_image_repin_id']; }
		if ( isset( $recipe['author_display'] ) )				{ $meta['wprm_author_display'] = $recipe['author_display']; }
		if ( isset( $recipe['author_name'] ) )					{ $meta['wprm_author_name'] = $recipe['author_name']; }
		if ( isset( $recipe['author_link'] ) )					{ $meta['wprm_author_link'] = $recipe['author_link']; }
		if ( isset( $recipe['author_bio'] ) )					{ $meta['wprm_author_bio'] = $recipe['author_bio']; }
		if ( isset( $recipe['servings'] ) )						{ $meta['wprm_servings'] = $recipe['servings']; }
		if ( isset( $recipe['servings_unit'] ) )				{ $meta['wprm_servings_unit'] = $recipe['servings_unit']; }
		if ( isset( $recipe['servings_advanced_enabled'] ) )	{ $meta['wprm_servings_advanced_enabled'] = $recipe['servings_advanced_enabled']; }
		if ( isset( $recipe['servings_advanced'] ) )			{ $meta['wprm_servings_advanced'] = $recipe['servings_advanced']; }
		if ( isset( $recipe['cost'] ) )							{ $meta['wprm_cost'] = $recipe['cost']; }
		if ( isset( $recipe['prep_time'] ) )					{ $meta['wprm_prep_time'] = $recipe['prep_time']; }
		if ( isset( $recipe['prep_time_zero'] ) )				{ $meta['wprm_prep_time_zero'] = $recipe['prep_time_zero']; }
		if ( isset( $recipe['cook_time'] ) )					{ $meta['wprm_cook_time'] = $recipe['cook_time']; }
		if ( isset( $recipe['cook_time_zero'] ) )				{ $meta['wprm_cook_time_zero'] = $recipe['cook_time_zero']; }
		if ( isset( $recipe['total_time'] ) )					{ $meta['wprm_total_time'] = $recipe['total_time']; }
		if ( isset( $recipe['custom_time'] ) )					{ $meta['wprm_custom_time'] = $recipe['custom_time']; }
		if ( isset( $recipe['custom_time_zero'] ) )				{ $meta['wprm_custom_time_zero'] = $recipe['custom_time_zero']; }
		if ( isset( $recipe['custom_time_label'] ) )			{ $meta['wprm_custom_time_label'] = $recipe['custom_time_label']; }
		if ( isset( $recipe['instructions'] ) )					{ $meta['wprm_instructions'] = $recipe['instructions']; }
		if ( isset( $recipe['notes'] ) )						{ $meta['wprm_notes'] = $recipe['notes']; }
		if ( isset( $recipe['ingredient_links_type'] ) )		{ $meta['wprm_ingredient_links_type'] = $recipe['ingredient_links_type']; }
		if ( isset( $recipe['unit_system'] ) )					{ $meta['wprm_unit_system'] = $recipe['unit_system']; }
		if ( isset( $recipe['import_source'] ) ) 				{ $meta['wprm_import_source'] = $recipe['import_source']; }
		if ( isset( $recipe['import_backup'] ) ) 				{ $meta['wprm_import_backup'] = $recipe['import_backup']; }

		$meta = apply_filters( 'wprm_recipe_save_meta', $meta, $id, $recipe );

		// Store version number.
		$meta['wprm_version'] = WPRM_Version::convert_to_number();

		// Post Fields.
		$post = array(
			'ID' => $id,
			'meta_input' => $meta,
		);

		if ( isset( $recipe['name'] ) ) {
			$post['post_title'] = $recipe['name'];

			$post_name_prefix = 'public' === WPRM_Settings::get( 'post_type_structure' ) ? '' : 'wprm-';
			$post['post_name'] = $post_name_prefix . sanitize_title( $recipe['name'] );
		}

		if ( isset( $recipe['summary'] ) ) {
			$post['post_content'] = wp_slash( $recipe['summary'] );
		}

		// Only for "public" recipe type or when manually setting author.
		if ( 'public' === WPRM_Settings::get( 'post_type_structure' ) || 'manual' === WPRM_Settings::get( 'recipe_use_author' ) ) {
			if ( isset( $recipe['post_author'] ) && $recipe['post_author'] ) {
				$post['post_author'] = $recipe['post_author'];
			}
		}

		// Only for "public" recipe type.
		if ( 'public' === WPRM_Settings::get( 'post_type_structure' ) ) {
			if ( isset( $recipe['slug'] ) && $recipe['slug'] ) {
				$post['post_name'] = $recipe['slug'];
			}

			if ( isset( $recipe['date'] ) && $recipe['date'] ) {
				$post['post_date'] = $recipe['date'];
				$post['post_date_gmt'] = get_gmt_from_date( $recipe['date'] );

				// If date is in the future, post status should be future.
				$post_date_timestamp = strtotime( $post['post_date'] );
				if ( $post_date_timestamp && time() < $post_date_timestamp ) {
					$recipe['post_status'] = 'future';
				}
			}
	
			if ( isset( $recipe['post_status'] ) && $recipe['post_status'] ) {
				$post['post_status'] = $recipe['post_status'];

				// Need to update post date if publishing and post date is still set to the future (scheduled).
				if ( 'publish' === $post['post_status'] ) {
					$post_date_timestamp = isset( $post['post_date'] ) ? strtotime( $post['post_date'] ) : get_the_date( 'U', $id );
					$current_timestamp = time();

					if ( $post_date_timestamp && $current_timestamp < $post_date_timestamp ) {
						$post['post_date'] = date( 'Y-m-d H:i:s',  $current_timestamp );
						$post['post_date_gmt'] = gmdate( 'Y-m-d H:i:s',  $current_timestamp );
					}
				}
			} else {
				if ( isset( $recipe['name'] ) ) {
					// Don't use wprm- in front by default for "public" recipe type.
					$post['post_name'] = sanitize_title( $recipe['name'] );
				}
			}

			if ( isset( $recipe['post_password'] ) ) {
				$post['post_password'] = $recipe['post_password'];
			}
		}

		// Always update post to make sure revision gets made.
		WPRM_Recipe_Manager::invalidate_recipe( $id );
		wp_update_post( $post );

		// Keep translated taxonomy terms in sync with the recipe language.
		$term_language = $language_is_set && isset( $recipe['language'] ) ? $recipe['language'] : WPRM_Compatibility::get_language_for( $id );
		self::sync_recipe_term_languages( $id, $term_language );

		if ( ! $ignore_log ) {
			WPRM_Changelog::log( 'recipe_edited', $id );
		}

		// Update recipe SEO values afterwards.
		WPRM_Seo_Checker::update_seo_for( $id );

		// Remove cached Instacart data.
		delete_post_meta( $id, 'wprm_instacart_combinations' );
	}

	/**
	 * Sync recipe taxonomy terms to translated terms for a specific language.
	 *
	 * Non-destructive: when no translated term exists, the original term remains assigned.
	 *
	 * @since	10.4.0
	 * @param	int          $recipe_id Recipe ID to sync terms for.
	 * @param	false|string $language  Language code to sync to.
	 */
	public static function sync_recipe_term_languages( $recipe_id, $language = false ) {
		$recipe_id = intval( $recipe_id );

		if ( ! $recipe_id ) {
			return;
		}

		if ( ! $language ) {
			$language = WPRM_Compatibility::get_language_for( $recipe_id );
		}

		if ( ! $language ) {
			return;
		}

		$taxonomies = WPRM_Taxonomies::get_taxonomies();

		foreach ( array_keys( $taxonomies ) as $taxonomy ) {
			if ( ! taxonomy_exists( $taxonomy ) ) {
				continue;
			}

			$term_ids = self::get_assigned_term_ids_from_database( $recipe_id, $taxonomy );

			if ( empty( $term_ids ) ) {
				continue;
			}

			$current_term_ids = $term_ids;
			$new_term_ids = array();
			$translated_term_replacements = array();

			foreach ( $term_ids as $term_id ) {
				$term_id = intval( $term_id );
				$translated_term_id = WPRM_Compatibility::get_translated_term_for_language( $term_id, $taxonomy, $language );

				if ( $translated_term_id && $translated_term_id !== $term_id ) {
					$new_term_ids[] = $translated_term_id;
					$translated_term_replacements[ $term_id ] = $translated_term_id;
				} else {
					$new_term_ids[] = $term_id;
				}
			}

			$new_term_ids = array_values( array_unique( $new_term_ids ) );
			sort( $current_term_ids );
			sort( $new_term_ids );

			if ( $current_term_ids !== $new_term_ids && ! empty( $translated_term_replacements ) ) {
				self::replace_translated_term_relationships( $recipe_id, $taxonomy, $translated_term_replacements );
			}
		}
	}

	/**
	 * Get assigned term IDs directly from the database.
	 *
	 * @since	10.8.1
	 * @param	int    $recipe_id Recipe ID.
	 * @param	string $taxonomy  Taxonomy to inspect.
	 */
	private static function get_assigned_term_ids_from_database( $recipe_id, $taxonomy ) {
		global $wpdb;

		$query = $wpdb->prepare(
			"SELECT DISTINCT tt.term_id
			FROM {$wpdb->term_relationships} tr
			INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
			WHERE tr.object_id = %d AND tt.taxonomy = %s
			ORDER BY tt.term_id ASC",
			$recipe_id,
			$taxonomy
		);

		$term_ids = $wpdb->get_col( $query );

		return is_array( $term_ids ) ? array_values( array_map( 'intval', $term_ids ) ) : array();
	}

	/**
	 * Replace translated term relationships without clearing unrelated assignments.
	 *
	 * This uses direct relationship updates because multilingual plugins can hide
	 * some term links from the standard replacement flow, which risks leaving old
	 * translated terms behind or wiping untranslated fallback terms.
	 *
	 * @since	10.8.1
	 * @param	int    $recipe_id    Recipe ID.
	 * @param	string $taxonomy     Taxonomy to update.
	 * @param	array  $replacements Map of source term IDs to translated term IDs.
	 */
	private static function replace_translated_term_relationships( $recipe_id, $taxonomy, $replacements ) {
		global $wpdb;

		if ( ! $recipe_id || ! $taxonomy || ! is_array( $replacements ) || empty( $replacements ) ) {
			return;
		}

		$current_relationships = self::get_assigned_term_taxonomy_ids_from_database( $recipe_id, $taxonomy );
		if ( empty( $current_relationships ) ) {
			return;
		}

		$translated_term_ids = array_values( array_unique( array_map( 'intval', $replacements ) ) );
		$translated_term_taxonomy_ids = self::get_term_taxonomy_ids_from_database( $translated_term_ids, $taxonomy );

		$term_taxonomy_ids_to_remove = array();
		$term_taxonomy_ids_to_add = array();

		foreach ( $replacements as $source_term_id => $translated_term_id ) {
			$source_term_id = intval( $source_term_id );
			$translated_term_id = intval( $translated_term_id );

			if ( ! isset( $current_relationships[ $source_term_id ] ) || ! isset( $translated_term_taxonomy_ids[ $translated_term_id ] ) ) {
				continue;
			}

			$term_taxonomy_ids_to_remove[] = intval( $current_relationships[ $source_term_id ] );
			$term_taxonomy_ids_to_add[] = intval( $translated_term_taxonomy_ids[ $translated_term_id ] );
		}

		$term_taxonomy_ids_to_remove = array_values( array_unique( array_filter( $term_taxonomy_ids_to_remove ) ) );
		$term_taxonomy_ids_to_add = array_values( array_unique( array_filter( $term_taxonomy_ids_to_add ) ) );

		if ( empty( $term_taxonomy_ids_to_remove ) && empty( $term_taxonomy_ids_to_add ) ) {
			return;
		}

		if ( ! empty( $term_taxonomy_ids_to_remove ) ) {
			$delete_placeholders = implode( ', ', array_fill( 0, count( $term_taxonomy_ids_to_remove ), '%d' ) );
			$delete_arguments = array_merge(
				array( $recipe_id ),
				$term_taxonomy_ids_to_remove
			);

			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->term_relationships}
					WHERE object_id = %d AND term_taxonomy_id IN ({$delete_placeholders})",
					$delete_arguments
				)
			);
		}

		if ( ! empty( $term_taxonomy_ids_to_add ) ) {
			foreach ( $term_taxonomy_ids_to_add as $term_taxonomy_id ) {
				$wpdb->replace(
					$wpdb->term_relationships,
					array(
						'object_id'        => $recipe_id,
						'term_taxonomy_id' => $term_taxonomy_id,
					),
					array(
						'%d',
						'%d',
					)
				);
			}
		}

		$affected_term_taxonomy_ids = array_values( array_unique( array_merge( $term_taxonomy_ids_to_remove, $term_taxonomy_ids_to_add ) ) );
		if ( ! empty( $affected_term_taxonomy_ids ) ) {
			wp_update_term_count( $affected_term_taxonomy_ids, $taxonomy );
		}

		clean_object_term_cache( $recipe_id, WPRM_POST_TYPE );
	}

	/**
	 * Get assigned term taxonomy IDs directly from the database.
	 *
	 * @since	10.8.1
	 * @param	int    $recipe_id Recipe ID.
	 * @param	string $taxonomy  Taxonomy to inspect.
	 */
	private static function get_assigned_term_taxonomy_ids_from_database( $recipe_id, $taxonomy ) {
		global $wpdb;

		$query = $wpdb->prepare(
			"SELECT DISTINCT tt.term_id, tt.term_taxonomy_id
			FROM {$wpdb->term_relationships} tr
			INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
			WHERE tr.object_id = %d AND tt.taxonomy = %s",
			$recipe_id,
			$taxonomy
		);

		$rows = $wpdb->get_results( $query );
		$relationships = array();

		if ( is_array( $rows ) ) {
			foreach ( $rows as $row ) {
				$relationships[ intval( $row->term_id ) ] = intval( $row->term_taxonomy_id );
			}
		}

		return $relationships;
	}

	/**
	 * Get term taxonomy IDs for specific term IDs directly from the database.
	 *
	 * @since	10.8.1
	 * @param	array  $term_ids  Term IDs to look up.
	 * @param	string $taxonomy  Taxonomy to inspect.
	 */
	private static function get_term_taxonomy_ids_from_database( $term_ids, $taxonomy ) {
		global $wpdb;

		$term_ids = array_values( array_unique( array_filter( array_map( 'intval', $term_ids ) ) ) );
		if ( empty( $term_ids ) || ! $taxonomy ) {
			return array();
		}

		$placeholders = implode( ', ', array_fill( 0, count( $term_ids ), '%d' ) );
		$arguments = array_merge( array( $taxonomy ), $term_ids );
		$query = $wpdb->prepare(
			"SELECT term_id, term_taxonomy_id
			FROM {$wpdb->term_taxonomy}
			WHERE taxonomy = %s AND term_id IN ({$placeholders})",
			$arguments
		);

		$rows = $wpdb->get_results( $query );
		$term_taxonomy_ids = array();

		if ( is_array( $rows ) ) {
			foreach ( $rows as $row ) {
				$term_taxonomy_ids[ intval( $row->term_id ) ] = intval( $row->term_taxonomy_id );
			}
		}

		return $term_taxonomy_ids;
	}

	/**
	 * Check if post being saved contains recipes we need to update.
	 *
	 * @since    1.0.0
	 * @param		 int    $id Post ID being saved.
	 * @param		 object $post Post being saved.
	 */
	public static function update_post( $id, $post ) {
		// Use parent post if we're currently updating a revision.
		$revision_parent = wp_is_post_revision( $post );
		if ( $revision_parent ) {
			$post = get_post( $revision_parent );
		}

		$recipe_ids = WPRM_Recipe_Manager::get_recipe_ids_from_post( $post->ID, true );
		$recipe_ids = false === $recipe_ids ? array() : $recipe_ids;

		// Make sure post itself is not included.
		if ( in_array( $post->ID, $recipe_ids ) ) {
			$recipe_ids = array_diff( $recipe_ids, array( $post->ID ) );
		}

		if ( count( $recipe_ids ) > 0 ) {
			// Immediately update when importing, otherwise do on next load to prevent issues with other plugins.
			if ( isset( $_POST['importer_uid'] ) || ( isset( $_POST['action'] ) && 'wprm_finding_parents' === $_POST['action'] ) ) { // Input var okay.
				self::update_recipes_in_post( $post->ID, $recipe_ids );
			} else {
				$post_recipes_to_update = get_option( 'wprm_post_recipes_to_update', array() );
				$post_recipes_to_update[ $post->ID ] = $recipe_ids;
				update_option( 'wprm_post_recipes_to_update', $post_recipes_to_update );
			}
		}

		if ( WPRM_POST_TYPE === $post->post_type ) {
			// Maybe clear cache of parent post.
			$parent_post_id = get_post_meta( $id, 'wprm_parent_post_id', true );
			$parent_post_id = $parent_post_id ? intval( $parent_post_id ) : false;

			if ( $parent_post_id && $parent_post_id !== $post->ID ) {
				WPRM_Cache::clear( $parent_post_id, false );
			}
		}

		// Fix recipes that have this post as a parent post when they aren't actually inside anymore.
		$args = array(
			'post_type' => WPRM_POST_TYPE,
			'post_status' => 'any',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key'     => 'wprm_parent_post_id',
					'compare' => '=',
					'value' => $id,
				),
			),
			'fields' => 'ids',
		);

		$query = new WP_Query( $args );
		
		if ( $query->have_posts() ) {
			$ids = $query->posts;

			foreach ( $ids as $id ) {
				if ( ! in_array( $id, $recipe_ids ) ) {
					delete_post_meta( $id, 'wprm_parent_post_id' );
				}
			}
		}
	}

	/**
	 * Check if post being saved contains recipes we need to update.
	 *
	 * @since    1.19.0
	 */
	public static function update_recipes_check() {
		if ( ! isset( $_POST['action'] ) ) {
			$post_recipes_to_update = get_option( 'wprm_post_recipes_to_update', array() );

			if ( ! empty( $post_recipes_to_update ) ) {
				$i = 0;
				while ( $i < 10 && ! empty( $post_recipes_to_update ) ) {
					// Get first post to update the recipes for.
					$recipe_ids = reset( $post_recipes_to_update );
					$post_id = key( $post_recipes_to_update );

					self::update_recipes_in_post( $post_id, $recipe_ids );

					// Update remaing post/recipes to update.
					unset( $post_recipes_to_update[ $post_id ] );
					$i++;
				}

				update_option( 'wprm_post_recipes_to_update', $post_recipes_to_update );
			}
		}
	}

	/**
	 * Update recipes with post data.
	 *
	 * @since    1.20.0
	 * @param	 mixed $post_id    Post to use the data from.
	 * @param	 array $recipe_ids Recipes to update.
	 */
	public static function update_recipes_in_post( $post_id, $recipe_ids ) {
		$post = get_post( $post_id );

		// Can happen when revision was scheduled and already removed.
		if ( ! $post ) {
			return;
		}

		// Skip Revisionize revisions.
		$revisionize = get_post_meta( $post_id, '_post_revision_of', true );
		if ( $revisionize && is_plugin_active( 'revisionize/revisionize.php' ) && get_post_status( $revisionize ) && intval( $revisionize ) !== $post_id ) {
			return;
		}

		// Skip Revision Manager TMC revisions.
		$rm_tmc = get_post_meta( $post_id, 'linked_post_id', true );
		if ( $rm_tmc && is_plugin_active( 'revision-manager-tmc/revision-manager-tmc.php' ) && get_post_status( $rm_tmc ) && intval( $rm_tmc ) !== $post_id ) {
			return;
		}

		// Skip Revisionary / PublishPress revisions.
		$revisionary = get_post_meta( $post_id, '_rvy_base_post_id', true );
		if ( $revisionary && is_plugin_active( 'revisionary/revisionary.php' ) && get_post_status( $revisionary ) && intval( $revisionary ) !== $post_id ) {
			return;
		}

		// Skip Yoast Duplicate Posts Rewrite.
		$yoast_dp = get_post_meta( $post_id, '_dp_is_rewrite_republish_copy', true );
		if ( $yoast_dp && is_plugin_active( 'duplicate-post/duplicate-post.php' ) ) {
			return;
		}

		if ( 'trash' !== $post->post_status ) {
			$categories = get_the_terms( $post, 'category' );
			$cat_ids = ! $categories || is_wp_error( $categories ) ? array() : wp_list_pluck( $categories, 'term_id' );

			// Don't use pending for recipe as we use that for Recipe Submissions.
			$recipe_post_status = 'pending' === $post->post_status ? 'draft' : $post->post_status;

			// Prevent recipes from taking over custom post statusses (and being excluded from the manage page).
			$allowed_post_statusses = array( 'publish', 'future', 'draft', 'private' );
			
			if ( ! in_array( $recipe_post_status, $allowed_post_statusses ) ) {
				$recipe_post_status = 'draft';
			}

			// Get language for this post.
			$parent_language = WPRM_Compatibility::get_language_for( $post_id );
			$parent_is_wpml_translation = WPRM_Compatibility::is_wpml_translation_post( $post_id );

			// Update recipes.
			foreach ( $recipe_ids as $recipe_id ) {
				// Prevent infinite loop.
				if ( $recipe_id === $post_id ) {
					continue;
				}

				if ( $parent_is_wpml_translation && $parent_language ) {
					$recipe_language = WPRM_Compatibility::get_language_for( $recipe_id );

					// A WPML translated post can contain the original/shared recipe shortcode.
					if ( $recipe_language && $recipe_language !== $parent_language ) {
						continue;
					}
				}

				$recipe = array(
					'ID'          	=> $recipe_id,
					'post_status' 	=> $recipe_post_status,
					'post_date' 	=> $post->post_date,
					'post_date_gmt' => $post->post_date_gmt,
					'post_modified' => $post->post_modified,
					'edit_date'		=> true, // Required when going from draft to future.
				);

				if ( 'parent' === WPRM_Settings::get( 'recipe_use_author' ) ) {
					$recipe['post_author'] = $post->post_author;
				}

				// Check if parent post for this recipe should be updated.
				wp_update_post( $recipe );

				$should_update_parent_post = true;
				if ( WPRM_Settings::get( 'parent_post_autolock' ) ) {
					$current_parent_id = get_post_meta( $recipe_id, 'wprm_parent_post_id', true );

					if ( $current_parent_id && false !== get_post_type( $current_parent_id ) ) {
						// A current parent exists and is still a valid post, so do not update.
						$should_update_parent_post = false;
					}
				}

				if ( $should_update_parent_post ) {
					update_post_meta( $recipe_id, 'wprm_parent_post_id', $post_id );
				}

				// Make sure recipe language matches parent language, unless manually setting the language for public post type.
				if ( 'public' !== WPRM_Settings::get( 'post_type_structure' ) ) {
					if ( false !== $parent_language ) {
						WPRM_Compatibility::set_language_for( $recipe_id, $parent_language );
						self::sync_recipe_term_languages( $recipe_id, $parent_language );
					}
				}

				// Optionally associate categories with recipes.
				if ( is_object_in_taxonomy( WPRM_POST_TYPE, 'category' ) ) {
					wp_set_post_categories( $recipe_id, $cat_ids );
				}
			}
		} else {
			// Parent got deleted, set as draft and remove parent post relation.
			foreach ( $recipe_ids as $recipe_id ) {
				$current_parent_id = intval( get_post_meta( $recipe_id, 'wprm_parent_post_id', true ) );

				if ( $current_parent_id && intval( $post_id ) !== $current_parent_id ) {
					continue;
				}

				$recipe = array(
					'ID'          => $recipe_id,
					'post_status' => 'draft',
				);
				wp_update_post( $recipe );

				delete_post_meta( $recipe_id, 'wprm_parent_post_id' );
			}
		}
	}

	/**
	 * Prevent post type switcher bug from changing our recipe's post type.
	 *
	 * @since    1.4.0
	 * @param		 array $data    Data that might have been modified by Post Type Switcher.
	 * @param	   array $postarr Unmodified post data.
	 */
	public static function post_type_switcher_fix( $data, $postarr ) {
		if ( WPRM_POST_TYPE === $postarr['post_type'] ) {
			$data['post_type'] = WPRM_POST_TYPE;
		}
		return $data;
	}
}

WPRM_Recipe_Saver::init();
