<?php
if ( ! defined( 'ABSPATH' ) ) exit();
/**
 * Translations groups for posts, pages and custom post types
 *
 * @since 0.2
 */
class Lingotek_Group_Post extends Lingotek_Group {

	// Preference constant used for downloaded translations.
	const SAME_AS_SOURCE = 'SAME_AS_SOURCE';
	/**
	 * set a translation term for an object
	 *
	 * @since 0.2
	 *
	 * @param int    $object_id post id.
	 * @param object $language
	 * @param string $document_id translation term name (Lingotek document id).
	 */
	public static function create( $object_id, $language, $document_id ) {
		$data = array(
			'lingotek'      => array(
				'type'         => get_post_type( $object_id ),
				'source'       => $object_id,
				'status'       => 'importing',
				'translations' => array(),
			),
			// For Polylang.
			$language->slug => $object_id,
		);
		self::save_hash_on_upload( $object_id );
		self::_create( $object_id, $document_id, $data, 'post_translations' );
	}

	/**
	 * returns content type fields
	 *
	 * @since 0.2
	 *
	 * @param string $post_type
	 * @return array
	 */
	public static function get_content_type_fields( $post_type, $post_ID = null ) {
		$cache_key = 'content_type_fields_' . $post_type;
		$arr       = wp_cache_get( $cache_key, 'lingotek' );
		if ( ! $arr ) {
			$arr = 'attachment' == $post_type ?
			array(
				'post_title'   => __( 'Title', 'lingotek-translation' ),
				'post_excerpt' => __( 'Caption', 'lingotek-translation' ),
				'metas'        => array( '_wp_attachment_image_alt' => __( 'Alternative Text', 'lingotek-translation' ) ),
				'post_content' => __( 'Description', 'lingotek-translation' ),
			) : array(
				'post_title'   => __( 'Title', 'lingotek-translation' ),
				'post_name'    => __( 'Slug', 'lingotek-translation' ),
				'post_content' => __( 'Content', 'lingotek-translation' ),
				'post_excerpt' => __( 'Excerpt', 'lingotek-translation' ),
			);

			// if the user hasn't visited the custom fields tab, and hasn't saved actions for custom
			// fields, and uploaded a post, check the wpml file for settings
			if ( $post_ID ) {
				self::get_updated_meta_values( $post_ID );
			}
			// add the custom fields from the lingotek_custom_fields option
			$custom_fields = get_option( 'lingotek_custom_fields', array() );

			if ( is_array( $custom_fields ) && ! empty( $custom_fields ) ) {
				foreach ( $custom_fields as $cf => $setting ) {
					if ( 'translate' == $setting ) {
						if ( Lingotek_Metadata_Elementor::$elementor_content === $cf ) {
							$arr['metas'][ Lingotek_Metadata_Elementor::$elementor_data ] = Lingotek_Metadata_Elementor::$elementor_data;
						} else {
							$arr['metas'][ $cf ] = $cf;
						}
					}
				}
			}
			wp_cache_set( $cache_key, $arr, 'lingotek', 300 );
		}//end if
		// allow plugins to modify the fields to translate
		return apply_filters( 'lingotek_post_content_type_fields', $arr, $post_type );
	}

	/**
	 * returns custom fields from the wpml-config.xml file
	 *
	 * @since 0.2
	 * @return array
	 */
	public static function get_custom_fields_from_wpml() {
		$wpml_config = PLL_WPML_Config::instance();
		$arr         = array();

		if ( isset( $wpml_config->tags['custom-fields'] ) ) {
			foreach ( $wpml_config->tags['custom-fields'] as $context ) {
				foreach ( $context['custom-field'] as $cf ) {
					$arr[ $cf['value'] ] = $cf['attributes']['action'];
				}
			}
		}

		// allow plugins to modify the fields to translate
		return apply_filters( 'lingotek_post_content_type_fields_from_wpml', $arr );
	}

