<?php
/**
 * Shared MCP bridge helpers for The Plus Addons (Pro) abilities.
 *
 * The Pro widget-ability files in modules/ability/widgets-ability/ each call a
 * small set of tpaep_mcp_* helper functions to read, mutate and persist the
 * Elementor element tree. They are defined here (self-contained, guarded with
 * function_exists) so the Pro abilities work on any site, independent of the
 * Free plugin's internal helper locations.
 *
 * @link    https://posimyth.com/
 * @since   6.4.17
 *
 * @package Theplus
 */

declare(strict_types=1);

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Whether Elementor is active and loaded.
 */
if ( ! function_exists( 'tpaep_mcp_has_elementor' ) ) {
	function tpaep_mcp_has_elementor(): bool {
		return class_exists( '\Elementor\Plugin' ) && isset( \Elementor\Plugin::$instance );
	}
}

/**
 * Whether a widget type is registered in Elementor.
 */
if ( ! function_exists( 'tpaep_mcp_has_registered_widget' ) ) {
	function tpaep_mcp_has_registered_widget( string $widget_type ): bool {
		if ( ! class_exists( '\Elementor\Plugin' ) ) {
			return false;
		}

		return (bool) \Elementor\Plugin::$instance->widgets_manager->get_widget_types( $widget_type );
	}
}

/**
 * Load the current Elementor element tree for a post.
 *
 * @return array<int, array<string, mixed>>|WP_Error
 */
if ( ! function_exists( 'tpaep_mcp_get_elementor_page_data' ) ) {
	function tpaep_mcp_get_elementor_page_data( int $post_id ) {
		$document = \Elementor\Plugin::$instance->documents->get( $post_id );
		if ( ! $document ) {
			return new WP_Error( 'document_not_found', __( 'Elementor document not found for this post.', 'theplus' ) );
		}

		$data = $document->get_elements_data();
		if ( is_array( $data ) && ! empty( $data ) ) {
			return $data;
		}

		$raw = get_post_meta( $post_id, '_elementor_data', true );
		if ( is_string( $raw ) && '' !== $raw ) {
			$decoded = json_decode( $raw, true );
			if ( is_array( $decoded ) ) {
				return $decoded;
			}
		}

		return array();
	}
}

/**
 * Save Elementor data with native save first, then meta fallback.
 *
 * @param array<int, array<string, mixed>> $data Element tree.
 * @return true|WP_Error
 */
if ( ! function_exists( 'tpaep_mcp_save_elementor_page_data' ) ) {
	function tpaep_mcp_save_elementor_page_data( int $post_id, array $data ) {
		$document = \Elementor\Plugin::$instance->documents->get( $post_id );
		if ( ! $document ) {
			return new WP_Error( 'document_not_found', __( 'Elementor document not found for save.', 'theplus' ) );
		}

		$result = $document->save( array( 'elements' => $data ) );
		if ( false === $result ) {
			$json = wp_json_encode( $data );
			if ( false === $json ) {
				return new WP_Error( 'json_encode_failed', __( 'Failed to encode Elementor data.', 'theplus' ) );
			}

			update_post_meta( $post_id, '_elementor_data', wp_slash( $json ) );
			update_post_meta( $post_id, '_elementor_edit_mode', 'builder' );

			if ( defined( 'ELEMENTOR_VERSION' ) ) {
				update_post_meta( $post_id, '_elementor_version', ELEMENTOR_VERSION );
			}

			delete_post_meta( $post_id, '_elementor_css' );
		}

		return true;
	}
}

/**
 * Insert an element into the Elementor tree under a parent container.
 *
 * @param array<int, array<string, mixed>> $data      Element tree (by reference).
 * @param string                           $parent_id Target container ID.
 * @param array<string, mixed>             $element   Element to insert.
 * @param int                              $position  Insert position (-1 appends).
 */
if ( ! function_exists( 'tpaep_mcp_insert_elementor_element' ) ) {
	function tpaep_mcp_insert_elementor_element( array &$data, string $parent_id, array $element, int $position = -1 ): bool {
		foreach ( $data as &$item ) {
			if ( ( $item['id'] ?? '' ) === $parent_id ) {
				if ( ! isset( $item['elements'] ) || ! is_array( $item['elements'] ) ) {
					$item['elements'] = array();
				}

				if ( $position < 0 || $position >= count( $item['elements'] ) ) {
					$item['elements'][] = $element;
				} else {
					array_splice( $item['elements'], $position, 0, array( $element ) );
				}

				return true;
			}

			if ( ! empty( $item['elements'] ) && is_array( $item['elements'] ) ) {
				if ( tpaep_mcp_insert_elementor_element( $item['elements'], $parent_id, $element, $position ) ) {
					return true;
				}
			}
		}

		return false;
	}
}

/**
 * Generate Elementor-style 7-char hex IDs.
 */
if ( ! function_exists( 'tpaep_mcp_generate_elementor_element_id' ) ) {
	function tpaep_mcp_generate_elementor_element_id(): string {
		return substr( bin2hex( random_bytes( 4 ) ), 0, 7 );
	}
}

/**
 * Sanitize nested widget settings while preserving Elementor-compatible arrays.
 *
 * @param mixed $value Raw value.
 * @return mixed
 */
if ( ! function_exists( 'tpaep_mcp_sanitize_widget_setting_value' ) ) {
	function tpaep_mcp_sanitize_widget_setting_value( $value ) {
		if ( is_array( $value ) ) {
			$sanitized = array();

			foreach ( $value as $key => $item ) {
				$sanitized_key               = is_string( $key ) ? sanitize_text_field( $key ) : $key;
				$sanitized[ $sanitized_key ] = tpaep_mcp_sanitize_widget_setting_value( $item );
			}

			return $sanitized;
		}

		if ( is_string( $value ) ) {
			return wp_check_invalid_utf8( $value );
		}

		if ( is_bool( $value ) || is_int( $value ) || is_float( $value ) || null === $value ) {
			return $value;
		}

		return (string) $value;
	}
}

/**
 * Merge raw widget control settings into the curated settings array.
 *
 * @param array<string, mixed> $settings     Curated settings.
 * @param mixed                $raw_settings Raw caller-supplied settings.
 * @return array<string, mixed>
 */
if ( ! function_exists( 'tpaep_mcp_merge_widget_settings' ) ) {
	function tpaep_mcp_merge_widget_settings( array $settings, $raw_settings ): array {
		if ( ! is_array( $raw_settings ) || array() === $raw_settings ) {
			return $settings;
		}

		return array_replace_recursive( $settings, tpaep_mcp_sanitize_widget_setting_value( $raw_settings ) );
	}
}
