<?php

use WPML\FP\Fns;
use WPML\FP\Obj;
use WPML\FP\Logic;
use WPML\FP\Type;
use WPML\FP\Str;
use WPML\PB\Shortcode\StringCleanUp;
use function WPML\FP\invoke;
use function WPML\FP\partialRight;
use function WPML\Container\make;

class WPML_PB_Integration {

	const MIGRATION_DONE_POST_META = '_wpml_location_migration_done';

	private $sitepress;

	private $factory;

	private $new_translations_recieved = false;

	private $save_post_queue = array();

	private $is_registering_string = false;

	private $strategies = array();

	private $stringCleanUp = [];

	private $rescan;

	private $media_updaters = [];

	public function __construct( SitePress $sitepress, WPML_PB_Factory $factory ) {
		$this->sitepress = $sitepress;
		$this->factory   = $factory;
	}

	public function add_strategy( IWPML_PB_Strategy $strategy ) {
		$this->strategies[] = $strategy;
	}

	public function get_rescan() {
		if ( null === $this->rescan ) {
			$this->rescan = new WPML_PB_Integration_Rescan( $this );
		}

		return $this->rescan;
	}

	public function set_rescan( WPML_PB_Integration_Rescan $rescan ) {
		$this->rescan = $rescan;
	}

	public function resave_post_translation_in_shutdown( WPML_Post_Element $post_element, $disallowed_in_shutdown = true ) {
		if ( ! $post_element->get_source_element()
			|| ( did_action( 'shutdown' ) && $disallowed_in_shutdown )
			|| array_key_exists( $post_element->get_id(), $this->save_post_queue )
		) {
			return;
		}

		if ( WPML_PB_Last_Translation_Edit_Mode::is_native_editor( $post_element->get_id() ) ) {
			return;
		}

		$updated_packages = $this->factory->get_package_strings_resave()->from_element( $post_element );

		if ( ! $updated_packages ) {
			$this->factory->get_handle_post_body()->copy(
				$post_element->get_id(),
				$post_element->get_source_element()->get_id(),
				array()
			);
		}

		$this->with_strategies(
			function ( IWPML_PB_Strategy $strategy ) use ( $updated_packages, $post_element ) {
				foreach ( $updated_packages as $package ) {
					$this->factory->get_string_translations( $strategy )
						->add_package_to_update_list( $package, $post_element->get_language_code() );
				}
			}
		);

		$this->new_translations_recieved = true;
		$this->queue_save_post_actions( $post_element->get_id(), $post_element->get_wp_object() );
	}

	public function queue_save_post_actions( $post_id, $post ) {
		$this->update_last_editor_mode( (int) $post_id );
		$this->save_post_queue[ $post_id ] = $post;
	}

	public function get_save_post_queue() {
		return $this->save_post_queue;
	}

	private function update_last_editor_mode( $post_id ) {
		if ( ! $this->is_translation( $post_id ) ) {
			return;
		}

		if ( $this->is_editing_translation_with_native_editor( $post_id ) ) {
			WPML_PB_Last_Translation_Edit_Mode::set_native_editor( $post_id );
		}
	}

	public function set_last_editor_mode_to_translation_editor( $post_id ) {
		WPML_PB_Last_Translation_Edit_Mode::set_translation_editor( $post_id );
	}

	private function is_editing_translation_with_native_editor( $translatedPostId ) {
		$getPOST = Obj::prop( Fns::__, $_POST );

		$isQuickEditAction = function ( $id ) use ( $getPOST ) {
			return wp_doing_ajax()
				&& 'inline-save' === $getPOST( 'action' )
				&& $id === (int) $getPOST( 'post_ID' );
		};

		$isSavingPostWithREST = function ( $translatedPostId ) {
			if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || ! in_array( $_SERVER['REQUEST_METHOD'], [ 'POST', 'PUT', 'PATCH' ], true ) ) {
				return false;
			}

			$hasValidBase = Logic::complement(
				Logic::anyPass(
					[
						Type::isNull(),
						Str::includes( '?P' ),
						Str::includes( '[' ),
						Str::includes( '(' ),
					]
				)
			);

			$quoteComposedBase = Fns::unary( partialRight( 'preg_quote', '/' ) );

			$postTypeSlugs = wpml_collect(
				get_post_types(
					[ 'show_in_rest' => true ],
					'objects'
				)
			)->map(
				function ( $postType ) {
					return Obj::prop( 'rest_base', $postType ) ?: Obj::prop( 'name', $postType );
				}
			)->filter( $hasValidBase )
				->map( $quoteComposedBase )
				->implode( '|' );

			preg_match( '/' . preg_quote( rest_get_url_prefix(), '/' ) . '\/wp\/v2\/(?:' . $postTypeSlugs . ')\/(\d+)/', $_SERVER['REQUEST_URI'], $matches );
			$RESTPostId = (int) Obj::prop( 1, $matches );

			return $RESTPostId === $translatedPostId;
		};