	/**
	 * returns meta (custom) fields from the wp-postmeta database table
	 *
	 * @since 0.2
	 *
	 * @return array
	 */
	public static function get_custom_fields_from_wp_postmeta( $post_ID = null ) {
		$all_acf_fields        = array();
		$elementor_field_group = array();
		$custom_fields         = get_option( 'lingotek_custom_fields', array() );
		$meta_black_list       = array( '_encloseme', '_edit_last', '_edit_lock', '_wp_trash_meta_status', '_wp_trash_meta_time' );
		$arr                   = array();
		$keys                  = array();

		if ( $post_ID ) {
			$p       = get_post( $post_ID );
			$posts[] = $p;
		} else {
			$posts = get_posts(
				array(
					'posts_per_page' => -1,
					'post_type'      => 'post',
				)
			);
			$pages = get_posts(
				array(
					'posts_per_page' => -1,
					'post_type'      => 'page',
				)
			);

			$posts = array_merge( $posts, $pages );
		}
		//Only perform if ACF is installed and these functions are active
		if ( function_exists( 'acf_get_field_groups' ) && function_exists( 'acf_get_fields' ) ) {
			$groups       = acf_get_field_groups();
			$field_holder = array();
			foreach ( $groups as $group ) {
				if ( isset( $group['key'] ) ) {
					$field_holder = acf_get_fields( $group['key'] );
					foreach ( $field_holder as $field ) {
						$field_data               = array();
						$field_data['meta_key']   = $field['name'];
						$field_data['meta_value'] = $field['key'];
						$all_acf_fields[]         = $field_data;
					}
				}
			}
		}

		foreach ( $posts as $post ) {
			$metadata = has_meta( $post->ID );
			foreach ( $metadata as $key => $meta ) {
				if ( in_array( $meta['meta_key'], $meta_black_list ) || in_array( $meta['meta_key'], $keys ) ) {
					unset( $metadata[ $key ] );
				}
				if ( self::is_elementor_meta( $meta['meta_key'] ) ) {
					$elementor_field_group[ $meta['meta_key'] ] = $meta['meta_key'];
					// Remove elementor field
					unset( $metadata [ $key ] );
				}
				$keys[ $meta['meta_key'] ] = $meta['meta_key'];
			}
			$arr = array_merge( $arr, $metadata );
		}
		$arr = array_merge( $arr, $all_acf_fields );
		if ( is_plugin_active( 'elementor/elementor.php' ) && ! empty( $elementor_field_group ) ) {
			// Set Lingotek elementor field
			self::group_elementor_fields( $arr, $elementor_field_group );
		}
		// allow plugins to modify the fields to translate
		return apply_filters( 'lingotek_post_custom_fields', $arr );
	}

	/**
	 * tests whether a meta belongs to the Advanced Custom Fields plugin
	 *
	 * @since 0.2
	 *
	 * @return bool
	 */
	protected static function is_advanced_custom_fields_meta( $key, $value ) {
		return ( ( substr( $key, 0, strlen( '_' ) ) === '_' ) && ( substr( $value, 0, strlen( 'field_' ) ) === 'field_' ) ) ? true : false;
	}

	/**
	 * tests whether a meta belongs to the Elementor plugin
	 *
	 * @since 1.5.1
	 *
	 * @return bool
	 */
	protected static function is_elementor_meta( $key ) {
		return ( is_string( $key ) && 0 === strpos( $key, '_elementor' ) ) ? true : false;
	}

	/**
	 * Checks if certain elementor fields exist, then groups all elementor metadata keys under one meta key
	 *
	 * @since 1.5.3
	 *
	 * @param array $metas The array of meta keys that users can translate.
	 * @param array $elementor_field_group The array of elementor metadata found in the posts.
	 */
	public static function group_elementor_fields( &$metas, $elementor_field_group ) {
		// Create Lingotek elementor meta field if posts exist with elementor content
		if ( in_array( Lingotek_Metadata_Elementor::$elementor_data, $elementor_field_group ) && in_array( Lingotek_Metadata_Elementor::$elementor_edit_mode, $elementor_field_group ) ) {
			$metas[] = array(
				'meta_key'   => Lingotek_Metadata_Elementor::$elementor_content,
				'meta_value' => $elementor_field_group,
			);
		}
	}

