<?php
/**
 * Image Hotspot Child Module.
 *
 * @package DiviSupreme
 */

declare(strict_types=1);

namespace DiviSupreme\Modules;

if ( ! defined( 'ABSPATH' ) ) {
	die( 'Direct access forbidden.' );
}

use ET\Builder\Framework\DependencyManagement\Interfaces\DependencyInterface;
use ET\Builder\Framework\Utility\HTMLUtility;
use ET\Builder\FrontEnd\BlockParser\BlockParserStore;
use ET\Builder\FrontEnd\Module\Style;
use DiviSupreme\Extensions\IconLibraries\LegacyModuleIconStyles;
use ET\Builder\Packages\Module\Module;
use ET\Builder\Packages\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use ET\Builder\Packages\Module\Options\Element\ElementClassnames;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;
use ET\Builder\Packages\ModuleUtils\ModuleUtils;

use DiviSupreme\Includes\ModuleRegisterAttrsNormalize;

/**
 * Class that handles "ImageHotspotsChild Module" module.
 */
class ImageHotspotsChild implements DependencyInterface {

	/**
	 * DSM Config.
	 *
	 * @var array<string, bool>
	 */
	public static $dsm_config = array(
		'load_icons' => true,
	);

	/**
	 * Register module.
	 * `DependencyInterface` interface ensures class method name `load()` is executed for initialization.
	 */
	public function load() {
		add_filter(
			'divi_module_library_register_module_attrs',
			array( self::class, 'filter_register_module_attrs' ),
			10,
			2
		);
		add_action( 'init', array( self::class, 'register_module' ) );
	}

	/**
	 * Normalize migrated tooltip `content` rich-text shape after merged attrs (matches card-carousel-child / flipbox-child).
	 *
	 * @param array<string, mixed> $module_attrs Merged module attrs.
	 * @param array<string, mixed> $filter_args  Contains `name`, `attrs`, `id`, `storeInstance`.
	 * @return array<string, mixed>
	 */
	public static function filter_register_module_attrs( $module_attrs, $filter_args ) {
		if ( ! is_array( $module_attrs ) || ! is_array( $filter_args ) ) {
			return is_array( $module_attrs ) ? $module_attrs : array();
		}

		$name = $filter_args['name'] ?? '';
		if ( ! in_array( $name, array( 'dsm/image-hotspots-child', 'dsm/image-hotspot-child' ), true ) ) {
			return $module_attrs;
		}

		ModuleRegisterAttrsNormalize::image_hotspots_child_merged( $module_attrs );

		return $module_attrs;
	}

	/**
	 * Initialises the module.
	 *
	 * @return void
	 */
	public static function dsm_initialise() {
		add_action(
			'divi_module_library_modules_dependency_tree',
			function ( $dependency_tree ) {
				$dependency_tree->add_dependency( new self() );
			}
		);
	}

	/**
	 * Register module.
	 *
	 * @return void
	 */
	public static function register_module() {
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/image-hotspots-child';

		ModuleRegistration::register_module(
			$module_json_path,
			array(
				'render_callback' => array( self::class, 'render_callback' ),
			)
		);
	}

	/**
	 * Marker class on portaled child tooltips (override selectors beat parent popper scope).
	 */
	private const CHILD_TOOLTIP_OVERRIDE_CLASS = 'dsm_image_hotspot_tooltip_child';


	/**
	 * Default tooltip max-width aligned with D4 `default_on_front`.
	 */
	private const TOOLTIP_MAX_WIDTH_DEFAULT = '180px';

	/**
	 * Default tooltip background when parent/child omit the key.
	 */
	private const TOOLTIP_BACKGROUND_DEFAULT = 'rgba(34,34,34,0.9)';

	/**
	 * Read desktop innerContent leaf for a scalar attr.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 * @param string               $key   Attribute key.
	 * @return string
	 */
	private static function inner_content_desktop_value( array $attrs, string $key ): string {
		$value = $attrs[ $key ]['innerContent']['desktop']['value'] ?? '';

		return is_string( $value ) ? trim( $value ) : '';
	}