		$getGET = Obj::prop( Fns::__, $_GET );

		$isBulkEditAction = function ( $id ) use ( $getGET ) {
			$screenAction = 'edit-' . get_post_type( $id );
			return $screenAction === $getGET( 'screen' )
				&& 'edit' === $getGET( 'action' )
				&& wpml_collect( (array) $getGET( 'post' ) )
					->map( \WPML\FP\Cast::toInt() )
					->contains( $id );
		};

		$isTranslationWithNativeEditor = ( 'editpost' === $getPOST( 'action' ) && (int) $getPOST( 'ID' ) === $translatedPostId )
			|| ( ( $isQuickEditAction( $translatedPostId ) || $isBulkEditAction( $translatedPostId ) ) && WPML_PB_Last_Translation_Edit_Mode::is_native_editor( $translatedPostId ) )
			|| $isSavingPostWithREST( $translatedPostId );

		return apply_filters( 'wpml_pb_is_editing_translation_with_native_editor', $isTranslationWithNativeEditor, $translatedPostId );
	}

	private function is_translation( $postId ) {
		return (bool) $this->factory->get_post_element( $postId )->get_source_language_code();
	}

	public function register_all_strings_for_translation( $post, $allowRegisteringPostTranslation = false ) {
		if ( $post instanceof \WP_Post && $this->is_post_status_ok( $post ) && ( $allowRegisteringPostTranslation || $this->is_original_post( $post ) ) ) {
			$this->is_registering_string = true;
			$this->with_strategies( invoke( 'register_strings' )->with( $post ) );
			$this->is_registering_string = false;
		}
	}

	private function is_original_post( $post ) {
		return (int) $post->ID === (int) $this->sitepress->get_original_element_id( $post->ID, 'post_' . $post->post_type, false, false, false, true );
	}

	public function is_post_status_ok( $post ) {
		return ! in_array( $post->post_status, array( 'trash', 'auto-draft', 'inherit' ), true );
	}

	public function add_hooks() {
		add_action( 'pre_post_update', array( $this, 'migrate_location' ) );
		add_action( 'save_post', array( $this, 'queue_save_post_actions' ), PHP_INT_MAX, 2 );
		add_action( 'wpml_pro_translation_after_post_save', array( $this, 'set_last_editor_mode_to_translation_editor' ) );
		add_action( 'wpml_pb_resave_post_translation', array( $this, 'resave_post_translation_in_shutdown' ), 10, 2 );
		add_action( 'icl_st_add_string_translation', array( $this, 'new_translation' ), 10, 1 );
		add_action( 'wpml_pb_finished_adding_string_translations', array( $this, 'process_pb_content_with_hidden_strings_only' ), 9, 2 );
		add_action( 'wpml_pb_finished_adding_string_translations', array( $this, 'save_translations_to_post' ), 10 );
		add_action( 'wpml_pro_translation_completed', array( $this, 'cleanup_strings_after_translation_completed' ), 10, 3 );

		add_filter( 'wpml_tm_translation_job_data', array( $this, 'rescan' ), 9, 2 );

		add_action( 'wpml_pb_register_all_strings_for_translation', [ $this, 'register_all_strings_for_translation' ] );
		add_filter( 'wpml_pb_register_strings_in_content', [ $this, 'register_strings_in_content' ], 10, 3 );
		add_filter( 'wpml_pb_update_translations_in_content', [ $this, 'update_translations_in_content' ], 10, 2 );

		add_action( 'wpml_start_GB_register_strings', [ $this, 'initialize_string_clean_up' ], 10, 1 );
		add_action( 'wpml_end_GB_register_strings', [ $this, 'clean_up_strings' ], 10, 1 );

		add_filter( 'wpml_auto_translate_string_package', [ $this, 'disableTranslateEverything' ], 10, 2 );
		add_filter( 'wpml_get_page_builder_text_domains', [ $this, 'getPageBuildersKinds' ] );
	}

	public function disableTranslateEverything( $enabled, $package ) {
		$kind  = Obj::prop( 'kind', $package );
		$kinds = $this->getPageBuildersKinds( [] );

		if ( in_array( $kind, $kinds, true ) ) {
			return false;
		}

		return $enabled;
	}

	public function getPageBuildersKinds( $pbBuilders ) {
		if ( ! is_array( $pbBuilders ) ) {
			$pbBuilders = [];
		}

		return wpml_collect( $this->strategies )
			->map( invoke( 'get_package_kind' ) )
			->merge( $pbBuilders )
			->all();
	}

	public function cleanup_strings_after_translation_completed( $new_post_id, array $fields, stdClass $job ) {
		if ( 'post' === $job->element_type_prefix ) {
			$original_post = get_post( $job->original_doc_id );
			$this->register_all_strings_for_translation( $original_post );
		}
	}

	public function new_translation( $translated_string_id ) {
		if ( ! $this->is_registering_string ) {
			$this->with_strategies(
				function ( $strategy ) use ( $translated_string_id ) {
					$this->factory->get_string_translations( $strategy )->new_translation( $translated_string_id );
				}
			);
			$this->new_translations_recieved = true;
		}
	}

	private function with_strategies( callable $callback ) {
		Fns::each( $callback, $this->strategies );
	}

	public function process_pb_content_with_hidden_strings_only( $new_post_id, $original_doc_id ) {
		if (
			! did_action( 'wpml_add_string_translation' )
			&& apply_filters( 'wpml_pb_is_page_builder_page', false, get_post( $new_post_id ) )
		) {
			$targetLang = $this->sitepress->get_language_for_element( $new_post_id, 'post_' . get_post_type( $new_post_id ) );

			$addPackageToUpdateList = function ( WPML_Package $package ) use ( $targetLang ) {
				$this->with_strategies(
					function ( IWPML_PB_Strategy $strategy ) use ( $package, $targetLang ) {
						$this->factory->get_string_translations( $strategy )
							->add_package_to_update_list( $package, $targetLang );
					}
				);
			};

			$this->new_translations_recieved = wpml_collect( apply_filters( 'wpml_st_get_post_string_packages', [], $original_doc_id ) )
				->each( $addPackageToUpdateList )
				->isNotEmpty();
		}
	}

	public function save_translations_to_post() {
		if ( $this->new_translations_recieved ) {
			$this->with_strategies(
				function ( IWPML_PB_Strategy $strategy ) {
					$this->factory->get_string_translations( $strategy )->save_translations_to_post();
				}
			);
			$this->new_translations_recieved = false;
		}
	}

	public function update_translations_in_content( $content, $lang ) {
		$this->with_strategies(
			function ( IWPML_PB_Strategy $strategy ) use ( &$content, $lang ) {
				$content = $this->factory->get_string_translations( $strategy )->update_translations_in_content( $content, $lang );
			}
		);

		return $content;
	}

	public function rescan( array $translation_package, $post ) {
		if ( $post instanceof WP_Post ) {
			$translation_package = $this->get_rescan()->rescan( $translation_package, $post );
		}

		return $translation_package;
	}

	public function migrate_location( $post_id ) {
		if ( $this->post_has_strings( $post_id ) && ! $this->is_migrate_location_done( $post_id ) ) {
			$wpdb = $this->sitepress->get_wpdb();
			$post = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_type, post_status, post_content FROM {$wpdb->posts} WHERE ID = %d", $post_id ) );
			if ( $this->is_post_status_ok( $post ) && $this->is_original_post( $post ) ) {
				$this->with_strategies( invoke( 'migrate_location' )->with( $post_id, $post->post_content ) );
			}

			$this->mark_migrate_location_done( $post_id );
		}
	}

	public function register_strings_in_content( $registered, $post_id, $content ) {
		foreach ( $this->strategies as $strategy ) {
			$registered = $strategy->register_strings_in_content( $post_id, $content, $this->stringCleanUp[ $post_id ] ) || $registered;
		}
		return $registered;
	}

	public function get_factory() {
		return $this->factory;
	}

	public function initialize_string_clean_up( WP_Post $post ) {
		$shortcodeStrategy = make( WPML_PB_Shortcode_Strategy::class );
		$shortcodeStrategy->set_factory( $this->factory );
		$this->stringCleanUp[ $post->ID ] = new StringCleanUp( $post->ID, $shortcodeStrategy );
	}

	public function clean_up_strings( WP_Post $post ) {
		$this->stringCleanUp[ $post->ID ]->cleanUp();
	}

	private function post_has_strings( $post_id ) {
		$wpdb                  = $this->sitepress->get_wpdb();
		$string_packages_table = $wpdb->prefix . 'icl_string_packages';

		$res = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $string_packages_table ) );

		if ( $res !== $string_packages_table ) {
			return false;
		}

		$string_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$string_packages_table} WHERE post_id = %d", $post_id ) );

		return $string_count > 0;
	}

	private function is_migrate_location_done( $post_id ) {
		return get_post_meta( $post_id, self::MIGRATION_DONE_POST_META, true );
	}

	private function mark_migrate_location_done( $post_id ) {
		update_post_meta( $post_id, self::MIGRATION_DONE_POST_META, true );
	}

	public function translate_media( $post ) {
		if ( $this->is_post_status_ok( $post ) && ! $this->is_original_post( $post ) ) {
			foreach ( $this->get_media_updaters( $post ) as $updater ) {
				$updater->translate( $post );
			}
		}
	}

	private function get_media_updaters( $post ) {
		if ( ! isset( $this->media_updaters[ $post->ID ] ) ) {
			$this->media_updaters[ $post->ID ] = apply_filters( 'wpml_pb_get_media_updaters', [], $post );
		}

		return $this->media_updaters[ $post->ID ];
	}
}