	/**
	 * updates meta (custom) fields values in the lingotek_custom_fields option
	 *
	 * @since 0.2
	 */
	public static function get_updated_meta_values( $post_ID = null ) {
		$custom_fields_from_wpml     = self::get_custom_fields_from_wpml();
		$custom_fields_from_postmeta = self::get_custom_fields_from_wp_postmeta( $post_ID );
		$custom_fields_from_lingotek = get_option( 'lingotek_custom_fields', array() );
		if ( ! is_array( $custom_fields_from_lingotek ) ) {
			$custom_fields_from_lingotek = array();
		}
		$default_custom_fields = get_option( 'lingotek_default_custom_fields' ) ? get_option( 'lingotek_default_custom_fields' ) : 'ignore';
		$custom_fields         = array();
		$items                 = array();

		foreach ( $custom_fields_from_postmeta as $cf ) {
			// hide-copy means hide from user, and always copy to translations (Advanced Custom Fields plugin)
			if ( self::is_advanced_custom_fields_meta( $cf['meta_key'], $cf['meta_value'] ) ) {
				$custom_fields[ $cf['meta_key'] ] = 'hide-copy';
				continue;
			}

			// no lingotek setting
			if ( ! array_key_exists( $cf['meta_key'], $custom_fields_from_lingotek ) ) {
				// no lingotek setting, but there's a wpml setting
				if ( array_key_exists( $cf['meta_key'], $custom_fields_from_wpml ) ) {
					$custom_fields[ $cf['meta_key'] ] = $custom_fields_from_wpml[ $cf['meta_key'] ];
				}
				// no lingotek setting, no wpml setting, so save default setting of ignore
				else {
					$custom_fields[ $cf['meta_key'] ] = $default_custom_fields;
				}
			}
			// lingotek already has this field setting saved
			else {
				$custom_fields[ $cf['meta_key'] ] = $custom_fields_from_lingotek[ $cf['meta_key'] ];
			}
		}//end foreach

		if ( $post_ID ) {
			// get_option sometimes returns empty string for `lingotek_custom_fields`
			$custom_fields = is_array( $custom_fields_from_lingotek ) ? array_merge( $custom_fields_from_lingotek, $custom_fields ) : $custom_fields;
		}
		update_option( 'lingotek_custom_fields', $custom_fields, false );
	}

	/**
	 * returns cached meta (custom) fields values in the lingotek_custom_fields option
	 *
	 * @since 0.2
	 *
	 * @return array
	 */
	public static function get_cached_meta_values( $search = '' ) {
		$custom_fields_from_lingotek = get_option( 'lingotek_custom_fields', array() );
		$items                       = array();

		if ( is_array( $custom_fields_from_lingotek ) ) {
			foreach ( $custom_fields_from_lingotek as $key => $setting ) {
				if ( 'hide-copy' === $setting ) {
					continue;
				}
				$arr = array(
					'meta_key' => $key,
					'setting'  => $setting,
				);
				if ( empty( $search ) || stripos( $key, $search ) !== false ) {
					$items[] = $arr;
				}
			}
		}
		return $items;
	}