	/**
	 * Whether a child tooltip scalar should inherit from the parent (not an explicit override).
	 *
	 * @param string $key   Attribute key.
	 * @param string $value Desktop innerContent value on the child.
	 * @return bool
	 */
	private static function is_child_tooltip_inherit_value( string $key, string $value ): bool {
		$value = trim( $value );

		if ( '' === $value ) {
			return true;
		}

		if ( 'tooltip_background_color' === $key ) {
			$normalized = strtolower( preg_replace( '/\s+/', '', $value ) ?? '' );

			return in_array(
				$normalized,
				array(
					strtolower( preg_replace( '/\s+/', '', self::TOOLTIP_BACKGROUND_DEFAULT ) ?? '' ),
					'rgba(34,34,34,0.9)',
					'#222222e6',
				),
				true
			);
		}

		if ( 'tooltip_max_width' === $key ) {
			return strtolower( $value ) === strtolower( self::TOOLTIP_MAX_WIDTH_DEFAULT );
		}

		return false;
	}

	/**
	 * Cascade innerContent: child value, else parent, else fallback desktop value.
	 *
	 * @param array<string, mixed> $child_attrs  Child attrs.
	 * @param array<string, mixed> $parent_attrs Parent attrs.
	 * @param string               $key          Attribute key.
	 * @param string               $fallback     Fallback desktop value.
	 * @return array<string, mixed>
	 */
	private static function merge_tooltip_inner_content_from_parent( array $child_attrs, array $parent_attrs, string $key, string $fallback ): array {
		$child_inner = $child_attrs[ $key ]['innerContent'] ?? array();
		if ( ! is_array( $child_inner ) ) {
			$child_inner = array();
		}

		$child_value = self::inner_content_desktop_value( $child_attrs, $key );

		if ( '' !== $child_value && ! self::is_child_tooltip_inherit_value( $key, $child_value ) ) {
			return $child_inner;
		}

		$parent_inner = $parent_attrs[ $key ]['innerContent'] ?? array();
		if ( is_array( $parent_inner ) && '' !== self::inner_content_desktop_value( $parent_attrs, $key ) ) {
			return $parent_inner;
		}

		$child_inner['desktop']['value'] = $fallback;

		return $child_inner;
	}

