<?php
/**
 * Admin floating blocks class file.
 *
 * @package woodmart
 */

namespace XTS\Modules\Floating_Blocks;

use Elementor\Plugin;
use XTS\Singleton;
use XTS\Modules\Floating_Blocks\Import;
use XTS\Modules\Floating_Blocks\Integrations\Fb_Document;
use XTS\Modules\Floating_Blocks\Integrations\Popup_Document;

/**
 * Manager class.
 */
class Manager extends Singleton {
	/**
	 * Importer instance.
	 *
	 * @var Import
	 */
	private $importer;

	/**
	 * Block types.
	 *
	 * @var array
	 */
	private $block_types;

	/**
	 * Constructor.
	 */
	public function init() {
		$this->importer    = new Import();
		$this->block_types = woodmart_get_config( 'fb-types' );

		add_action( 'elementor/documents/register', array( $this, 'register_elementor_document_type' ) );

		foreach ( $this->block_types as $block_type ) {
			add_action( 'wp_ajax_' . $block_type['ajax_action'], array( $this, 'create_post' ) );
		}
	}

	/**
	 * Get active builder for specific post ID.
	 *
	 * @param int $post_id Post ID.
	 * @return string Builder name (wpb|gutenberg|elementor).
	 */
	public function get_active_editor( $post_id ) {
		if ( defined( 'WPB_VC_VERSION' ) && ! wp_is_serving_rest_request() ) {
			$vc_data = get_post_meta( $post_id, '_wpb_vc_js_status', true );

			if ( 'true' === $vc_data || get_post_meta( $post_id, '_wpb_shortcodes_custom_css', true ) || get_post_meta( $post_id, 'woodmart_shortcodes_custom_css', true ) ) {
				return 'wpb';
			}
		}

		if ( woodmart_is_elementor_installed() ) {
			$document            = Plugin::$instance->documents->get( $post_id );
			$elementor_edit_mode = get_post_meta( $post_id, '_elementor_edit_mode', true );

			if ( 'builder' === $elementor_edit_mode && ( $document instanceof Popup_Document || $document instanceof Fb_Document ) ) {
				return 'elementor';
			}
		}

		return 'gutenberg';
	}

	/**
	 * Include required admin files.
	 *
	 * @param \Elementor\Core\Documents_Manager $documents_manager Elementor documents manager.
	 */
	public function register_elementor_document_type( $documents_manager ) {
		if ( ! woodmart_is_elementor_installed() ) {
			return;
		}

		$documents_manager->register_document_type( 'wd_floating_block', Fb_Document::class );
		$documents_manager->register_document_type( 'wd_popup', Popup_Document::class );

		add_action( 'elementor/editor/before_enqueue_scripts', 'woodmart_enqueue_admin_scripts' );
	}

	/**
	 * Create new block.
	 */
	public function create_post() {
		check_ajax_referer( 'wd-new-template-nonce', 'security' );

		$current_action = isset( $_POST['action'] ) ? woodmart_clean( $_POST['action'] ) : ''; // phpcs:ignore
		$block          = array();
		$block_key      = '';

		foreach ( $this->block_types as $key => $block_type ) {
			if ( $current_action === $block_type['ajax_action'] ) {
				$block     = $block_type;
				$block_key = $key;
				break;
			}
		}

		if ( empty( $block ) ) {
			wp_send_json_error( esc_html__( 'Invalid block type', 'woodmart' ) );
		}

		$title           = woodmart_clean( isset( $_POST['name'] ) ? $_POST['name'] : $block['label'] ); // phpcs:ignore
		$predefined_type = isset( $_POST['floating_type'] ) ? woodmart_clean( $_POST['floating_type'] ) : ''; // phpcs:ignore
		$predefined_name = isset( $_POST['predefined_name'] ) ? woodmart_clean( $_POST['predefined_name'] ) : ''; // phpcs:ignore

		if ( ! $predefined_type || ! $predefined_name ) {
			$args = array(
				'post_title'  => $title,
				'post_type'   => $block['post_type'],
				'post_status' => 'draft',
			);

			$id = wp_insert_post( $args );

			wp_send_json(
				array(
					'redirect_url' => $this->get_edit_url( $id ),
				)
			);
		}

		ob_start();
		$id = $this->importer->import_xml( $predefined_name, $predefined_type, $block_key );
		ob_end_clean();

		wp_update_post(
			array(
				'ID'          => $id,
				'post_title'  => $title,
				'post_name'   => sanitize_title( $title ),
				'post_status' => 'draft',
			)
		);

		wp_send_json(
			array(
				'redirect_url' => $this->get_edit_url( $id ),
			)
		);
	}