	/**
	 * returns the content to translate
	 *
	 * @since 0.2
	 *
	 * @param object $post
	 * @return string json encoded content to translate
	 */
	public static function get_content( $post ) {
		$arr                = array();
		$fields             = self::get_content_type_fields( $post->post_type, $post->ID );
		$content_types      = get_option( 'lingotek_content_type' );
		$post_custom_fields = get_post_meta( $post->ID );
		$meta_black_list    = array( '_encloseme', '_edit_last', '_edit_lock', '_wp_trash_meta_status', '_wp_trash_meta_time' );
		foreach ( array_keys( $fields ) as $key ) {
			if ( 'metas' == $key ) {
				foreach ( $post_custom_fields as $meta => $value ) {
					// check for advanced custom fields meta expects a string
					// so if array, check first item
					$value = is_array( $value ) ? current( $value ) : $value;
					if ( self::is_advanced_custom_fields_meta( $meta, $value ) || self::is_elementor_meta( $meta ) || in_array( $meta, $meta_black_list ) ) {
						continue;
					}
					// Get all metas in array format
					$meta_value = get_post_meta( $post->ID, $meta, true );
					if ( $meta_value && isset( $fields['metas'][ $meta ] ) ) {
						$arr['metas'][ $meta ] = $meta_value;
					}
				}
				// Check if acf is active and using these functions
				$acf_empty_string = '';
				if ( isset( $fields['metas'][ $acf_empty_string ] ) && function_exists( 'acf_get_field_groups' ) && function_exists( 'acf_get_fields' ) ) {
					$arr['metas'][ $acf_empty_string ] = get_post_meta( $post->ID, $acf_empty_string, true );
				}

				// Check if elementor data exists in post
				if ( isset( $fields['metas'][ Lingotek_Metadata_Elementor::$elementor_data ] ) && 'builder' === get_post_meta( $post->ID, Lingotek_Metadata_Elementor::$elementor_edit_mode, true ) ) {
					$post_meta_json_string = get_post_meta( $post->ID, Lingotek_Metadata_Elementor::$elementor_data, true );

					$post_meta_array = json_decode( $post_meta_json_string, true );

					$post_and_template_meta_array = Lingotek_Metadata_Elementor::get_merged_post_and_template_meta( $post_meta_array );

					if ( !empty($post_and_template_meta_array) ) {
						$merged_json_string = json_encode( $post_and_template_meta_array );
						$arr['metas'][ Lingotek_Metadata_Elementor::$elementor_data . '_original' ]  = $merged_json_string;
					} else {
						$post_meta_json_string = json_encode( $post_meta_array );
						$arr['metas'][ Lingotek_Metadata_Elementor::$elementor_data . '_original' ]  = $post_meta_json_string;
					}
					// We need to keep the original meta string with the translation
					$arr['metas'][ Lingotek_Metadata_Elementor::$elementor_data ] = Lingotek_Metadata_Elementor::get_post_metadata_content( $post_meta_array, $post_and_template_meta_array );

					// Since the elementor data overwrites post_content this prevents duplicating the content sent up for translation.
					$arr['post']['post_content'] = '';
				}
			}//end if
			// send slug for translation only if it has been modified
			elseif ( 'post_name' == $key && empty( $content_types[ $post->post_type ]['fields'][ $key ] ) ) {
				// Default slug created by WordPress.
				$default_slug = sanitize_title( $post->post_title );
				// if ($default_slug != $post->post_name)
				$arr['post'][ $key ] = $post->$key;
			} elseif ( empty( $content_types[ $post->post_type ]['fields'][ $key ] ) ) {
				$arr['post'][ $key ] = $post->$key;
			}
		}//end foreach
		return json_encode( $arr );
	}

	public static function is_valid_auto_upload_post_status( $post_status ) {
		$prefs          = Lingotek_Model::get_prefs();
		$valid_statuses = $prefs['auto_upload_post_statuses'];
		$valid          = isset( $valid_statuses[ $post_status ] ) && $valid_statuses[ $post_status ];
		return $valid;
	}

	/**
	 * requests translations to Lingotek TMS
	 *
	 * @since 0.1
	 */
	public function request_translations() {
		if ( isset( $this->source ) ) {
			$language = PLL()->model->post->get_language( (int) $this->source );
			$this->_request_translations( $language );
		}
	}

