<?php
/**
 * Handles the WPML integration with the Custom Tables v1 ECP portion of the code.
 *
 * @since   6.0.11
 *
 * @package TEC\Events_Pro\Custom_Tables\V1\Integrations\WPML;
 */

namespace TEC\Events_Pro\Custom_Tables\V1\Integrations\WPML;

use TEC\Common\Contracts\Service_Provider;
use TEC\Events\Custom_Tables\V1\Models\Occurrence;
use TEC\Events_Pro\Custom_Tables\V1\Models\Provisional_Post;
use TEC\Events_Pro\Custom_Tables\V1\Models\Series_Relationship;
use TEC\Events_Pro\Custom_Tables\V1\Series\Autogenerated_Series;
use TEC\Events_Pro\Custom_Tables\V1\Series\Post_Type;
use Tribe__Cache_Listener as Cache_Listener;
use Tribe__Events__Main as TEC;
use WP_Post;

/**
 * Class Provider.
 *
 * @since   6.0.11
 *
 * @package TEC\Events_Pro\Custom_Tables\V1\Integrations\WPML;
 */
class WPML_Integration extends Service_Provider {

	/**
	 * Binds and sets up implementations.
	 *
	 * @since 6.0.11
	 *
	 * @return void
	 */
	public function register() {
		add_filter(
			'tec_events_custom_tables_v1_updated_post',
			[ $this, 'localize_autogenerated_series' ],
			50,
			2
		);

		add_filter( 'wpml_get_ls_translations', [ $this, 'filter_provisional_id_ls_translation' ], 10, 2 );

		add_filter( 'post_type_link', [ $this, 'filter_permalinks_with_provisional_id' ], 15, 2 );

		add_filter( 'wpml_meta_box_post', [ $this, 'filter_post_metabox' ] );

		add_filter( 'tec_events_pro_custom_tables_v1_recurrence_view_link_url', [ $this, 'filter_recurrence_view_link_url' ], 10, 4 );
	}

	/**
	 * Filters the provisional ID so WPML can process the post object properly.
	 *
	 *  @since 6.3.1
	 *
	 * @param WP_Post $post The post object for this metabox.
	 *
	 * @return WP_Post
	 */
	public function filter_post_metabox( $post ) {
		if ( get_post_type( $post ) === TEC::POSTTYPE ) {
			$post     = clone $post;
			$post->ID = Occurrence::normalize_id( $post->ID );
		}

		return $post;
	}

	/**
	 * Filter the permalink to retain the lang param. When provisional IDs are involved, the WPML
	 * parser will fail to locate the lang and lose the permalink.
	 *
	 * @since 6.3.0
	 * @since 7.7.12 Use wp_make_link_relative instead of str_replace for home URL removal.
	 *
	 * @param string  $post_link The post link that has been pre-filtered.
	 * @param WP_Post $post      The WP_Post for this permalink.
	 *
	 * @return string
	 */
	public function filter_permalinks_with_provisional_id( $post_link, WP_Post $post ) {
		if ( $post->post_type !== TEC::POSTTYPE ) {
			return $post_link;
		}

		// We only need to fix provisional posts, WPML can't find us by that ID.
		if ( ! tribe( Provisional_Post::class )->is_provisional_post_id( $post->ID ) ) {
			return $post_link;
		}

		// Recurring events are already being handled properly elsewhere. If that changes remove this conditional.
		if ( tribe_is_recurring_event( $post->ID ) ) {
			return $post_link;
		}

		// Remove the home URL from permalink.
		$post_link = wp_make_link_relative( $post_link );

		// Add the home URL back and retain the lang query param.
		return home_url( user_trailingslashit( $post_link ) );
	}

	/**
	 * Sets the correct language details for auto-generated Series following their creation.
	 *
	 * @since 6.0.11
	 *
	 * @param bool $updated Whether the Event custom tables data was updated correctly or not.
	 * @param int  $post_id The ID of the Event post.
	 *
	 * @return bool Whether the Event custom tables data was updated correctly or not.
	 */
	public function localize_autogenerated_series( $updated, $post_id ) {
		if ( ! is_numeric( $post_id ) ) {
			return $updated;
		}

		$series_post_type = Post_Type::POSTTYPE;
		$post             = get_post( $post_id );

		if ( ! ( $post instanceof WP_Post && $post->post_type === TEC::POSTTYPE ) ) {
			return $updated;
		}

		$series = Series_Relationship::where( 'event_post_id', $post_id )->first();

		if ( ! $series instanceof Series_Relationship ) {
			return $updated;
		}

		$is_autogenerated = tribe_is_truthy(
			get_post_meta( $series->series_post_id, Autogenerated_Series::FLAG_META_KEY, true )
		);

		if ( ! $is_autogenerated ) {
			return $updated;
		}

		$language_details = apply_filters( 'wpml_post_language_details', null, $post->ID );

		if ( ! is_array( $language_details ) || empty ( $language_details ) ) {
			do_action( 'tribe_log', 'error', __CLASS__, [
				'message'        => 'WPML event language details not found',
				'event_post_id'  => $post->ID,
				'series_post_id' => $series->series_post_id,
			] );

			return $updated;
		}
		$language_details['element_id']   = $series->series_post_id;
		$language_details['element_type'] = 'post_' . $series_post_type;
		// There is not one yet, create a new translation.
		$language_details['trid'] = false;

		do_action( 'wpml_set_element_language_details', $language_details );

		return $updated;
	}