	/**
	 * Get edit URL.
	 *
	 * @param int $id Post ID.
	 * @return string Edit URL.
	 */
	private function get_edit_url( $id ) {
		$external_builder = 'wpb' === woodmart_get_current_page_builder() ? 'wpb' : 'elementor';
		$current_builder  = 'native' === woodmart_get_opt( 'current_builder' ) ? 'gutenberg' : $external_builder;

		if ( defined( 'WPB_VC_VERSION' ) && 'wpb' === $current_builder ) {
			return html_entity_decode( get_edit_post_link( $id ) . '&wpb-backend-editor' );
		}

		if ( woodmart_is_elementor_installed() && 'elementor' === $current_builder ) {
			return Plugin::$instance->documents->get( $id )->get_edit_url();
		}

		return html_entity_decode( get_edit_post_link( $id, 'block-editor' ) );
	}

	/**
	 * Get condition priority.
	 *
	 * @param string $type Condition type.
	 *
	 * @return int
	 */
	public function get_condition_priority( $type ) {
		$priority = 70;

		switch ( $type ) {
			case 'all':
				$priority = 10;
				break;
			case 'post_type':
			case 'taxonomy':
				$priority = 20;
				break;
			case 'single_post_type':
			case 'term_id':
			case 'single_posts_term_id':
				$priority = 30;
				break;
			case 'post_id':
				$priority = 40;
				break;
			case 'user_role':
				$priority = 50;
				break;
			case 'custom':
				$priority = 60;
				break;
		}

		return $priority;
	}

	/**
	 * Retrieves the IDs of floating blocks that exist on current page.
	 *
	 * @param string $post_type Post type.
	 * @return array The IDs of rendered floating blocks.
	 */
	public function get_current_ids( $post_type ) {
		$rendered_block_ids = array();
		$all_conditions     = $this->get_all_conditions();

		if ( woodmart_get_opt( 'promo_popup' ) && 'wd_popup' === $post_type ) {
			$rendered_block_ids[] = 'legacy';
		}

		foreach ( $all_conditions as $block_id => $conditions ) {
			if ( ! $this->check_conditions( $conditions ) || get_post_type( $block_id ) !== $post_type || ( 'legacy' === $block_id && 'wd_popup' === $post_type ) ) {
				continue;
			}

			$rendered_block_ids[] = $block_id;
		}

		return $rendered_block_ids;
	}

	/**
	 * Sort conditions by priority.
	 *
	 * @param array $a First condition.
	 * @param array $b Second condition.
	 *
	 * @return int
	 */
	public function sort_by_priority( $a, $b ) {
		return $b['condition_priority'] <=> $a['condition_priority'];
	}