	/**
	 * create a translation downloaded from Lingotek TMS
	 *
	 * @since 0.1
	 * @uses Lingotek_Group::safe_translation_status_update() as the status can be automatically set by the TMS callback
	 *
	 * @param string $locale
	 */
	public function create_translation( $locale, $automatic = false, $callback_type = null ) {
		// Removes content sanitization so YouTube videos, links, etc don't get removed when inserting translations
		remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
		remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
		$client            = new Lingotek_API();
		if ( $callback_type === 'target') {
			$interim_download = false;
			// already checked in callback handler
			$ready_to_download = true;
		} else if ($callback_type === 'download_interim_translation') {
			$interim_download = true;
			$ready_to_download = true;
		} else {
			// Check if the locale status is `ready-interim` or `interim`.
			$interim_download = isset($this->translations[$locale]) && ($this->translations[$locale] === 'ready-interim' || $this->translations[$locale] === 'interim') ? true : false;
			// If translation not an interim status, check translation status. 
			$ready_to_download = $interim_download ? true : $client->get_translation_status( $this->document_id, $locale );
		}

		if ( ! $ready_to_download ) {
			return;
		}

		$translation = $client->get_translation( $this->document_id, $locale, $this->source );
		// If the request failed.
		if ( ! $translation ) {
			return;
		}
		// wp_insert_post expects array
		$translation = json_decode( $translation, true );

		self::$creating_translation = true;
		// Need an array by default.
		$prefs = Lingotek_Model::get_prefs();

		$tr_post = $translation['post'];
		if ( isset( $tr_post['post_name'] ) ) {
			$tr_post['post_name'] = sanitize_title( $tr_post['post_name'] );
		}

		// source post
		$post = get_post( $this->source );
		// status
		$tr_post['post_status'] = ( $prefs['download_post_status'] === self::SAME_AS_SOURCE ) ? $post->post_status : $prefs['download_post_status'];
		$language               = $this->pllm->get_language( $locale );
		// update existing translation
		if ( $tr_id = PLL()->model->post->get( $this->source, $language->locale ) ) {
			$tr_post['ID'] = $tr_id;

			// translate metas
			if ( isset( $translation['metas'] ) ) {
				self::copy_translated_metas( $translation['metas'], $tr_id );
			}

			// copy or ignore metas
			self::copy_or_ignore_metas( $post->ID, $tr_id );

			wp_update_post( $tr_post );
			if ( $interim_download ) {
				$this->safe_translation_status_update( $locale, 'interim' );
			} else {
				$this->safe_translation_status_update( $language->locale, 'current' );
			}
		}

		// create new translation
		elseif ( ( $this->translations[ $locale ] == 'ready' || $this->translations[ $locale ] == 'pending' ) || $automatic || $this->translations[ $locale ] == 'ready-interim' ) {
			$content_type_options = get_option( 'lingotek_content_type' );
			if ( ! isset( $content_type_options[ $post->post_type ]['fields']['post_name'] ) ) {
				// Forces the creation of a new default slug if not translated by Lingotek.
				unset( $post->post_name );
			}
			// Copy all untranslated fields from the original post.
			$tr_post = array_merge( (array) $post, $tr_post );
			// Will force the creation of a new post.
			$tr_post['ID'] = null;

			// translate parent
			$tr_post['post_parent'] = ( $post->post_parent && $tr_parent = $this->pllm->post->get_translation( $post->post_parent, $language->locale ) ) ? $tr_parent : 0;

			if ( 'attachment' == $post->post_type ) {
				$tr_id = wp_insert_attachment( $tr_post );
				add_post_meta( $tr_id, '_wp_attachment_metadata', get_post_meta( $this->source, '_wp_attachment_metadata', true ) );
				add_post_meta( $tr_id, '_wp_attached_file', get_post_meta( $this->source, '_wp_attached_file', true ) );
			} else {
				$tr_id = wp_insert_post( $tr_post );
			}

			if ( $tr_id ) {
				$tr_lang = $this->pllm->get_language( $locale );
				PLL()->model->post->set_language( $tr_id, $tr_lang );
				$this->safe_translation_status_update( $locale, 'current', array( $tr_lang->slug => $tr_id ) );
				if ( $interim_download ) {
					$this->safe_translation_status_update( $locale, 'interim' );
				}
				wp_set_object_terms( $tr_id, $this->term_id, 'post_translations' );

				// Copies categories and tags
				$GLOBALS['polylang']->sync->taxonomies->copy( $this->source, $tr_id, $tr_lang->slug );

				// assign terms and metas
				$GLOBALS['polylang']->sync->post_metas->copy( $this->source, $tr_id, $tr_lang->slug );

				// translate metas
				if ( isset( $translation['metas'] ) ) {
					self::copy_translated_metas( $translation['metas'], $tr_id );
				}

				// copy or ignore metas
				self::copy_or_ignore_metas( $post->ID, $tr_id );

				if ( class_exists( 'PLL_Share_Post_Slug', true ) && $content_type_options[ $post->post_type ]['fields']['post_name'] == 1 ) {
					wp_update_post(
						array(
							'ID'        => $tr_id,
							'post_name' => $post->post_name,
						)
					);
				}
			}//end if
		}

		self::$creating_translation = false;

		// Adds content sanitization back in after Lingotek saves the translation
		add_filter( 'content_save_pre', 'wp_filter_post_kses' );
		add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
	}

