<?php
/**
 * Polylang to Linguator Migration Class
 *
 * @package Linguator
 */

namespace Linguator\Includes\Migration;

// phpcs:disable

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

use Linguator\Includes\Models\Languages;
use Linguator\Includes\Other\Linguator_Language;
use Linguator\Includes\Options\Options;
use WP_Error;

/**
 * Handles migration from Polylang to Linguator
 */
class Polylang_Migration {

	/**
	 * Reference to Linguator model
	 *
	 * @var object
	 */
	private $model;

	/**
	 * Reference to Linguator options
	 *
	 * @var Options
	 */
	private $options;

	/**
	 * Reference to Linguator languages
	 *
	 * @var array<string, Linguator_Language>
	 */
	private $linguator_languages_lists;

	/**
	 * Constructor
	 *
	 * @param object $model Reference to Linguator model.
	 * @param Options $options Reference to Linguator options.
	 */
	public function __construct( $model, Options $options ) {
		$this->model   = $model;
		$this->options = $options;
	}

	/**
	 * Check if Polylang is installed and has data
	 *
	 * @return array|false Returns migration info if Polylang is detected, false otherwise.
	 */
	public function detect_polylang() {
		global $wpdb;

		// Check if Polylang data exists in database (works even if plugin is deactivated)
		// Check for 'language' taxonomy terms directly in database
		$polylang_languages_count = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s",
				'language'
			)
		);

		// Get Polylang settings first to check if Polylang was ever used
		$polylang_options = get_option( 'polylang', array() );
		
		// If no languages found, check if Polylang was ever installed by checking for settings
		if ( empty( $polylang_languages_count ) || 0 === (int) $polylang_languages_count ) {
			// If no languages and no settings, Polylang was never used
			if ( empty( $polylang_options ) ) {
				return false;
			}
			// Settings exist but no languages - still show migration option for settings
			$polylang_languages_count = 0;
		}

		// Try to get languages using get_terms if taxonomy is registered (Polylang is active)
		$polylang_languages = array();
		if ( taxonomy_exists( 'language' ) ) {
			$polylang_languages = get_terms(
				array(
					'taxonomy'   => 'language',
					'hide_empty' => false,
				)
			);
			if ( is_wp_error( $polylang_languages ) ) {
				$polylang_languages = array();
			}
		}

		// Use database count if get_terms didn't work
		if ( empty( $polylang_languages ) && $polylang_languages_count > 0 ) {
			// Get language count from database
			$polylang_languages_count = (int) $polylang_languages_count;
		} else {
			$polylang_languages_count = is_array( $polylang_languages ) ? count( $polylang_languages ) : 0;
		}

		// If no languages found and no settings, return false
		// But if settings exist, we should still show migration option
		if ( empty( $polylang_languages_count ) && empty( $polylang_options ) ) {
			return false;
		}

		// Count translation links - check database directly since taxonomies might not be registered
		$post_translations_count = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s",
				'post_translations'
			)
		);

		$term_translations_count = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s",
				'term_translations'
			)
		);

		// Count static strings translations
		// Polylang stores strings in term meta _pll_strings_translations on language terms
		$strings_count = 0;
		if ( ! empty( $polylang_languages ) ) {
			foreach ( $polylang_languages as $lang ) {
				$strings = get_term_meta( $lang->term_id, '_pll_strings_translations', true );
				if ( ! empty( $strings ) && is_array( $strings ) ) {
					$strings_count += count( $strings );
				}
			}
		} elseif ( $polylang_languages_count > 0 ) {
			// Query database directly if languages aren't loaded
			$language_terms = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT t.term_id 
					FROM {$wpdb->terms} t
					INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
					WHERE tt.taxonomy = %s",
					'language'
				)
			);
			if ( ! empty( $language_terms ) ) {
				foreach ( $language_terms as $lang_term ) {
					$strings = get_term_meta( $lang_term->term_id, '_pll_strings_translations', true );
					if ( ! empty( $strings ) && is_array( $strings ) ) {
						$strings_count += count( $strings );
					}
				}
			}
		}

		return array(
			'has_polylang'          => true,
			'languages_count'      => $polylang_languages_count,
			'post_translations'     => $post_translations_count,
			'term_translations'     => $term_translations_count,
			'strings_count'        => $strings_count,
			'has_settings'         => ! empty( $polylang_options ),
		);
	}

	/**
	 * Migrate languages from Polylang to Linguator
	 *
	 * @return array Migration result.
	 */
	public function migrate_languages() {
		global $wpdb;
		
		$results = array(
			'success' => true,
			'migrated' => 0,
			'errors' => array(),
		);

		// Get Polylang languages - query database directly if taxonomy not registered
		$polylang_languages = array();
		if ( taxonomy_exists( 'language' ) ) {
			$polylang_languages = get_terms(
				array(
					'taxonomy'   => 'language',
					'hide_empty' => false,
				)
			);
			if ( is_wp_error( $polylang_languages ) ) {
				$polylang_languages = array();
			}
		}

		// If get_terms didn't work, query database directly
		if ( empty( $polylang_languages ) ) {
			$language_terms = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT t.term_id, t.name, t.slug, tt.description 
					FROM {$wpdb->terms} t
					INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
					WHERE tt.taxonomy = %s",
					'language'
				)
			);

			if ( ! empty( $language_terms ) ) {
				// Convert to term objects
				foreach ( $language_terms as $term_data ) {
					$term = new \WP_Term( (object) array(
						'term_id'     => $term_data->term_id,
						'name'        => $term_data->name,
						'slug'        => $term_data->slug,
						'description' => $term_data->description,
						'taxonomy'     => 'language',
					) );
					$polylang_languages[] = $term;
				}
			}
		}

		if ( empty( $polylang_languages ) ) {
			$results['success'] = false;
			$results['errors'][] = __( 'No Polylang languages found.', 'translate-words' );
			return $results;
		}

		// Get existing Linguator languages to avoid duplicates
		$existing_languages = $this->model->languages->get_list();
		$existing_slugs = array();
		foreach ( $existing_languages as $lang ) {
			$existing_slugs[] = $lang->slug;
		}

		$default_lang_set = false;

		foreach ( $polylang_languages as $pll_lang ) {
			// Parse language metadata from description
			$lang_data = maybe_unserialize( $pll_lang->description );
			if ( ! is_array( $lang_data ) ) {
				$lang_data = array();
			}

			// Extract language properties
			$locale = isset( $lang_data['locale'] ) ? $lang_data['locale'] : $pll_lang->slug;
			$rtl    = isset( $lang_data['rtl'] ) ? (bool) $lang_data['rtl'] : false;
			$flag   = isset( $lang_data['flag_code'] ) ? $lang_data['flag_code'] : ( isset( $lang_data['flag'] ) ? $lang_data['flag'] : '' );

			// Skip if language already exists
			if ( in_array( $pll_lang->slug, $existing_slugs, true ) ) {
				continue;
			}

			// Add language to Linguator
			$result = $this->model->languages->add(
				array(
					'name'       => $pll_lang->name,
					'slug'       => $pll_lang->slug,
					'locale'     => $locale,
					'rtl'        => $rtl,
					'flag'       => $flag,
					'term_group' => isset( $pll_lang->term_group ) ? (int) $pll_lang->term_group : 0,
				)
			);

			if ( is_wp_error( $result ) ) {
				$results['errors'][] = sprintf(
					/* translators: %s: Language name */
					__( 'Failed to migrate language: %s', 'translate-words' ),
					$pll_lang->name
				);
				$results['success'] = false;
			} else {
				$results['migrated']++;

				// Set default language if not set yet
				if ( ! $default_lang_set ) {
					$polylang_options = get_option( 'polylang', array() );
					if ( ! empty( $polylang_options['default_lang'] ) && $polylang_options['default_lang'] === $pll_lang->slug ) {
						$this->options->set( 'default_lang', $pll_lang->slug );
						$default_lang_set = true;
					} elseif ( empty( $this->options['default_lang'] ) ) {
						// If no default is set in Polylang, use the first migrated language
						$this->options->set( 'default_lang', $pll_lang->slug );
						$default_lang_set = true;
					}
				}
			}
		}

		return $results;
	}

	/**
	 * Migrate individual post and term language assignments
	 *
	 * @return array Migration result.
	 */
	public function linguator_migrate_language_assignments(&$term_count_update_languages) {
		global $wpdb;
		
		$results = array(
			'success' => true,
			'posts_assigned' => 0,
			'terms_assigned' => 0,
			'errors' => array(),
		);

		if(!isset($this->linguator_languages_lists) || empty($this->linguator_languages_lists) || count($this->linguator_languages_lists) < 1) {
			return $results;
		}

		// -----------------------------
		// 1. Build Polylang → Linguator language map
		// -----------------------------
		$lang_map = [];
	
		foreach ( $this->linguator_languages_lists as $slug => $data ) {
			$slug  = sanitize_key( $slug );
			$linguator_language = absint( $data['lmat_language'] );
			$linguator_term_language = absint( $data['lmat_term_language'] );
			
			if ( $slug && $linguator_language && $linguator_term_language ) {
				$lang_map[ $slug ] = array( 'lmat_language' => $linguator_language, 'lmat_term_language' => $linguator_term_language );
			}
		}
	
		if ( empty( $lang_map ) ) {
			return $results;
		}

		$this->linguator_migration_post_language_assignment($results, $lang_map);
		$this->linguator_migration_term_language_assignment($results, $lang_map);

		$term_count_update_languages=$lang_map;

		return $results;
	}

	public function linguator_migration_post_language_assignment( &$results, $lang_map ) {
		global $wpdb;
	
		if ( ! isset( $results['errors'] ) ) {
			$results['errors'] = [];
		}
		if ( ! isset( $results['posts_assigned'] ) ) {
			$results['posts_assigned'] = 0;
		}
		if ( ! isset( $results['success'] ) ) {
			$results['success'] = true;
		}
	
		// -----------------------------
		// 2. Fetch posts with Polylang language (exclude already migrated)
		// -----------------------------
		$posts = $wpdb->get_results(
			$wpdb->prepare(
				"
				SELECT DISTINCT p.ID, t.slug
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
				LEFT JOIN {$wpdb->term_relationships} lmat_tr ON p.ID = lmat_tr.object_id
				LEFT JOIN {$wpdb->term_taxonomy} lmat_tt
					ON lmat_tr.term_taxonomy_id = lmat_tt.term_taxonomy_id
					AND lmat_tt.taxonomy = %s
				WHERE tt.taxonomy = %s
				AND p.post_status != %s
				AND lmat_tt.term_taxonomy_id IS NULL
				",
				'lmat_language',
				'language',
				'auto-draft'
			)
		);
	
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		if ( empty( $posts ) ) {
			return $results;
		}
	
		// -----------------------------
		// 3. Prepare bulk insert data
		// -----------------------------
		$new_relations     = [];
		$inserted_post_ids = [];
	
		foreach ( $posts as $row ) {
			$post_id = absint( $row->ID );
			$slug    = sanitize_key( $row->slug );
	
			if ( ! $post_id || ! isset( $lang_map[ $slug ]['lmat_language'] ) ) {
				continue;
			}
	
			$new_relations[]     = [ $post_id, absint( $lang_map[ $slug ]['lmat_language'] ) ];
			$inserted_post_ids[] = $post_id;
		}
	
		if ( empty( $new_relations ) ) {
			return $results;
		}

		// Delete old relations
		$delete_old_relations_ids = implode( ',', array_fill( 0, count( $inserted_post_ids ), '%d' ) );

		$wpdb->query(
			$wpdb->prepare(
				"
				DELETE tr
				FROM {$wpdb->term_relationships} tr
				INNER JOIN {$wpdb->term_taxonomy} tt
					ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND tr.object_id IN ( {$delete_old_relations_ids} )
				",
				array_merge(
					[ 'lmat_language' ],
					$inserted_post_ids
				)
			)
		);
		
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		// -----------------------------
		// 4. Bulk INSERT (ON DUPLICATE)
		// -----------------------------
		$placeholders = implode( ',', array_fill( 0, count( $new_relations ), '( %d, %d, 0 )' ) );
	
		$insert_sql = $wpdb->prepare(
			"INSERT INTO {$wpdb->term_relationships}
			 ( object_id, term_taxonomy_id, term_order )
			 VALUES {$placeholders}
			 ON DUPLICATE KEY UPDATE
			 term_taxonomy_id = VALUES(term_taxonomy_id)",
			array_merge( ...$new_relations )
		);
	
		$wpdb->query( $insert_sql );

		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		$results['posts_assigned'] += count( $new_relations );
	
		// -----------------------------
		// 5. Fetch Polylang post_translations
		// -----------------------------
		$id_placeholders = implode( ',', array_fill( 0, count( $inserted_post_ids ), '%d' ) );
	
		$pll_descs = $wpdb->get_results(
			$wpdb->prepare(
				"
				SELECT p.ID, tt.description
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND p.ID IN ( {$id_placeholders} )
				",
				array_merge( [ 'post_translations' ], $inserted_post_ids )
			)
		);
	
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		if ( empty( $pll_descs ) ) {
			return $results;
		}
	
		// -----------------------------
		// 6. Fetch existing LMAT post translations
		// -----------------------------
		$existing = $wpdb->get_col(
			$wpdb->prepare(
				"
				SELECT DISTINCT p.ID
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND p.ID IN ( {$id_placeholders} )
				",
				array_merge( [ 'lmat_post_translations' ], $inserted_post_ids )
			)
		);
	
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		$existing = array_map( 'absint', (array) $existing );
	
		// -----------------------------
		// 7. Prepare terms
		// -----------------------------
		$terms = [];
	
		foreach ( $pll_descs as $row ) {
			$post_id = absint( $row->ID );
	
			if ( in_array( $post_id, $existing, true ) ) {
				continue;
			}
	
			$data = maybe_unserialize( $row->description );
			if ( ! is_array( $data ) ) {
				continue;
			}

			$pll_term_desc=maybe_unserialize($row->description);
			$filter_description=array();

			if(is_array($pll_term_desc)){
				foreach($pll_term_desc as $key => $value){
					$filter_description[sanitize_text_field($key)]=intval($value);
				}
			}
	
			$key = uniqid( 'lmat_' );
			$terms[ $key ] = [
				'name' => $key,
				'slug' => $key,
				'post_id' => $post_id,
				'filter_description' => $filter_description,
			];
		}
	
		if ( empty( $terms ) ) {
			return $results;
		}
	
		// -----------------------------
		// 8. Insert terms safely
		// -----------------------------
		$term_values = [];
		$term_taxonomy_derived_sql  = [];
		$term_taxonomy_derived_args = [];
		$term_relationships_derived_sql = [];
		$term_relationships_derived_args = [];

		foreach ( $terms as $term ) {
			$slug        = sanitize_text_field( $term['slug'] );
			$name = sanitize_text_field( $term['name'] );
			$description = maybe_serialize( $term['filter_description'] );
			$count       = is_array( $term['filter_description'] )
				? count( $term['filter_description'] )
				: 0;
				
			$term_values[] = $wpdb->prepare( '( %s, %s, 0 )', $name, $slug );

			$term_taxonomy_derived_sql[] = 'SELECT %s AS slug, %s AS description, %d AS term_count';
			$term_relationships_derived_sql[] = 'SELECT %s AS slug, %d AS post_id, %s AS search_val';

			$term_taxonomy_derived_args[] = $slug;
			$term_taxonomy_derived_args[] = $description;
			$term_taxonomy_derived_args[] = $count;

			$term_relationships_derived_args[] = $slug;
			$term_relationships_derived_args[] = absint( $term['post_id'] );
			$term_relationships_derived_args[] = '%i:' . absint( $term['post_id'] ) . ';%';
		}

		$wpdb->query(
			"INSERT IGNORE INTO {$wpdb->terms} ( name, slug, term_group ) VALUES " . implode( ',', $term_values )
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
		}

		/**
		 * Insert terms into term_taxonomy
		 */
		$wpdb->query(
		$wpdb->prepare(
			"
			INSERT IGNORE INTO {$wpdb->term_taxonomy}
				( term_id, taxonomy, description, parent, count )
			SELECT
				t.term_id,
				%s,
				d.description,
				0,
				d.term_count
			FROM {$wpdb->terms} t
			INNER JOIN (
				" . implode( ' UNION ALL ', $term_taxonomy_derived_sql ) . "
			) d ON d.slug = t.slug
			WHERE t.name = t.slug
			",
			array_merge(
				[ 'lmat_post_translations' ],
				$term_taxonomy_derived_args
			)
			)
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}

		/**
		 * Insert terms into term_relationships
		 */
		$wpdb->query(
			$wpdb->prepare(
				"
				INSERT INTO {$wpdb->term_relationships}
					( object_id, term_taxonomy_id, term_order )
				SELECT
					d.post_id,
					tt.term_taxonomy_id,
					0
				FROM {$wpdb->term_taxonomy} tt
				INNER JOIN {$wpdb->terms} t
					ON tt.term_id = t.term_id
				INNER JOIN (
					" . implode( ' UNION ALL ', $term_relationships_derived_sql ) . "
				) d
					ON d.slug = t.slug
					AND tt.description LIKE d.search_val
				WHERE tt.taxonomy = %s
				ON DUPLICATE KEY UPDATE
					term_taxonomy_id = VALUES(term_taxonomy_id)
				",
				array_merge(
					$term_relationships_derived_args,
					[ 'lmat_post_translations' ]
				)
			)
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
			
		return $results;
	}
	

	public function linguator_migration_term_language_assignment(&$results, $lang_map){
		global $wpdb;
		
		$pll_translation_terms = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT tr.object_id as taxonomy_id, tt.term_taxonomy_id as term_taxonomy_id, tt.description as description, t.term_id as term_id, t.slug as slug
				FROM {$wpdb->term_relationships} tr
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
				WHERE tt.taxonomy = %s",
				'term_translations'
			)
		);

		// -----------------------------
		// 2. Fetch posts with Polylang language (exclude already migrated)
		// -----------------------------
		$pll_translation_terms = $wpdb->get_results(
			$wpdb->prepare(
				"
				SELECT DISTINCT ct.term_id as term_id, pt.slug
				FROM {$wpdb->terms} ct
				INNER JOIN {$wpdb->term_relationships} tr ON ct.term_id = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				INNER JOIN {$wpdb->terms} pt ON tt.term_id = pt.term_id
				LEFT JOIN {$wpdb->term_relationships} lmat_tr ON ct.term_id = lmat_tr.object_id
				LEFT JOIN {$wpdb->term_taxonomy} lmat_tt
					ON lmat_tr.term_taxonomy_id = lmat_tt.term_taxonomy_id
					AND lmat_tt.taxonomy = %s
				WHERE tt.taxonomy = %s
				AND lmat_tt.term_taxonomy_id IS NULL",
				'lmat_term_language',
				'term_language',
			)
		);


		if(empty($pll_translation_terms)) {
			return $results;
		}

		// -----------------------------
		// 3. Prepare bulk insert data
		// -----------------------------
		$new_relations     = [];
		$inserted_term_ids = [];

		foreach ( $pll_translation_terms as $row ) {
			$term_id = absint( $row->term_id );
			$slug    = str_replace('pll_', '', sanitize_text_field($row->slug) );
	
			if ( ! $term_id || ! isset( $lang_map[ $slug ]['lmat_term_language'] ) ) {
				continue;
			}
	
			$new_relations[]     = [ $term_id, absint( $lang_map[ $slug ]['lmat_term_language'] ) ];
			$inserted_term_ids[] = $term_id;
		}
	
		if ( empty( $new_relations ) ) {
			return $results;
		}


		// Delete old relations
		$delete_old_relations_ids = implode( ',', array_fill( 0, count( $inserted_term_ids ), '%d' ) );
	
		$wpdb->query(
			$wpdb->prepare(
				"
				DELETE tr
				FROM {$wpdb->term_relationships} tr
				INNER JOIN {$wpdb->term_taxonomy} tt
					ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND tr.object_id IN ( {$delete_old_relations_ids} )
				",
				array_merge(
					[ 'lmat_term_language' ],
					$inserted_term_ids
				)
			)
		);

		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}

		// -----------------------------
		// 4. Bulk INSERT (ON DUPLICATE)
		// -----------------------------
		$placeholders = implode( ',', array_fill( 0, count( $new_relations ), '( %d, %d, 0 )' ) );
	
		$insert_sql = $wpdb->prepare(
			"INSERT INTO {$wpdb->term_relationships}
			 ( object_id, term_taxonomy_id, term_order )
			 VALUES {$placeholders}
			 ON DUPLICATE KEY UPDATE
			 term_taxonomy_id = VALUES(term_taxonomy_id)",
			array_merge( ...$new_relations )
		);
	
		$wpdb->query( $insert_sql );

		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		$results['posts_assigned'] += count( $new_relations );
	
		// -----------------------------
		// 5. Fetch Polylang post_translations
		// -----------------------------
		$id_placeholders = implode( ',', array_fill( 0, count( $inserted_term_ids ), '%d' ) );
	
		$pll_descs = $wpdb->get_results(
			$wpdb->prepare(
				"
				SELECT t.term_id as ID, tt.description
				FROM {$wpdb->terms} t
				INNER JOIN {$wpdb->term_relationships} tr ON t.term_id = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND t.term_id IN ( {$id_placeholders} )
				",
				array_merge( [ 'term_translations' ], $inserted_term_ids )
			)
		);
	
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		if ( empty( $pll_descs ) ) {
			return $results;
		}
	
		// -----------------------------
		// 6. Fetch existing LMAT post translations
		// -----------------------------
		$existing = $wpdb->get_col(
			$wpdb->prepare(
				"
				SELECT DISTINCT t.term_id as ID
				FROM {$wpdb->terms} t
				INNER JOIN {$wpdb->term_relationships} tr ON t.term_id = tr.object_id
				INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
				WHERE tt.taxonomy = %s
				AND t.term_id IN ( {$id_placeholders} )
				",
				array_merge( [ 'lmat_term_translations' ], $inserted_term_ids )
			)
		);

	
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
	
		$existing = array_map( 'absint', (array) $existing );
	
		// -----------------------------
		// 7. Prepare terms
		// -----------------------------
		$terms = [];
	
		foreach ( $pll_descs as $row ) {
			$term_id = absint( $row->ID );
	
			if ( in_array( $term_id, $existing, true ) ) {
				continue;
			}
	
			$data = maybe_unserialize( $row->description );
			if ( ! is_array( $data ) ) {
				continue;
			}

			$pll_term_desc=maybe_unserialize($row->description);
			$filter_description=array();

			if(is_array($pll_term_desc)){
				foreach($pll_term_desc as $key => $value){
					$filter_description[sanitize_text_field($key)]=intval($value);
				}
			}
	
			$key = uniqid( 'lmat_' );
			$terms[ $key ] = [
				'name' => $key,
				'slug' => $key,
				'term_id' => $term_id,
				'filter_description' => $filter_description,
			];
		}
	
		if ( empty( $terms ) ) {
			return $results;
		}
	
		// -----------------------------
		// 8. Insert terms safely
		// -----------------------------
		$term_values = [];
		$term_taxonomy_derived_sql  = [];
		$term_taxonomy_derived_args = [];
		$term_relationships_derived_sql = [];
		$term_relationships_derived_args = [];

		foreach ( $terms as $term ) {
			$slug        = sanitize_text_field( $term['slug'] );
			$name = sanitize_text_field( $term['name'] );
			$description = maybe_serialize( $term['filter_description'] );
			$count       = is_array( $term['filter_description'] )
				? count( $term['filter_description'] )
				: 0;
				
			$term_values[] = $wpdb->prepare( '( %s, %s, 0 )', $name, $slug );

			$term_taxonomy_derived_sql[] = 'SELECT %s AS slug, %s AS description, %d AS term_count';
			$term_relationships_derived_sql[] = 'SELECT %s AS slug, %d AS term_id, %s AS search_val';

			$term_taxonomy_derived_args[] = $slug;
			$term_taxonomy_derived_args[] = $description;
			$term_taxonomy_derived_args[] = $count;

			$term_relationships_derived_args[] = $slug;
			$term_relationships_derived_args[] = absint( $term['term_id'] );
			$term_relationships_derived_args[] = '%i:' . absint( $term['term_id'] ) . ';%';
		}
	
		$wpdb->query(
			"INSERT IGNORE INTO {$wpdb->terms} ( name, slug, term_group ) VALUES " . implode( ',', $term_values )
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
		}

		/**
		 * Insert terms into term_taxonomy
		 */
		$wpdb->query(
		$wpdb->prepare(
			"
			INSERT IGNORE INTO {$wpdb->term_taxonomy}
				( term_id, taxonomy, description, parent, count )
			SELECT
				t.term_id,
				%s,
				d.description,
				0,
				d.term_count
			FROM {$wpdb->terms} t
			INNER JOIN (
				" . implode( ' UNION ALL ', $term_taxonomy_derived_sql ) . "
			) d ON d.slug = t.slug
			WHERE t.name = t.slug
			",
			array_merge(
				[ 'lmat_term_translations' ],
				$term_taxonomy_derived_args
			)
			)
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}

		/**
		 * Insert terms into term_relationships
		 */
		$wpdb->query(
			$wpdb->prepare(
				"
				INSERT INTO {$wpdb->term_relationships}
					( object_id, term_taxonomy_id, term_order )
				SELECT
					d.term_id,
					tt.term_taxonomy_id,
					0
				FROM {$wpdb->term_taxonomy} tt
				INNER JOIN {$wpdb->terms} t
					ON tt.term_id = t.term_id
				INNER JOIN (
					" . implode( ' UNION ALL ', $term_relationships_derived_sql ) . "
				) d
					ON d.slug = t.slug
					AND tt.description LIKE d.search_val
				WHERE tt.taxonomy = %s
				ON DUPLICATE KEY UPDATE
					term_taxonomy_id = VALUES(term_taxonomy_id)
				",
				array_merge(
					$term_relationships_derived_args,
					[ 'lmat_term_translations' ]
				)
			)
		);
		if ( $wpdb->last_error ) {
			$results['errors'][] = esc_html( $wpdb->last_error );
			$results['success']  = false;
			return $results;
		}
			
		return $results;
		
	}

	private function linguator_update_term_counts(&$results, $lang_map){
		global $wpdb;

		$post_types = array_unique(
			array_merge(
				(array) $this->model->options->get('post_types'),
				['post', 'page', 'elementor_library']
			)
		);

		$media_support = $this->model->options->get('media_support');

		if($media_support) {
			$post_types[] = 'attachment';
		}
		
		$term_taxonomy_ids = array_map(
			'intval',
			array_column( $lang_map, 'lmat_language' )
		);

		$post_type_placeholders = implode(
			',',
			array_fill( 0, count( $post_types ), '%s' )
		);
		
		$ttid_placeholders = implode(
			',',
			array_fill( 0, count( $term_taxonomy_ids ), '%d' )
		);
		
		$update_counts = "UPDATE {$wpdb->term_taxonomy} tt
		LEFT JOIN (
			SELECT
				tr.term_taxonomy_id,
				COUNT(DISTINCT p.ID) AS total
			FROM {$wpdb->term_relationships} tr
			INNER JOIN {$wpdb->posts} p
				ON p.ID = tr.object_id
			LEFT JOIN {$wpdb->posts} pp
				ON p.post_parent = pp.ID
			WHERE
				(
					p.post_status = 'publish'
					OR (p.post_type = 'attachment' AND p.post_status = 'inherit' AND pp.post_status = 'publish' AND pp.post_type IN ($post_type_placeholders))
				)
				AND p.post_type IN ($post_type_placeholders)
				AND tr.term_taxonomy_id IN ($ttid_placeholders)
			GROUP BY tr.term_taxonomy_id
		) rel ON tt.term_taxonomy_id = rel.term_taxonomy_id
		SET tt.count = COALESCE( rel.total, 0 )
		WHERE
			tt.taxonomy = %s
			AND tt.term_taxonomy_id IN ($ttid_placeholders)
			AND tt.count <> COALESCE( rel.total, 0 )
		";

		$params = array_merge(
			$post_types,                // for pp.post_type IN (...)
			$post_types,                // for post_type IN (...)
			$term_taxonomy_ids,          // for subquery IN (...)
			['lmat_language'],           // taxonomy
			$term_taxonomy_ids           // for outer WHERE IN (...)
		);

		$wpdb->query(
			$wpdb->prepare( $update_counts, $params )
		);

		if($wpdb->last_error) {
			$results['errors'][] = esc_html( $wpdb->last_error );
		}

		$this->model->clean_languages_cache();
	}

	private function set_linguator_taxonomy_id() {
		$linguator_languages = $this->model->languages->get_list();

		foreach($linguator_languages as $linguator_language) {
			$taxonomy_id = $linguator_language->get_tax_prop('lmat_language','term_taxonomy_id');
			$term_id = $linguator_language->get_tax_prop('lmat_term_language','term_taxonomy_id');

			$lang_slug = $linguator_language->slug;
			$taxonomy_id = (int) $taxonomy_id;

			if(!$lang_slug || empty($lang_slug) || !$taxonomy_id || empty($taxonomy_id)) {
				continue;	
			}

			$this->linguator_languages_lists[$lang_slug] = array( 'lmat_language' => (int) $taxonomy_id, 'lmat_term_language' => (int) $term_id );
		}
	}

	/**
	 * Migrate settings from Polylang to Linguator
	 *
	 * @return array Migration result.
	 */
	public function migrate_settings() {
		$results = array(
			'success' => true,
			'migrated' => array(),
			'errors' => array(),
		);

		$polylang_options = get_option( 'polylang', array() );
		if ( empty( $polylang_options ) || ! is_array( $polylang_options ) ) {
			return $results;
		}

		// Ensure options are registered
		do_action( 'lmat_init_options_for_blog', $this->options, get_current_blog_id() );

		// Map Polylang settings to Linguator settings
		// All settings use the same key names in both plugins
		$settings_map = array(
			// URL modifications
			'force_lang'       => 'force_lang',        // How language is determined (0=content, 1=directory, 2=subdomain, 3=domain)
			'domains'          => 'domains',           // Domain mapping per language
			'hide_default'     => 'hide_default',      // Hide language code for default language
			'rewrite'          => 'rewrite',            // Remove /language/ in pretty permalinks
			'redirect_lang'    => 'redirect_lang',     // Redirect to language
			// Browser detection
			'browser'          => 'browser',            // Detect browser language
			// Media
			'media_support'    => 'media_support',      // Translate media
			// Custom post types and taxonomies
			'post_types'       => 'post_types',         // Translatable post types
			'taxonomies'       => 'taxonomies',         // Translatable taxonomies
			// Synchronization
			'sync'             => 'sync',               // Synchronization settings
			// Navigation menus
			'nav_menus'        => 'nav_menus',          // Navigation menu locations per language
			// Default language (migrated separately in migrate_languages, but included here for completeness)
			'default_lang'     => 'default_lang',       // Default language slug
		);

		foreach ( $settings_map as $pll_key => $linguator_key ) {
			if ( ! isset( $polylang_options[ $pll_key ] ) ) {
				continue;
			}
			
			$value = $polylang_options[ $pll_key ];
			
			// Skip null values
			if ( null === $value ) {
				continue;
			}
			
			// For default_lang, only migrate if not already set (it's set during language migration)
			if ( 'default_lang' === $linguator_key && ! empty( $this->options[ $linguator_key ] ) ) {
				continue;
			}
			
			// Check if setting already exists in Linguator
			$existing_value = $this->options->get( $linguator_key );
			
			// For boolean settings (browser, media_support, hide_default, redirect_lang, rewrite)
			// Always migrate if they exist in Polylang, even if false
			$boolean_settings = array( 'browser', 'media_support', 'hide_default', 'redirect_lang', 'rewrite' );
			$should_migrate = false;
			
			if ( in_array( $linguator_key, $boolean_settings, true ) ) {
				// Always migrate boolean settings from Polylang
				$should_migrate = true;
			} elseif ( empty( $existing_value ) || ( is_array( $existing_value ) && empty( $existing_value ) ) ) {
				// For other settings, migrate if Linguator doesn't have a value
				$should_migrate = true;
			}
			
			if ( ! $should_migrate ) {
				continue;
			}
			
			// Convert language slugs in settings if needed
			if ( is_array( $value ) ) {
				$value = $this->linguator_convert_language_slugs_in_array( $value );
				// Skip if array became empty after conversion (unless it's a boolean-like array)
				if ( empty( $value ) && ! in_array( $linguator_key, array( 'sync', 'post_types', 'taxonomies' ), true ) ) {
					continue;
				}
			}
			
			// Special handling for certain settings
			if ( 'force_lang' === $linguator_key ) {
				// Ensure force_lang is a valid integer (0, 1, 2, or 3)
				$value = (int) $value;
				if ( ! in_array( $value, array( 0, 1, 2, 3 ), true ) ) {
					$value = 1; // Default to directory mode
				}
			} elseif ( in_array( $linguator_key, $boolean_settings, true ) ) {
				// Ensure boolean settings are actual booleans
				$value = (bool) $value;
			} elseif ( 'domains' === $linguator_key && is_array( $value ) ) {
				// Domains should be an associative array with language slugs as keys
				// Already handled by linguator_convert_language_slugs_in_array
			} elseif ( in_array( $linguator_key, array( 'post_types', 'taxonomies', 'sync' ), true ) && ! is_array( $value ) ) {
				// These should be arrays
				if ( empty( $value ) ) {
					$value = array();
				} else {
					// Convert to array if it's a string or other type
					$value = (array) $value;
				}
			}
			
			// Check if option exists before trying to set it
			if ( ! $this->options->has( $linguator_key ) ) {
				$results['errors'][] = sprintf(
					/* translators: %s: Setting key */
					__( 'Setting %s is not registered in Linguator', 'translate-words' ),
					$linguator_key
				);
				$results['success'] = false;
				continue;
			}
			
			$result = $this->options->set( $linguator_key, $value );
			if ( ! $result->has_errors() ) {
				$results['migrated'][] = $linguator_key;
			} else {
				// Get error messages for debugging
				$error_messages = $result->get_error_messages();
				$error_message = ! empty( $error_messages ) ? implode( ', ', $error_messages ) : '';
				$results['errors'][] = sprintf(
					/* translators: %1$s: Setting key, %2$s: Error message */
					__( 'Failed to migrate setting: %1$s%2$s', 'translate-words' ),
					$linguator_key,
					$error_message ? ' (' . $error_message . ')' : ''
				);
				$results['success'] = false;
			}
		}

		// Save all modified options
		if ( ! empty( $results['migrated'] ) ) {
			$this->options->save();
		}

		return $results;
	}

	/**
	 * Convert language slugs in an array (for settings migration)
	 *
	 * @param array $array Array that may contain language slugs.
	 * @return array Converted array.
	 */
	private function linguator_convert_language_slugs_in_array( $array ) {
		foreach ( $array as $key => $value ) {
			if ( is_array( $value ) ) {
				$array[ $key ] = $this->linguator_convert_language_slugs_in_array( $value );
			} elseif ( is_string( $key ) ) {
				// Check if key is a language slug
				$linguator_lang = $this->model->languages->get( $key );
				if ( ! $linguator_lang ) {
					// Key might be a Polylang slug that doesn't exist in Linguator, skip it
					unset( $array[ $key ] );
				}
			}
		}
		return $array;
	}

	/**
	 * Migrate static strings translations from Polylang to Linguator
	 *
	 * @return array Migration result.
	 */
	public function migrate_strings() {
		global $wpdb;
		
		$results = array(
			'success' => true,
			'strings_migrated' => 0,
			'languages_processed' => 0,
			'errors' => array(),
		);

		// Get all Polylang languages
		$polylang_languages = array();
		if ( taxonomy_exists( 'language' ) ) {
			$polylang_languages = get_terms(
				array(
					'taxonomy'   => 'language',
					'hide_empty' => false,
				)
			);
			if ( is_wp_error( $polylang_languages ) ) {
				$polylang_languages = array();
			}
		}

		// If get_terms didn't work, query database directly
		if ( empty( $polylang_languages ) ) {
			$language_terms = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT t.term_id, t.name, t.slug, tt.description 
					FROM {$wpdb->terms} t
					INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
					WHERE tt.taxonomy = %s",
					'language'
				)
			);

			if ( ! empty( $language_terms ) ) {
				foreach ( $language_terms as $term_data ) {
					$term = new \WP_Term( (object) array(
						'term_id'     => $term_data->term_id,
						'name'        => $term_data->name,
						'slug'        => $term_data->slug,
						'description' => $term_data->description,
						'taxonomy'     => 'language',
					) );
					$polylang_languages[] = $term;
				}
			}
		}

		if ( empty( $polylang_languages ) ) {
			return $results;
		}

		// Migrate strings for each language
		foreach ( $polylang_languages as $pll_lang ) {
			// Get Polylang strings for this language
			$pll_strings = get_term_meta( $pll_lang->term_id, '_pll_strings_translations', true );
			
			if ( empty( $pll_strings ) || ! is_array( $pll_strings ) ) {
				continue;
			}

			// Find the corresponding Linguator language
			$linguator_lang = $this->model->languages->get( $pll_lang->slug );
			if ( ! $linguator_lang ) {
				$results['errors'][] = sprintf(
					/* translators: %s: Language slug */
					__( 'Linguator language not found for Polylang language: %s', 'translate-words' ),
					$pll_lang->slug
				);
				$results['success'] = false;
				continue;
			}

			// Get existing Linguator strings for this language
			$linguator_strings = get_term_meta( $linguator_lang->term_id, '_lmat_strings_translations', true );
			if ( ! is_array( $linguator_strings ) ) {
				$linguator_strings = array();
			}

			// Merge Polylang strings with existing Linguator strings
			// Use original string as key to avoid duplicates
			$strings_map = array();
			foreach ( $linguator_strings as $string_pair ) {
				if ( is_array( $string_pair ) && isset( $string_pair[0] ) ) {
					$strings_map[ $string_pair[0] ] = $string_pair;
				}
			}

			// Add Polylang strings (will overwrite if duplicate)
			$strings_added = 0;
			foreach ( $pll_strings as $string_pair ) {
				if ( ! is_array( $string_pair ) || ! isset( $string_pair[0] ) || ! isset( $string_pair[1] ) ) {
					continue;
				}
				
				$original = wp_unslash( $string_pair[0] );
				$translation = wp_unslash( $string_pair[1] );
				
				// Skip empty strings
				if ( '' === $original || '' === $translation ) {
					continue;
				}

				// Add or update the string translation
				$strings_map[ $original ] = array( $original, $translation );
				$strings_added++;
			}

			// Convert back to array format and save
			$merged_strings = array_values( $strings_map );
			
			// Update term meta with merged strings
			// Note: update_term_meta returns false if value is unchanged, so we always try to update
			// and then verify by reading back the value
			update_term_meta( $linguator_lang->term_id, '_lmat_strings_translations', $merged_strings );
			
			// Verify the update was successful by reading the stored value
			// update_term_meta can return false if value is unchanged, so we verify by reading
			$stored_meta = get_term_meta( $linguator_lang->term_id, '_lmat_strings_translations', true );
			
			// Verify the update was successful
			if ( is_array( $stored_meta ) && ! empty( $stored_meta ) ) {
				// Check if at least the expected strings are stored
				$stored_count = count( $stored_meta );
				$expected_count = count( $merged_strings );
				
				// If we have strings stored (even if count doesn't match exactly due to merging),
				// consider it successful if we have at least as many as we tried to add
				if ( $stored_count >= $expected_count || ( $stored_count > 0 && $strings_added > 0 ) ) {
					$results['strings_migrated'] += $strings_added;
					$results['languages_processed']++;
				} else {
					$results['errors'][] = sprintf(
						/* translators: %1$s: Language slug, %2$d: Stored count, %3$d: Expected count */
						__( 'Failed to save strings for language: %1$s (stored: %2$d, expected: %3$d)', 'translate-words' ),
						$linguator_lang->slug,
						$stored_count,
						$expected_count
					);
					$results['success'] = false;
				}
			} else {
				$results['errors'][] = sprintf(
					/* translators: %s: Language slug */
					__( 'Failed to save strings for language: %s (no strings stored)', 'translate-words' ),
					$linguator_lang->slug
				);
				$results['success'] = false;
			}
		}

		// Clear cache after migration
		if ( $results['strings_migrated'] > 0 ) {
			// Clear Linguator strings cache
			if ( class_exists( '\Linguator\Includes\Helpers\Linguator_Cache' ) ) {
				$cache = new \Linguator\Includes\Helpers\Linguator_Cache();
				foreach ( $polylang_languages as $pll_lang ) {
					$linguator_lang = $this->model->languages->get( $pll_lang->slug );
					if ( $linguator_lang ) {
						$cache->clean( $linguator_lang->slug );
					}
				}
			}
		}

		return $results;
	}

	/**
	 * Migrate navigation menu language switcher items from Polylang to Linguator
	 *
	 * @return array Migration result.
	 */
	/**
	 * Migrate navigation menu language switcher items from Polylang to Linguator
	 *
	 * @param bool $dry_run If true, do not perform DB updates; return planned changes.
	 * @return array Migration result.
	 */
	public function linguator_migrate_menu_switchers( $dry_run = false ) {
		global $wpdb;
		
		$results = array(
			'success' => true,
			'menu_items_migrated' => 0,
			'planned' => array(),
			'errors' => array(),
		);

		// Find nav menu items that either use the Polylang switcher URL
		// or that have Polylang menu-item meta. Some installs store the switcher
		// in `_pll_menu_item` without using the `_menu_item_url = '#pll_switcher'` marker,
		// so check for either condition.
		$menu_items = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT DISTINCT p.ID
				FROM {$wpdb->posts} p
				LEFT JOIN {$wpdb->postmeta} pm_url ON p.ID = pm_url.post_id AND pm_url.meta_key = %s
				LEFT JOIN {$wpdb->postmeta} pm_pll ON p.ID = pm_pll.post_id AND pm_pll.meta_key = %s
				WHERE p.post_type = %s
				AND ( pm_url.meta_value = %s OR pm_pll.meta_id IS NOT NULL )",
				'_menu_item_url',
				'_pll_menu_item',
				'nav_menu_item',
				'#pll_switcher'
			)
		);
		
		if ( empty( $menu_items ) ) {
			return $results;
		}

		foreach ( $menu_items as $item ) {
			$menu_item_id = (int) $item->ID;

			// If dry run, collect planned actions instead of performing them.
			if ( $dry_run ) {
				$results['planned'][] = array(
					'menu_item_id' => $menu_item_id,
					'set_url'      => '#lmat_switcher',
					'migrate_meta' => get_post_meta( $menu_item_id, '_pll_menu_item', true ),
				);
				$results['menu_items_migrated']++;
				continue;
			}

			// Update the URL from #pll_switcher to #lmat_switcher.
			// Try update_post_meta first; if it fails (rare), try add_post_meta as a fallback.
			$update_url = update_post_meta( $menu_item_id, '_menu_item_url', '#lmat_switcher' );
			if ( false === $update_url ) {
				// Try to add the meta if update failed (covers some edge cases)
				add_post_meta( $menu_item_id, '_menu_item_url', '#lmat_switcher', true );
			}

			// Verify the URL was written (update_post_meta can return false if value didn't change)
			$current_url = get_post_meta( $menu_item_id, '_menu_item_url', true );
			if ( $current_url !== '#lmat_switcher' ) {
				$results['errors'][] = sprintf(
					/* translators: %d: Menu item ID */
					__( 'Failed to update URL for menu item ID %d', 'translate-words' ),
					$menu_item_id
				);
				$results['success'] = false;
				continue;
			}

			// Migrate menu item options from _pll_menu_item to _lmat_menu_item
			$pll_options = get_post_meta( $menu_item_id, '_pll_menu_item', true );

			if ( ! empty( $pll_options ) && is_array( $pll_options ) ) {
				// Update meta key from _pll_menu_item to _lmat_menu_item
				update_post_meta( $menu_item_id, '_lmat_menu_item', $pll_options );

				// Verify the meta was written (update_post_meta may return false if unchanged)
				$stored_meta = get_post_meta( $menu_item_id, '_lmat_menu_item', true );
				if ( empty( $stored_meta ) || ! is_array( $stored_meta ) ) {
					$results['errors'][] = sprintf(
						/* translators: %d: Menu item ID */
						__( 'Failed to migrate options for menu item ID %d', 'translate-words' ),
						$menu_item_id
					);
					$results['success'] = false;
					continue;
				}
			} else {
				// If no options exist, create default options for Linguator
				$default_options = array(
					'hide_if_no_translation' => 0,
					'hide_current' => 0,
					'force_home' => 0,
					'show_flags' => 0,
					'show_names' => 1,
					'dropdown' => 0,
				);
				update_post_meta( $menu_item_id, '_lmat_menu_item', $default_options );

				// verify default meta saved
				$stored_default = get_post_meta( $menu_item_id, '_lmat_menu_item', true );
				if ( empty( $stored_default ) || ! is_array( $stored_default ) ) {
					$results['errors'][] = sprintf(
						/* translators: %d: Menu item ID */
						__( 'Failed to set default options for menu item ID %d', 'translate-words' ),
						$menu_item_id
					);
					$results['success'] = false;
					continue;
				}
			}

			// Update menu item title to Linguator's default if needed
			$menu_item_title = get_post_meta( $menu_item_id, '_menu_item_title', true );
			if ( empty( $menu_item_title ) ) {
				update_post_meta( $menu_item_id, '_menu_item_title', __( 'Languages', 'translate-words' ) );

				// verify title set
				$stored_title = get_post_meta( $menu_item_id, '_menu_item_title', true );
				if ( empty( $stored_title ) ) {
					$results['errors'][] = sprintf(
						/* translators: %d: Menu item ID */
						__( 'Failed to set menu item title for menu item ID %d', 'translate-words' ),
						$menu_item_id
					);
					$results['success'] = false;
					continue;
				}
			}

			$results['menu_items_migrated']++;
		}

		return $results;
	}

	/**
	 * Perform complete migration from Polylang to Linguator
	 *
	 * @param bool $migrate_languages Whether to migrate languages.
	 * @param bool $migrate_translations Whether to migrate translation links.
	 * @param bool $migrate_settings Whether to migrate settings.
	 * @param bool $migrate_strings Whether to migrate static strings.
	 * @return array Complete migration result.
	 */
	public function migrate_all( $migrate_languages = true, $migrate_translations = true, $migrate_settings = true, $migrate_strings = true ) {
		$results = array(
			'success' => true,
			'languages' => array(),
			'language_assignments' => array(),
			'translations' => array(),
			'settings' => array(),
			'strings' => array(),
			'menu_switchers' => array(),
			'errors' => array(),
		);

		$term_count_update_languages=false;
		
		if ( $migrate_languages ) {
			$this->set_linguator_taxonomy_id();

			$lang_results = $this->migrate_languages();
			$results['languages'] = $lang_results;
			if ( ! $lang_results['success'] ) {
				$results['success'] = false;
			}
			$results['errors'] = array_merge( $results['errors'], $lang_results['errors'] );
		}

		if($results['success'] && $migrate_languages) {
			$this->set_linguator_taxonomy_id();
		}

		// Always migrate language assignments after languages are migrated
		// This ensures posts/pages/terms have their correct language assigned
		if ( $migrate_languages && $results['success'] ) {
			$assignments_results = $this->linguator_migrate_language_assignments($term_count_update_languages);
			$results['language_assignments'] = $assignments_results;
			if ( ! $assignments_results['success'] ) {
				$results['success'] = false;
			}
			$results['errors'] = array_merge( $results['errors'], $assignments_results['errors'] );
		}

		if ( $migrate_settings && $results['success'] ) {
			$settings_results = $this->migrate_settings();
			$results['settings'] = $settings_results;
			if ( ! $settings_results['success'] ) {
				$results['success'] = false;
			}
			$results['errors'] = array_merge( $results['errors'], $settings_results['errors'] );
		}

		if ( $migrate_strings && $results['success'] ) {
			$strings_results = $this->migrate_strings();
			$results['strings'] = $strings_results;
			if ( ! $strings_results['success'] ) {
				$results['success'] = false;
			}
			$results['errors'] = array_merge( $results['errors'], $strings_results['errors'] );
		}

		// Migrate menu switchers after translations are migrated
		// Menu items are independent so attempt migration regardless of previous step results.
		$menu_switchers_results = $this->linguator_migrate_menu_switchers();
		$results['menu_switchers'] = $menu_switchers_results;
		if ( ! $menu_switchers_results['success'] ) {		
			$results['success'] = false;
		}
		$results['errors'] = array_merge( $results['errors'], $menu_switchers_results['errors'] );

		if($term_count_update_languages && is_array($term_count_update_languages) && count($term_count_update_languages) > 0) {
			$this->linguator_update_term_counts($results, $term_count_update_languages);
		}

		// Clear caches after migration
		if ( $results['success'] ) {
			$this->model->languages->clean_cache();
			delete_option( 'rewrite_rules' );
		}
 
		return $results;
	}
}

// phpcs:enable