	/**
	 * Check floating block conditions and determine if a floating block should be active.
	 *
	 * @param array $conditions Floating block condition name.
	 *
	 * @return bool
	 */
	public function check_conditions( $conditions ) {
		if ( empty( $conditions ) || ! is_array( $conditions ) ) {
			return false;
		}

		foreach ( $conditions as $id => $condition ) {
			$conditions[ $id ]['condition_priority'] = $this->get_condition_priority( $condition['type'] );
		}

		uasort( $conditions, array( $this, 'sort_by_priority' ) );

		$includes_matchs = array();
		$excludes_matchs = array();

		foreach ( $conditions as $condition ) {
			$met        = false;
			$query      = isset( $condition['query'] ) ? $condition['query'] : '';
			$comparison = isset( $condition['comparison'] ) ? $condition['comparison'] : '';

			switch ( $condition['type'] ) {
				case 'all':
					$met = true;
					break;

				case 'post_type':
					$met = get_post_type() === $query;
					break;

				case 'single_post_type':
					$met = is_singular( $query );
					break;

				case 'post_id':
					if ( $query && ! is_admin() ) {
						$met = (int) woodmart_get_the_ID() === (int) $query;
					}
					break;

				case 'single_posts_term_id':
					if ( is_singular() ) {
						$terms = wp_get_post_terms( get_the_ID(), get_taxonomies(), array( 'fields' => 'ids' ) );
						if ( $terms ) {
							$met = in_array( $query, $terms, false );
						}
					}
					break;

				case 'term_id':
					$object  = get_queried_object();
					$term_id = false;

					if ( is_object( $object ) && property_exists( $object, 'term_id' ) ) {
						$term_id = $object->term_id;
						$type    = $object->taxonomy;

						$ancestors = get_ancestors( $term_id, $type );

						if ( in_array( $query, $ancestors, false ) ) {
							$term_id = $query;
						}
					}

					if ( $term_id ) {
						$met = (int) $term_id === (int) $query;
					}
					break;

				case 'taxonomy':
					$object   = get_queried_object();
					$taxonomy = is_object( $object ) && property_exists( $object, 'taxonomy' ) ? $object->taxonomy : false;

					if ( $taxonomy ) {
						$met = $taxonomy === $query;
					}
					break;

				case 'user_role':
					$user_roles = is_user_logged_in() ? (array) wp_get_current_user()->roles : array();
					$met        = in_array( $query, $user_roles, true );
					break;

				case 'custom':
					switch ( $query ) {
						case 'search':
							$met = is_search();
							break;
						case 'blog':
							$met = (int) get_the_ID() === (int) get_option( 'page_for_posts' );
							break;
						case 'front':
							$met = (int) get_the_ID() === (int) get_option( 'page_on_front' );
							break;
						case 'archives':
							$met = is_archive();
							break;
						case 'author':
							$met = is_author();
							break;
						case 'error404':
							$met = is_404();
							break;
						case 'logged_in':
							$met = is_user_logged_in();
							break;
						case 'shop':
							if ( woodmart_woocommerce_installed() ) {
								$met = is_shop();
							}
							break;
						case 'single_product':
							if ( woodmart_woocommerce_installed() ) {
								$met = is_product();
							}
							break;
						case 'cart':
							if ( woodmart_woocommerce_installed() ) {
								$met = is_cart();
							}
							break;
						case 'checkout':
							if ( woodmart_woocommerce_installed() ) {
								$met = is_checkout();
							}
							break;
						case 'account':
							if ( woodmart_woocommerce_installed() ) {
								$met = is_account_page();
							}
							break;
						case 'is_mobile':
							$met = wp_is_mobile();
							break;
						case 'is_rtl':
							$met = is_rtl();
							break;
					}
					break;
			}

			if ( 'exclude' === $comparison ) {
				$excludes_matchs[] = $met;
			} else {
				$includes_matchs[] = $met;
			}
		}

		$is_included = ( ! empty( $includes_matchs ) && in_array( true, $includes_matchs, true ) );
		$is_excluded = ( ! empty( $excludes_matchs ) && in_array( true, $excludes_matchs, true ) );

		return $is_included && ! $is_excluded;
	}

	/**
	 * Get all IDs.
	 *
	 * Retrieves all IDs for each floating type.
	 *
	 * @return array List of IDs.
	 */
	public function get_all_ids() {
		$all_ids = array();

		foreach ( $this->block_types as $block_key => $block_type ) {
			$post_type = $block_type['post_type'];

			$ids = get_posts(
				array(
					'fields'         => 'ids',
					'posts_per_page' => apply_filters( 'woodmart_fl_block_posts_per_page', 100 ),
					'post_type'      => $post_type,
					'post_status'    => 'publish',
				)
			);

			if ( woodmart_get_opt( 'promo_popup' ) ) {
				$all_ids[] = 'legacy';
			}

			$all_ids = array_merge( $all_ids, $ids );
		}

		return $all_ids;
	}

	/**
	 * Get IDs for specific block type.
	 *
	 * @param string $block_key Block key (e.g., 'popup', 'floating-block').
	 * @return array List of IDs for the specific block type.
	 */
	public function get_ids_for_type( $block_key ) {
		$block_types = woodmart_get_config( 'fb-types' );

		if ( ! isset( $block_types[ $block_key ] ) ) {
			return array();
		}

		$post_type = $block_types[ $block_key ]['post_type'];

		$ids = get_posts(
			array(
				'fields'         => 'ids',
				'posts_per_page' => apply_filters( 'woodmart_fl_block_posts_per_page', 100 ),
				'post_type'      => $post_type,
				'post_status'    => 'publish',
			)
		);

		if ( 'popup' === $block_key && woodmart_get_opt( 'promo_popup' ) ) {
			$ids[] = 'legacy';
		}

		return $ids;
	}