	/**
	 * copies source meta strings to translations or deletes meta if set to ignore
	 *
	 * @since 1.0.9
	 */

	public static function copy_or_ignore_metas( $post_id, $tr_id ) {
		// copy or ignore metas
		$custom_fields      = get_option( 'lingotek_custom_fields', array() );
		$post_custom_fields = get_post_meta( $post_id );
		foreach ( $post_custom_fields as $key => $source_meta ) {
			// Set to blank string to ignore if no lingotek setting has been set.
			$setting = isset( $custom_fields[ $key ] ) ? $custom_fields[ $key ] : '';
			if ( self::is_elementor_meta( $key ) ) {
				// Use custom lingotek elementor field
				$setting = isset( $custom_fields [ Lingotek_Metadata_Elementor::$elementor_content ] ) ? $custom_fields[ Lingotek_Metadata_Elementor::$elementor_content ] : '';
				// If the setting is translate but key is not elementor data, set to copy
				if ( $setting === 'translate' && $key !== Lingotek_Metadata_Elementor::$elementor_data ) {
					$setting = 'copy';
				}
			}
			if ( 'copy' === $setting || 'hide-copy' === $setting ) {
				$source_meta = current( get_post_meta( $post_id, $key ) );
				update_post_meta( $tr_id, $key, $source_meta );
			} elseif ( 'ignore' === $setting ) {
				delete_post_meta( $tr_id, $key );
			}
		}
	}

	/**
	 * inserts translated meta values into translations
	 *
	 * @since 1.0.9
	 */
	protected static function copy_translated_metas( $translation_metas, $tr_id ) {
		if ( ! empty( $translation_metas ) ) {
			foreach ( $translation_metas as $key => $meta ) {
				if ( $key === Lingotek_Metadata_Elementor::$elementor_data || $key === Lingotek_Metadata_Elementor::$elementor_data . '_original' ) {
					continue;
				}
				update_post_meta( $tr_id, $key, $meta );
			}

			if ( isset( $translation_metas[ Lingotek_Metadata_Elementor::$elementor_data ], $translation_metas[ Lingotek_Metadata_Elementor::$elementor_data . '_original' ] ) ) {
				Lingotek_Metadata_Elementor::set_post_metadata_content( $tr_id, $translation_metas[ Lingotek_Metadata_Elementor::$elementor_data . '_original' ], $translation_metas[ Lingotek_Metadata_Elementor::$elementor_data ] );
			}
		}
	}

	/**
	 * checks if content should be automatically uploaded
	 *
	 * @since 0.2
	 *
	 * @return bool
	 */
	public function is_automatic_upload() {
		 return 'automatic' == Lingotek_Model::get_profile_option( 'upload', get_post_type( $this->source ), $this->get_source_language(), false, $this->source ) && parent::is_automatic_upload();
	}

	/**
	 * get the the language of the source post
	 *
	 * @since 0.2
	 *
	 * @return object
	 */
	public function get_source_language() {
		 return PLL()->model->post->get_language( $this->source );
	}

	/**
	 * creates hash for lingotek_hash term
	 *
	 * @since 1.0.9
	 */
	protected static function save_hash_on_upload( $object_id ) {
		$post        = get_post( $object_id );
		$document_id = 'lingotek_hash_' . $post->ID;
		$new_hash    = md5( self::get_content( $post ) );

		wp_insert_term( $document_id, 'lingotek_hash', array( 'description' => $new_hash ) );
		wp_set_object_terms( $post->ID, $document_id, 'lingotek_hash' );
	}
}
