<?php

use WPML\FP\Fns;
use WPML\FP\Lst;
use WPML\FP\Maybe;
use WPML\FP\Obj;
use WPML\FP\Relation;
use WPML\LIB\WP\Hooks;
use function WPML\Container\make;
use function WPML\FP\pipe;

class WPML_Menu_Item_Sync extends WPML_Menu_Sync_Functionality {

	/** @var array $labels_to_add */
	private $labels_to_add = array();
	/** @var array $urls_to_add */
	private $urls_to_add = array();

	/**
	 * @var string
	 */
	const MENU_ITEM_POST_TYPE = 'post_nav_menu_item';

	/**
	 * @return int the number of removed broken page items
	 */
	function cleanup_broken_page_items() {

		return $this->wpdb->query(
			"
			DELETE o FROM {$this->wpdb->term_relationships} o
			JOIN {$this->wpdb->postmeta} pm
				ON pm.post_id = o.object_id
			JOIN {$this->wpdb->posts} p
				ON p.ID = pm.post_id
			JOIN {$this->wpdb->postmeta} pm_type
				ON pm_type.post_id = pm.post_id
			WHERE p.post_type = 'nav_menu_item'
				AND pm.meta_key = '_menu_item_object_id'
				AND pm_type.meta_key = '_menu_item_type'
				AND pm_type.meta_value = 'post_type'
				AND pm.meta_value = 0"
		);
	}

	function sync_deleted_menus( $deleted_data ) {
		foreach ( $deleted_data as $languages ) {
			foreach ( $languages as $items ) {
				foreach ( $items as $item_id => $name ) {
					wp_delete_post( $item_id, true );
					$delete_trid = $this->post_translations->get_element_trid( $item_id );
					if ( $delete_trid ) {
						$this->sitepress->delete_element_translation( $delete_trid, 'post_nav_menu_item' );
					}
				}
			}
		}
	}