	/**
	 * Get all block conditions.
	 *
	 * Retrieves conditions for all blocks by their IDs.
	 *
	 * @return array List of conditions grouped by block IDs.
	 */
	public function get_all_conditions() {
		$all_conditions = array();
		$block_types    = woodmart_get_config( 'fb-types' );

		foreach ( $block_types as $block_key => $block_type ) {
			$transient_key = $this->get_transient_key( $block_key );
			$cache         = get_transient( $transient_key );

			if ( $cache ) {
				$all_conditions += $cache;
				continue;
			}

			$ids = $this->get_ids_for_type( $block_key );

			$type_conditions = array();

			foreach ( $ids as $id ) {
				if ( 'legacy' === $id ) {
					$all_conditions['legacy'] = array();
					continue;
				}

				$conditions = $this->get_single_post_conditions( $id );

				if ( ! empty( $conditions ) ) {
					$type_conditions[ $id ] = $conditions;
				}
			}

			set_transient( $transient_key, $type_conditions );
			$all_conditions += $type_conditions;
		}

		return $all_conditions;
	}

	/**
	 * Get Gutenberg option value.
	 *
	 * @param int    $floating_id Floating block ID.
	 * @param string $option_name Option name.
	 * @return mixed
	 */
	public function get_gutenberg_option( $floating_id, $option_name ) {
		if ( strpos( $option_name, '_tablet' ) !== false ) {
			$option_name = str_replace( '_tablet', 'Tablet', $option_name );
		}

		if ( strpos( $option_name, '_mobile' ) !== false ) {
			$option_name = str_replace( '_mobile', 'Mobile', $option_name );
		}

		return get_post_meta( $floating_id, 'wd_' . $option_name, true );
	}

	/**
	 * Get transient key for block type.
	 *
	 * @param string $block_key Block key.
	 * @return string Transient key.
	 */
	private function get_transient_key( $block_key ) {
		$transient_keys = array(
			'popup'          => 'wd_all_popup_conditions',
			'floating-block' => 'wd_all_floating_block_conditions',
		);

		return isset( $transient_keys[ $block_key ] ) ? $transient_keys[ $block_key ] : 'wd_all_block_conditions';
	}

	/**
	 * Get conditions for a single floating block
	 *
	 * Retrieves the conditions for a specific floating block by its ID.
	 *
	 * @param string $id Floating block ID.
	 *
	 * @return array List of conditions for the floating block
	 */
	public function get_single_post_conditions( $id = '' ) {
		if ( 'legacy' === $id ) {
			return array();
		}

		$active_builder = $this->get_active_editor( $id );
		$conditions     = array();

		if ( 'wpb' === $active_builder ) {
			$conditions = get_post_meta( $id, 'conditions', true );
		} elseif ( 'elementor' === $active_builder ) {
			$doc               = Plugin::$instance->documents->get_doc_for_frontend( $id );
			$elementor_options = $doc ? $doc->get_settings_for_display() : array();
			$post              = get_post( $id );
			$conditions_key    = 'wd_fb_conditions';

			if ( $post && 'wd_popup' === $post->post_type ) {
				$conditions_key = 'wd_popup_conditions';
			}

			$conditions = isset( $elementor_options[ $conditions_key ] ) ? $elementor_options[ $conditions_key ] : array();

			if ( $conditions && is_array( $conditions ) ) {
				$filtered_conditions = array();

				foreach ( $conditions as $condition ) {
					if ( empty( $condition['type'] ) && empty( $condition['comparison'] ) ) {
						continue;
					}

					$filtered_conditions[] = array(
						'comparison' => $condition['comparison'],
						'type'       => $condition['type'],
						'query'      => isset( $condition[ 'query_' . $condition['type'] ] ) ? $condition[ 'query_' . $condition['type'] ] : '',
					);
				}

				$conditions = $filtered_conditions;
			}
		} else {
			$gutenberg_conditions = $this->get_gutenberg_option( $id, 'conditions' );

			if ( $gutenberg_conditions ) {
				foreach ( $gutenberg_conditions as $condition ) {
					if ( empty( $condition['type'] ) && empty( $condition['comparison'] ) ) {
						continue;
					}

					$conditions[] = array(
						'comparison' => $condition['comparison'],
						'type'       => $condition['type'],
						'query'      => isset( $condition[ 'query_' . $condition['type'] ] ) ? $condition[ 'query_' . $condition['type'] ] : '',
					);
				}
			}
		}

		return $conditions;
	}

	/**
	 * Get the block key by post type.
	 *
	 * @param string $post_type Post type.
	 * @return string|false Block key if found, false otherwise.
	 */
	public function get_block_key_by_post_type( $post_type ) {
		foreach ( $this->block_types as $block_key => $block_type ) {
			if ( $block_type['post_type'] === $post_type ) {
				return $block_key;
			}
		}
		return false;
	}
}

Manager::get_instance();