	/**
	 * Filters the language switcher resolution of the translation to link
	 * Event Occurrences correctly one with the other.
	 *
	 * @since 6.0.11
	 *
	 * @param array $translations An array of translations.
	 * @param \WP_Query $query The WP_Query object being filtered.
	 *
	 * @return array An array of filtered translations.
	 */
	function filter_provisional_id_ls_translation( $translations, $query ) {
		if ( is_admin() || is_archive() || ! tribe_is_event_query() ) {
			// Only filter the main query in non admin context.
			return $translations;
		}

		if ( ! (
			is_array( $translations )
			&& $query instanceof \WP_Query
			&& isset( $query->post )
			&& $query->post instanceof WP_Post
		) ) {
			return $translations;
		}

		$post_id = $query->post->ID;

		$cache               = tribe_cache();
		$cache_key           = 'wpml_ls_translations_' . $post_id;
		$cached_translations = $cache->get( $cache_key, Cache_Listener::TRIGGER_SAVE_POST );

		if ( is_array( $cached_translations ) ) {
			return $cached_translations;
		}

		if ( ! tribe( Provisional_Post::class )->is_provisional_post_id( $post_id ) ) {
			return $translations;
		}

		$event_post_id = Occurrence::normalize_id( $post_id );

		// Get translations of the post ID.
		$post_type    = $query->post->post_type;
		$element_type = apply_filters( 'wpml_element_type', $post_type );
		$element_trid = apply_filters( 'wpml_element_trid', false, $event_post_id, $element_type );
		$translations = apply_filters( 'wpml_get_element_translations', [], $element_trid, $element_type );
		// Try and get the Occurrence object from the post.
		$occurrence            = $query->post->_tec_occurrence;
		$occurrence_start_date = $occurrence instanceof Occurrence ? $occurrence->start_date : null;
		$occurrence_end_date   = $occurrence instanceof Occurrence ? $occurrence->end_date : null;

		// Convert the post IDs back to provisional IDs.
		foreach ( $translations as $code => $translation ) {
			$translated_occurrence = null;
			$translated_post_id    = apply_filters( 'wpml_object_id', $event_post_id, $post_type, true, $code );

			if ( $occurrence_start_date && $occurrence_end_date ) {
				// Is there an occurrence matching the current one in the translated post?
				$translated_occurrence = Occurrence::where( 'post_id', $translated_post_id )
				                                   ->where( 'start_date', $occurrence_start_date )
				                                   ->where( 'end_date', $occurrence_end_date )
				                                   ->first();
			}

			$translation->element_id = $translated_occurrence instanceof Occurrence ?
				$translated_occurrence->provisional_id
				: $translated_post_id;
		}

		$cache->set( $cache_key, $translations, 0, Cache_Listener::TRIGGER_SAVE_POST );

		return $translations;
	}

	/**
	 * Filters the reconstructed recurrence view link URL to ensure WPML language context is preserved.
	 *
	 * @since 7.7.12
	 *
	 * @param string  $reconstructed_url The reconstructed URL.
	 * @param string  $post_link         The relative post link path.
	 * @param WP_Post $post              The post object.
	 * @param string  $date              The occurrence date in Y-m-d format.
	 *
	 * @return string The filtered URL with WPML language context preserved.
	 */
	public function filter_recurrence_view_link_url( string $reconstructed_url, string $post_link, WP_Post $post, string $date ): string {
		// Get the post's language from WPML.
		$language_details = apply_filters( 'wpml_post_language_details', null, $post->ID );
		$post_language    = $language_details['language_code'] ?? null;

		// Use WPML's permalink filter to ensure language is correct (handles both path and domain).
		if ( $post_language ) {
			$reconstructed_url = apply_filters( 'wpml_permalink', $reconstructed_url, $post_language );
		}

		return $reconstructed_url;
	}
}