	function sync_menu_options( $options_data ) {
		foreach ( $options_data as $menu_id => $translations ) {
			foreach ( $translations as $language => $option ) {
				$translated_menu_id = $this->term_translations->term_id_in( $menu_id, $language );
				if ( isset( $option['auto_add'] ) ) {
					$nav_menu_option             = (array) get_option( 'nav_menu_options' );
					$nav_menu_option['auto_add'] = isset( $nav_menu_option['auto_add'] ) ? $nav_menu_option['auto_add'] : array();
					if ( $option['auto_add'] && ! in_array( $translated_menu_id, $nav_menu_option['auto_add'] ) ) {
						$nav_menu_option['auto_add'][] = $translated_menu_id;
					} elseif ( ! $option['auto_add'] && false !== ( $key = array_search(
						$translated_menu_id,
						$nav_menu_option['auto_add']
					) )
					) {
						unset( $nav_menu_option['auto_add'][ $key ] );
					}

					/**
					 * We need to disable Sitepress::get_term_adjust_id hook to avoid overriding menu_ids
					 * present in $nav_menu_option['auto_add'] by their original menu_ids.
					 */
					$filterUnExistingMenuIds = function () use ( $nav_menu_option ) {
						return array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( [ 'fields' => 'ids' ] ) );
					};

					$disableAdjustTermIds        = Fns::always( true );
					$nav_menu_option['auto_add'] = Hooks::callWithFilter( $filterUnExistingMenuIds, 'wpml_disable_term_adjust_id', $disableAdjustTermIds );

					update_option( 'nav_menu_options', array_filter( $nav_menu_option ) );
					wp_defer_term_counting( false );
					do_action( 'wp_update_nav_menu', $translated_menu_id );
				}
			}
		}
	}

	public function sync_menu_order( array $menus ) {
		global $wpdb;

		foreach ( $menus as $menu_id => $menu ) {
			$menu_index_by_lang = array();
			foreach ( $menu['items'] as $item_id => $item ) {
				$valid_translations = array_filter(
					$item['translations'],
					function ( $item ) {
						return $item && $item['ID'];
					}
				);

				foreach ( $valid_translations as $language => $item_translation ) {
					$new_menu_order                  = empty( $menu_index_by_lang[ $language ] ) ? 1 : $menu_index_by_lang[ $language ] + 1;
					$menu_index_by_lang[ $language ] = $new_menu_order;
					if ( $new_menu_order != $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] ) {
						$menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] = $new_menu_order;
						$wpdb->update(
							$wpdb->posts,
							[ 'menu_order' => $new_menu_order ],
							[ 'ID' => $item_translation['ID'] ]
						);
					}
				}
			}
		}

		return $menus;
	}

	function sync_added_items( array $added_data, array $menus ) {
		global $wpdb;

		foreach ( $added_data as $menu_id => $items ) {
			foreach ( $items as $language => $translations ) {
				foreach ( $translations as $item_id => $name ) {
					$trid                = $this->get_or_set_trid( $item_id, $this->sitepress->get_default_language() );
					$translated_object   = $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ];
					$menu_name           = $this->get_menu_name( $menu_id );
					$object_type         = $translated_object['object_type'];
					$object_title        = $translated_object['title'];
					$object_url          = $translated_object['url'];
					$icl_st_label_exists = false;
					$icl_st_url_exists   = false;
					if ( $object_type === 'custom' && function_exists( 'icl_t' ) ) {
						$item                              = new stdClass();
						$item->url                         = $object_url;
						$item->ID                          = $item_id;
						$item->post_title                  = $object_title;
						list( $object_title, $object_url ) = $this->icl_t_menu_item(
							$menu_name,
							$item,
							$language,
							$icl_st_label_exists,
							$icl_st_url_exists
						);
					}

					$menu_data = array(
						'menu-item-db-id'       => 0,
						'menu-item-object-id'   => $translated_object['object_id'],
						'menu-item-object'      => $translated_object['object'],
						'menu-item-parent-id'   => 0,
						'menu-item-position'    => 0,
						'menu-item-type'        => $object_type,
						'menu-item-title'       => $object_title,
						'menu-item-url'         => $object_url,
						'menu-item-description' => '',
						'menu-item-attr-title'  => $translated_object['attr-title'],
						'menu-item-target'      => $translated_object['target'],
						'menu-item-classes'     => ( $translated_object['classes'] ? implode(
							' ',
							$translated_object['classes']
						) : '' ),
						'menu-item-xfn'         => $translated_object['xfn'],
						'menu-item-status'      => 'publish',
					);

					$translated_menu_id = $menus[ $menu_id ]['translations'][ $language ]['id'];

					remove_filter( 'get_term', array( $this->sitepress, 'get_term_adjust_id' ), 1 );
					$translated_item_id = wp_update_nav_menu_item( $translated_menu_id, 0, $menu_data );

					// set language explicitly since the 'wp_update_nav_menu_item' is still TBD
					$this->sitepress->set_element_language_details(
						$translated_item_id,
						'post_nav_menu_item',
						$trid,
						$language
					);

					$menu_tax_id_prepared = $wpdb->prepare(
						"SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu' LIMIT 1",
						$translated_menu_id
					);
					$menu_tax_id          = $wpdb->get_var( $menu_tax_id_prepared );

					if ( $translated_item_id && $menu_tax_id ) {
						$rel_prepared = $wpdb->prepare(
							"SELECT object_id FROM {$wpdb->term_relationships} WHERE object_id=%d AND term_taxonomy_id=%d LIMIT 1",
							$translated_item_id,
							$menu_tax_id
						);
						$rel          = $wpdb->get_var( $rel_prepared );
						if ( ! $rel ) {
							$wpdb->insert(
								$wpdb->term_relationships,
								array(
									'object_id'        => $translated_item_id,
									'term_taxonomy_id' => $menu_tax_id,
								)
							);
						}
					}

					$menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['ID'] = $translated_item_id;
				}
			}
		}
		$this->fix_hierarchy_added_items( $added_data );

		return $menus;
	}

	function sync_moved_items( array $moved_data, array $menus ) {
		global $wpdb;

		foreach ( $moved_data as $menu_id => $items ) {
			foreach ( $items as $language => $changes ) {
				foreach ( $changes as $item_id => $details ) {
					$trid               = $this->get_or_set_trid( $item_id, $this->sitepress->get_default_language() );
					$translated_item_id = $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['ID'];

					$new_menu_order = key( $details );
					$menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] = $new_menu_order;

					$wpdb->update(
						$wpdb->posts,
						array( 'menu_order' => $new_menu_order ),
						array( 'ID' => $translated_item_id )
					);

					if ( $this->post_translations->get_element_trid( $translated_item_id ) != $trid ) {
						$this->sitepress->set_element_language_details(
							$translated_item_id,
							'post_nav_menu_item',
							$trid,
							$language
						);
					}

					$translated_menu_id = $menus[ $menu_id ]['translations'][ $language ]['id'];
					$this->assign_orphan_item_to_menu( $translated_item_id, $translated_menu_id, $language );
				}
			}
		}
		$this->fix_hierarchy_moved_items( $moved_data );

		return $menus;
	}

	/**
	 * @param int $item_id
	 * @param int $menu_id
	 */
	private function assign_orphan_item_to_menu( $item_id, $menu_id, $language ) {
		$this->sitepress->switch_lang( $language );
		if ( ! wp_get_object_terms( $item_id, 'nav_menu' ) ) {
			wp_set_object_terms( $item_id, array( $menu_id ), 'nav_menu' );
		}
		$this->sitepress->switch_lang();
	}

	function sync_caption( $label_change_data ) {
		foreach ( $label_change_data as $languages ) {
			foreach ( $languages as $language => $items ) {
				foreach ( $items as $item_id => $name ) {
					$trid = $this->sitepress->get_element_trid( $item_id, 'post_nav_menu_item' );
					if ( $trid ) {
						$item_translations = $this->sitepress->get_element_translations(
							$trid,
							'post_nav_menu_item',
							true
						);
						if ( isset( $item_translations[ $language ] ) ) {
							$translated_item = get_post( $item_translations[ $language ]->element_id );
							if ( $translated_item && $translated_item->post_title != $name ) {
								$translated_item->post_title = $name;
								/** @phpstan-ignore-next-line WP doc issue. */
								wp_update_post( $translated_item );
							}
						}
					}
				}
			}
		}
	}

	function sync_urls( $url_change_data ) {
		foreach ( $url_change_data as $languages ) {
			foreach ( $languages as $language => $items ) {
				foreach ( $items as $item_id => $url ) {
					$trid = $this->sitepress->get_element_trid( $item_id, 'post_nav_menu_item' );
					if ( $trid ) {
						$item_translations = $this->sitepress->get_element_translations(
							$trid,
							'post_nav_menu_item',
							true
						);
						if ( isset( $item_translations[ $language ] ) ) {
							$translated_item_id = $item_translations[ $language ]->element_id;
							if ( $url ) {
								update_post_meta( $translated_item_id, '_menu_item_url', $url );
							}
						}
					}
				}
			}
		}
	}

	function sync_missing_captions( $label_missing ) {
		foreach ( $label_missing as $menu_id => $languages ) {
			foreach ( $languages as $items ) {
				foreach ( $items as $item_id => $name ) {
					if ( ! in_array( $menu_id . '-' . $item_id, $this->labels_to_add ) ) {
						$item = get_post( $item_id );
						icl_register_string(
							$this->get_menu_name( $menu_id ) . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX,
							WPML_Menu_Sync_Functionality::STRING_NAME_LABEL_PREFIX . $item_id,
							$item->post_title
						);
						$this->labels_to_add[] = $menu_id . '-' . $item_id;
					}
				}
			}
		}
	}

	function sync_urls_to_add( $url_missing_data ) {
		foreach ( $url_missing_data as $menu_id => $languages ) {
			foreach ( $languages as $items ) {
				foreach ( $items as $item_id => $url ) {
					if ( ! in_array( $menu_id . '-' . $item_id, $this->urls_to_add ) ) {
						icl_register_string(
							$this->get_menu_name( $menu_id ) . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX,
							WPML_Menu_Sync_Functionality::STRING_NAME_URL_PREFIX . $item_id,
							$url
						);
						$this->urls_to_add[] = $menu_id . '-' . $item_id;
					}
				}
			}
		}
	}

	/**
	 * @param array $menus Registered menus.
	 */
	public function sync_custom_fields( $menus ) {

		$syncMenuItem = function ( $menuItemId ) {
			$this->sync_custom_fields_set_to_copy( $menuItemId );
			$this->sync_custom_fields_set_to_copy_once( $menuItemId );
		};

		$syncMenu = pipe( Obj::prop( 'items' ), Obj::keys(), Fns::each( $syncMenuItem ) );

		Fns::each( $syncMenu, $menus );
	}

	/**
	 * @param int $menuItemId
	 */
	private function sync_custom_fields_set_to_copy( $menuItemId ) {
		$copy = new WPML_Sync_Custom_Fields(
			new WPML_Translation_Element_Factory( $this->sitepress ),
			$this->sitepress->get_custom_fields_translation_settings( WPML_COPY_CUSTOM_FIELD )
		);
		$copy->sync_all_custom_fields( $menuItemId );
	}

	/**
	 * @param int $menuItemId
	 */
	private function sync_custom_fields_set_to_copy_once( $menuItemId ) {
		$getItemTranslations = function( $menuItemId ) {
			return $this->sitepress->get_element_translations(
				$this->sitepress->get_element_trid( $menuItemId, self::MENU_ITEM_POST_TYPE ),
				self::MENU_ITEM_POST_TYPE
			);
		};

		Maybe::of( $menuItemId )
			->map( $getItemTranslations )
			->map( Lst::pluck( 'element_id' ) )
			->map( Fns::reject( Relation::equals( $menuItemId ) ) )
			->map( Fns::map( [ make( WPML_Copy_Once_Custom_Field::class ), 'copy' ] ) );
	}

	private function fix_hierarchy_added_items( $added_data ) {
		foreach ( $added_data as $menu_id => $items ) {
			foreach ( $items as $language => $translations ) {
				foreach ( $translations as $item_id => $name ) {
					$this->fix_hierarchy_for_item( $item_id, $language );
				}
			}
		}
	}

	private function fix_hierarchy_moved_items( $moved_data ) {
		foreach ( $moved_data as $menu_id => $items ) {
			foreach ( $items as $language => $changes ) {
				foreach ( $changes as $item_id => $details ) {
					$this->fix_hierarchy_for_item( $item_id, $language );
				}
			}
		}
	}

	private function fix_hierarchy_for_item( $item_id, $language ) {
		$parent_item                    = get_post_meta( $item_id, '_menu_item_menu_item_parent', true );
		$translated_item_id             = $this->post_translations->element_id_in(
			$item_id,
			$language
		);
		$translated_parent_menu_item_id = $this->post_translations->element_id_in(
			$parent_item,
			$language
		);
		$translated_parent_menu_item_id = $translated_parent_menu_item_id == $translated_item_id
			? false : $translated_parent_menu_item_id;

		update_post_meta(
			$translated_item_id,
			'_menu_item_menu_item_parent',
			$translated_parent_menu_item_id
		);

	}

	private function get_or_set_trid( $item_id, $language_code ) {
		$trid = $this->post_translations->get_element_trid( $item_id );
		if ( ! $trid ) {
			$this->sitepress->set_element_language_details(
				$item_id,
				'post_nav_menu_item',
				false,
				$language_code
			);
			$trid = $this->post_translations->get_element_trid( $item_id );
		}

		return $trid;
	}
}
