<?php
/**
 * @package Polylang-Pro
 */

use WP_Syntex\Polylang\Options\Options;

/**
 * Smart copy of post content
 *
 * @since 2.6
 */
class PLL_Sync_Content {
	/**
	 * Stores the plugin options.
	 *
	 * @var Options
	 */
	protected $options;

	/**
	 * @var PLL_Model
	 */
	protected $model;

	/**
	 * Instance of a child class of PLL_Links_Model.
	 *
	 * @var PLL_Links_Model
	 */
	protected $links_model;

	/**
	 * Shortcodes translator.
	 *
	 * @var PLL_Sync_Shortcodes
	 */
	protected $shortcodes;

	/**
	 * HTML translator.
	 *
	 * @var PLL_Sync_HTML
	 */
	protected $html;

	/**
	 * Constructor
	 *
	 * @since 1.9
	 *
	 * @param PLL_Frontend|PLL_Admin|PLL_Settings|PLL_REST_Request $polylang Polylang object.
	 */
	public function __construct( &$polylang ) {
		$this->options    = &$polylang->model->options;
		$this->model      = &$polylang->model;
		$sync_ids         = new PLL_Sync_Ids( $this->model );
		$this->shortcodes = new PLL_Sync_Shortcodes( $sync_ids );
		$this->html       = new PLL_Sync_HTML( $sync_ids );
	}

	/**
	 * Copy the content from one post to the other
	 *
	 * @since 1.9
	 *
	 * @param WP_Post             $from_post       The post to copy from.
	 * @param WP_Post             $target_post     The post to copy to.
	 * @param PLL_Language|string $target_language The language of the post to copy to.
	 * @return WP_Post
	 */
	public function copy_content( $from_post, $target_post, $target_language ) {
		$from_language   = $this->model->post->get_language( $from_post->ID );
		$target_language = $this->model->get_language( $target_language );

		if ( ! $from_language || ! $target_language ) {
			return $target_post;
		}

		$target_post->post_title = $from_post->post_title;
		$target_post->post_name  = wp_unique_post_slug(
			$from_post->post_name,
			$target_post->ID,
			$target_post->post_status,
			$target_post->post_type,
			$target_post->post_parent
		);
		$target_post->post_excerpt = $this->translate(
			$from_post->post_excerpt,
			$target_language,
			$target_post,
			$from_post
		);
		$target_post->post_content = $this->translate(
			$from_post->post_content,
			$target_language,
			$target_post,
			$from_post
		);

		return $target_post;
	}

	/**
	 * Translates shortcodes, HTML and blocks in text.
	 *
	 * @since 1.9
	 * @since 3.3 Requires $target_post, $from_language and $target_language parameters.
	 * @since 3.7 Deprecate `$from_language` argument and move `$target_language` to third position.
	 *
	 * @param string       $content         Text to translate.
	 * @param ?WP_Post     $target_post     The post object to translate to, pass `null` if the content is not post related.
	 * @param PLL_Language $target_language The language to translate to.
	 * @param PLL_Language $deprecated      Deprecated target language object for backward compatibility.
	 * @return string Translated text
	 */
	public function translate_content( $content, ?WP_Post $target_post, PLL_Language $target_language, ?PLL_Language $deprecated = null ) {
		if ( $deprecated instanceof PLL_Language ) {
			$target_language = $deprecated;

			_deprecated_argument(
				__METHOD__,
				'3.7',
				'You must use only 3 arguments, `$target_language` as third.'
			);
		}

		return $this->translate( $content, $target_language, $target_post );
	}

	/**
	 * Duplicates the feature image if the translation does not exist yet.
	 *
	 * @since 2.3
	 *
	 * @param int    $id   Thumbnail ID.
	 * @param string $key  Meta key.
	 * @param string $lang Language code.
	 * @return int
	 */
	public function duplicate_thumbnail( $id, $key, $lang ) {
		if ( '_thumbnail_id' === $key && ! $tr_id = $this->model->post->get( $id, $lang ) ) {
			$tr_id = $this->model->post->create_media_translation( $id, $lang );
		}
		return empty( $tr_id ) ? $id : $tr_id;
	}

	/**
	 * Duplicates a term if the translation does not exist yet.
	 *
	 * @since 2.3
	 *
	 * @param int    $tr_term_id Translated term id.
	 * @param int    $term_id    Source term id.
	 * @param string $lang       Language slug.
	 * @return int The translated term id. O on failure.
	 */
	public function duplicate_term( $tr_term_id, $term_id, $lang ) {
		if ( empty( $tr_term_id ) ) {
			$term = get_term( $term_id );

			if ( $term instanceof WP_Term ) {
				$language = $this->model->term->get_language( $term->term_id );

				if ( $language && $language->slug !== $lang ) { // Create a new term translation only if the source term has a language.
					$tr_parent = empty( $term->parent ) ? 0 : (int) $this->model->term->get_translation( $term->parent, $lang );

					// Duplicate the parent if the parent translation doesn't exist yet.
					if ( empty( $tr_parent ) && ! empty( $term->parent ) ) {
						$tr_parent = $this->duplicate_term( 0, $term->parent, $lang );
					}

					$args = array(
						'description' => wp_slash( $term->description ),
						'parent'      => $tr_parent,
					);

					if ( $this->options['force_lang'] ) {
						// Share slugs
						$args['slug'] = $term->slug . '___' . $lang;
					} else {
						// Language set from the content: assign a different slug
						// otherwise we would change the current term language instead of creating a new term
						$args['slug'] = sanitize_title( $term->name ) . '-' . $lang;
					}

					$t = wp_insert_term( wp_slash( $term->name ), $term->taxonomy, $args );

					$tr_term_id = 0;

					if ( is_array( $t ) ) {
						$tr_term_id = $t['term_id'];
						$this->model->term->set_language( $tr_term_id, $lang );
						$translations = $this->model->term->get_translations( $term->term_id );
						$translations[ $lang ] = $tr_term_id;
						$this->model->term->save_translations( $term->term_id, $translations );

						/**
						 * Fires after a term translation is automatically created when duplicating a post.
						 *
						 * @since 2.3.8
						 *
						 * @param int    $from Term ID of the source term.
						 * @param int    $to   Term ID of the new term translation.
						 * @param string $lang Language code of the new translation.
						 */
						do_action( 'pll_duplicate_term', $term->term_id, $tr_term_id, $lang );
					}
				}
			}
		}
		return $tr_term_id;
	}

	/**
	 * Translates a piece of content, using source post context if available.
	 *
	 * @since 3.7
	 *
	 * @param string       $content         Content to translate.
	 * @param PLL_Language $target_language The language to translate to.
	 * @param WP_Post|null $target_post     The target post object, pass `null` if the content is not post related. Default to `null`.
	 * @param WP_Post|null $source_post     The source post object, pass `null` if the content doesn't need this context. Default to `null`.
	 * @return string Translated content.
	 */
	private function translate( $content, PLL_Language $target_language, ?WP_Post $target_post = null, ?WP_Post $source_post = null ) {
		if ( has_blocks( $content ) ) {
			$content = ( new PLL_Sync_Blocks( $this->shortcodes, $this->html, $source_post ) )->translate( $content, $target_language, $target_post );
		} else {
			$content = $this->html->translate(
				$this->shortcodes->translate( $content, $target_language, $target_post ),
				$target_language,
				$target_post
			);
		}

		return $content;
	}
}