	/**
	 * Tooltip background with !important (beats Tippy / react-tooltip library defaults).
	 *
	 * @param array<string, mixed> $params Parameters.
	 * @return string
	 */
	public static function tooltip_background_style_declaration( $params ) {
		$attr_value = $params['attrValue'] ?? '';

		if ( ! is_string( $attr_value ) || '' === trim( $attr_value ) ) {
			return '';
		}

		$declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				'important'  => array(
					'background-color' => true,
				),
			)
		);

		$declarations->add( 'background-color', $attr_value );

		return $declarations->value();
	}

	/**
	 * Child tooltip selectors on the portaled tooltip element (beats parent popper defaults).
	 *
	 * @param string $child_order_class Child base order class (e.g. `.dsm_image_hotspots_child_0`).
	 * @return array{box:string,content:string,body:string,arrow:string,react_arrow:string}
	 */
	private static function portaled_child_tooltip_selectors( string $child_order_class ): array {
		$marker  = self::CHILD_TOOLTIP_OVERRIDE_CLASS;
		$box     = "{$child_order_class}.{$marker}.react-tooltip.tippy-tooltip.dsm_image_hotspot_tooltip_wrapper, {$child_order_class}.{$marker}.tippy-box.tippy-tooltip.dsm_image_hotspot_tooltip_wrapper";
		$content = "{$box} .tippy-content";
		$body    = "{$box} .dsm_image_tooltip_content, {$child_order_class}.{$marker}.react-tooltip.tippy-tooltip .dsm_image_tooltip_content";
		$arrow   = "{$child_order_class}.{$marker}.tippy-box.tippy-tooltip.dsm_image_hotspot_tooltip_wrapper .tippy-arrow, {$child_order_class}.{$marker}.react-tooltip.tippy-tooltip.dsm_image_hotspot_tooltip_wrapper .tippy-arrow";
		$react_arrow = "{$child_order_class}.{$marker}.react-tooltip.tippy-tooltip.dsm_image_hotspot_tooltip_wrapper .react-tooltip-arrow, {$child_order_class}.{$marker}.react-tooltip.dsm_image_hotspot_tooltip_wrapper > .react-tooltip-arrow";

		return array(
			'box'         => $box,
			'content'     => $content,
			'body'        => $body,
			'arrow'       => $arrow,
			'react_arrow' => $react_arrow,
		);
	}

	/**
	 * Tippy arrow fill (uses `color` → border-*-color: initial on ::before).
	 *
	 * @param array<string, mixed> $params Parameters.
	 * @return string
	 */
	public static function tooltip_arrow_color_style_declaration( $params ) {
		$attr_value = $params['attrValue'] ?? '';

		if ( ! is_string( $attr_value ) || '' === trim( $attr_value ) ) {
			return '';
		}

		$declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				'important'  => array(
					'color' => true,
				),
			)
		);

		$declarations->add( 'color', $attr_value );

		return $declarations->value();
	}

	/**
	 * react-tooltip arrow (rotated square via linear-gradient).
	 *
	 * @param array<string, mixed> $params Parameters.
	 * @return string
	 */
	public static function tooltip_react_arrow_background_style_declaration( $params ) {
		$attr_value = $params['attrValue'] ?? '';

		if ( ! is_string( $attr_value ) || '' === trim( $attr_value ) ) {
			return '';
		}

		$declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				'important'  => array(
					'background' => true,
				),
			)
		);

		$declarations->add(
			'background',
			'linear-gradient(to right bottom, transparent 50%, ' . $attr_value . ' 50%)'
		);

		return $declarations->value();
	}

	/**
	 * Icon ::before declaration for hotspot icon font.
	 *
	 * @param array<string, mixed> $params Parameters.
	 * @return string
	 */
	public static function icon_style_declaration( $params ) {
		return LegacyModuleIconStyles::build_pseudo_icon_declaration( $params );
	}

	/**
	 * Render module style.
	 *
	 * @param array $args Arguments.
	 * @return void
	 */
	public static function module_styles( $args ) {
		$args = \DiviSupreme\Includes\DsmDiviBuilderCssScope::normalize_module_styles_args( $args );
		$order_class = (string) ( $args['orderClass'] ?? '' );
		$sc        = static function ( string $parent_chain, string $suffix = '' ) use ( $order_class ): string {
			return \DiviSupreme\Includes\DsmDiviBuilderCssScope::scoped_child_selector( $parent_chain, $order_class, $suffix );
		};
		$attrs         = $args['attrs'] ?? array();
		$parent_attrs  = is_array( $args['parentAttrs'] ?? null ) ? $args['parentAttrs'] : array();
		$elements      = $args['elements'];
		$child_class   = (string) ( $args['baseOrderClass'] ?? $args['orderClass'] ?? '' );
		$tooltip_selectors = self::portaled_child_tooltip_selectors( $child_class );
		$tooltip_style_props = array(
			'selector'  => $tooltip_selectors['box'],
			'border'    => array(
				'selector'  => $tooltip_selectors['box'],
				'important' => true,
			),
			'boxShadow' => array(
				'selector'   => $tooltip_selectors['box'],
				'useOverlay' => true,
			),
		);
		$tooltip_body_style_props = array(
			'selector' => $tooltip_selectors['body'],
		);
		$tooltip_padding_style_props = array(
			'selector' => $tooltip_selectors['content'],
		);
		$child_tooltip_background_value = self::inner_content_desktop_value( $attrs, 'tooltip_background_color' );
		$has_child_tooltip_background_override =
			'' !== $child_tooltip_background_value &&
			! self::is_child_tooltip_inherit_value( 'tooltip_background_color', $child_tooltip_background_value );
		$tooltip_background_inner = $has_child_tooltip_background_override
			? ( $attrs['tooltip_background_color']['innerContent'] ?? array() )
			: array();
		$tooltip_max_width_inner = self::merge_tooltip_inner_content_from_parent(
			$attrs,
			$parent_attrs,
			'tooltip_max_width',
			self::TOOLTIP_MAX_WIDTH_DEFAULT
		);

		Style::add(
			array(
				'id'            => $args['id'],
				'name'          => $args['name'],
				'orderIndex'    => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles'        => array(
					$elements->style(
						array(
							'attrName'   => 'module',
							'styleProps' => array(
								'disabledOn' => array(
									'disabledModuleVisibility' => $args['settings']['disabledModuleVisibility'] ?? null,
								),
							),
						)
					),
					CssStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr'     => $attrs['css'] ?? array(),
						)
					),
					$elements->style(
						array(
							'attrName' => 'hotspot_padding',
						)
					),
					$elements->style(
						array(
							'attrName'   => 'tooltip_padding',
							'styleProps' => $tooltip_padding_style_props,
						)
					),
					$elements->style(
						array(
							'attrName' => 'hotspotStyle',
						)
					),
					$elements->style(
						array(
							'attrName' => 'imageStyle',
						)
					),
					$elements->style(
						array(
							'attrName' => 'hotspot_text',
						)
					),
					$elements->style(
						array(
							'attrName'   => 'tooltipStyle',
							'styleProps' => $tooltip_style_props,
						)
					),
					$elements->style(
						array(
							'attrName' => 'pulseStyle',
						)
					),
					$elements->style(
						array(
							'attrName'   => 'tooltip_body',
							'styleProps' => $tooltip_body_style_props,
						)
					),
					CommonStyle::style(
						array(
							'selector'            => "{$args['orderClass']} .dsm_image_hotspots_icon::before",
							'attr'                => $attrs['font_icon']['innerContent'] ?? array(),
							'declarationFunction' => array( static::class, 'icon_style_declaration' ),
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}",
							'attr'     => $attrs['hotspot_image_max_width']['innerContent'] ?? array(),
							'property' => 'max-width',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}:not(.tippy-box)",
							'attr'     => $attrs['left_position']['innerContent'] ?? array(),
							'property' => 'left',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}:not(.tippy-box)",
							'attr'     => $attrs['top_position']['innerContent'] ?? array(),
							'property' => 'top',
						)
					),
					CommonStyle::style(
						array(
							'selector' => $sc( '.dsm_image_hotspots', ' .dsm_image_hotspots_icon::before' ),
							'attr'     => $attrs['icon_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => $sc( '.dsm_image_hotspots', ' .dsm_image_hotspots_icon::before' ),
							'attr'     => $attrs['icon_font_size']['innerContent'] ?? array(),
							'property' => 'font-size',
						)
					),
					$has_child_tooltip_background_override &&
					'' !== $tooltip_selectors['box'] ?
					CommonStyle::style(
						array(
							'selector'            => $tooltip_selectors['box'],
							'attr'                => $tooltip_background_inner,
							'declarationFunction' => array( static::class, 'tooltip_background_style_declaration' ),
						)
					) : '',
					$has_child_tooltip_background_override &&
					'' !== $tooltip_selectors['arrow'] ?
					CommonStyle::style(
						array(
							'selector'            => $tooltip_selectors['arrow'],
							'attr'                => $tooltip_background_inner,
							'declarationFunction' => array( static::class, 'tooltip_arrow_color_style_declaration' ),
						)
					) : '',
					$has_child_tooltip_background_override &&
					'' !== $tooltip_selectors['react_arrow'] ?
					CommonStyle::style(
						array(
							'selector'            => $tooltip_selectors['react_arrow'],
							'attr'                => $tooltip_background_inner,
							'declarationFunction' => array( static::class, 'tooltip_react_arrow_background_style_declaration' ),
						)
					) : '',
					'' !== $tooltip_selectors['content'] ?
					CommonStyle::style(
						array(
							'selector' => $tooltip_selectors['content'],
							'attr'     => $tooltip_max_width_inner,
							'property' => 'max-width',
						)
					) : '',
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_image_hotspot_pulse:before",
							'attr'     => $attrs['pulse_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
				),
			)
		);
	}

	/**
	 * Render module script data.
	 *
	 * @param array $args Arguments.
	 * @return void
	 */
	public static function module_script_data( $args ) {
		$elements = $args['elements'];

		$elements->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

	/**
	 * Render module classnames.
	 *
	 * @param array $args Arguments.
	 * @return void
	 */
	public static function module_classnames( $args ) {
		$classnames_instance = $args['classnamesInstance'];
		$attrs               = $args['attrs'];

		$classnames_instance->add(
			ElementClassnames::classnames(
				array(
					'attrs' => $attrs['module']['decoration'] ?? array(),
				)
			)
		);
	}

	/**
	 * Render module HTML output.
	 *
	 * @param array     $attrs   Modules Attributes.
	 * @param string    $content Content.
	 * @param \WP_Block $block   Block.
	 * @param mixed     $elements Elements.
	 * @return string
	 */
	public static function render_callback( $attrs, $content, $block, $elements ) {
		$hotspot_type      = $attrs['hotspot_type']['innerContent']['desktop']['value'] ?? 'icon';
		$hotspot_text      = $attrs['hotspot_text']['innerContent']['desktop']['value'] ?? 'text';
		$hotspot_img_raw = $attrs['hotspot_img']['innerContent']['desktop']['value'] ?? '';
		$hotspot_img     = ImageHotspots::get_image_src( $hotspot_img_raw );
		$tooltip_raw       = $attrs['content']['innerContent']['desktop']['value'] ?? 'Your tooltip content goes here';
		$tooltip_placement = $attrs['tooltip_placement']['innerContent']['desktop']['value'] ?? 'top';
		$use_tooltip       = $attrs['use_tooltip']['innerContent']['desktop']['value'] ?? 'on';
		$pulse_animation   = $attrs['pulse_animation']['innerContent']['desktop']['value'] ?? 'off';
		$block_id          = isset( $block->parsed_block['id'] ) ? (string) $block->parsed_block['id'] : '';
		$store_instance    = $block->parsed_block['storeInstance'] ?? null;
		$order_class_name  = ModuleUtils::get_module_order_class_name( $block_id, $store_instance );
		if ( '' === $order_class_name ) {
			$order_class_name = 'dsm_image_hotspots_child_' . (int) ( $block->parsed_block['orderIndex'] ?? 0 );
		}

		if ( is_array( $tooltip_raw ) && isset( $tooltip_raw['text'] ) ) {
			$tooltip_html = wp_kses_post( (string) $tooltip_raw['text'] );
		} elseif ( is_string( $tooltip_raw ) ) {
			$tooltip_html = wp_kses_post( $tooltip_raw );
		} else {
			$tooltip_html = '';
		}

		$tooltip_wrapper = sprintf(
			'<div class="dsm_image_hotspot_tooltip">
			   <div class="dsm_image_tooltip_content">
			   	  %1$s
			   </div>
			</div>',
			$tooltip_html
		);

		$container = sprintf(
			'%8$s
			<div class="dsm_image_hotspots_wrapper dsm-hotspot-frontend" data-tippy-arrow="true" data-tippy-placement="%5$s" data-dsm-tooltip-show="%6$s" data-order-class="%7$s">
			  %1$s
			  %2$s
			  %3$s
			</div>
			%4$s',
			'icon' === $hotspot_type ? '<span class="et-pb-icon dsm_image_hotspots_icon"></span>' : '',
			'text' === $hotspot_type ? sprintf( '<span class="dsm_image_hotspots_text">%1$s</span>', esc_html( (string) $hotspot_text ) ) : '',
			'image' === $hotspot_type ? ImageHotspots::render_responsive_image_tag( $hotspot_img, $hotspot_img_raw, 'dsm_image_hotspot_img', '' ) : '',
			$tooltip_wrapper,
			esc_attr( $tooltip_placement ),
			esc_attr( $use_tooltip ),
			esc_attr( $order_class_name ),
			'on' === $pulse_animation ? '<div class="dsm_image_hotspot_pulse"></div>' : ''
		);

		$module_inner = HTMLUtility::render(
			array(
				'tag'               => 'div',
				'attributes'        => array(
					'class' => 'et_pb_module_inner',
				),
				'childrenSanitizer' => 'et_core_esc_previously',
				'children'          => $container,
			)
		);

		$module_elements = $elements->style_components(
			array(
				'attrName' => 'module',
			)
		);

		$module_container_children = $module_elements . $module_inner;

		$parent       = BlockParserStore::get_parent( $block->parsed_block['id'], $block->parsed_block['storeInstance'] );
		$parent_attrs = ( is_object( $parent ) && isset( $parent->attrs ) && is_array( $parent->attrs ) ) ? $parent->attrs : array();

		return Module::render(
			array(
				'orderIndex'          => $block->parsed_block['orderIndex'],
				'storeInstance'       => $block->parsed_block['storeInstance'],
				'attrs'               => $attrs,
				'parentAttrs'         => $parent_attrs,
				'elements'            => $elements,
				'id'                  => $block->parsed_block['id'],
				'moduleClassName'     => 'dsm_image_hotspots_child',
				'name'                => $block->block_type->name,
				'classnamesFunction'  => array( self::class, 'module_classnames' ),
				'moduleCategory'      => $block->block_type->category,
				'stylesComponent'     => array( self::class, 'module_styles' ),
				'scriptDataComponent' => array( self::class, 'module_script_data' ),
				'children'            => $module_container_children,
			)
		);
	}
}
