<?php

/**
 * Image Accordion 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\Module\Style;
use ET\Builder\Packages\Module\Module;
use ET\Builder\Packages\Module\Options\Element\ElementClassnames;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;
use ET\Builder\Packages\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;
use ET\Builder\Packages\IconLibrary\IconFont\Utils;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use DiviSupreme\Extensions\IconLibraries\IconModuleRenderBridge;
use DiviSupreme\Extensions\IconLibraries\LegacyModuleIconStyles;



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

	/**
	 * Normalize D5 `headingLink` innerContent value (string or `['text' => string]`) for sprintf output.
	 *
	 * @param mixed $value Raw `innerContent.{bp}.value`.
	 * @return string
	 */
	private static function inner_heading_plain_text($value): string
	{
		if (is_string($value)) {
			return $value;
		}
		if (is_array($value) && isset($value['text']) && is_string($value['text'])) {
			return $value['text'];
		}

		return '';
	}

	/**
	 * Upload `innerContent` value to a single URL string (D5 uses `{ src, id }`; legacy may be a bare string).
	 *
	 * @param mixed $value Raw `innerContent.{bp}.value` for an image upload field.
	 */
	private static function inner_upload_src_string($value): string
	{
		if (is_string($value)) {
			return $value;
		}

		if (is_array($value) && isset($value['src']) && is_string($value['src'])) {
			return $value['src'];
		}

		if (is_array($value)) {
			foreach (array('desktop', 'tablet', 'phone') as $bp) {
				if (! isset($value[ $bp ]['value'])) {
					continue;
				}
				$leaf = $value[ $bp ]['value'];
				if (is_string($leaf)) {
					return $leaf;
				}
				if (is_array($leaf) && isset($leaf['src']) && is_string($leaf['src'])) {
					return $leaf['src'];
				}
			}
		}

		return '';
	}

	/**
	 * Escape URL for CSS `background-image: url('…')`.
	 *
	 * `esc_url_raw()` strips `data:` URIs (not an allowed protocol), which removes SVG placeholders on the frontend.
	 *
	 * @param string $url URL or data URI.
	 * @return string
	 */
	private static function escape_css_background_image_url( string $url ): string
	{
		if (preg_match('#^data:image/[a-zA-Z0-9.+-]+;base64,[a-zA-Z0-9+/=]+$#', $url)) {
			return $url;
		}

		return esc_url_raw($url);
	}

	/**
	 * Grey placeholder SVG used for new inserts (must match `image-accordion-child-vb-default-attrs.js`).
	 */
	private const IMAGE_ACCORDION_PLACEHOLDER_SRC = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4MCIgaGVpZ2h0PSI1NDAiIHZpZXdCb3g9IjAgMCAxMDgwIDU0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPHBhdGggZmlsbD0iI0VCRUJFQiIgZD0iTTAgMGgxMDgwdjU0MEgweiIvPgogICAgICAgIDxwYXRoIGQ9Ik00NDUuNjQ5IDU0MGgtOTguOTk1TDE0NC42NDkgMzM3Ljk5NSAwIDQ4Mi42NDR2LTk4Ljk5NWwxMTYuMzY1LTExNi4zNjVjMTUuNjItMTUuNjIgNDAuOTQ3LTE1LjYyIDU2LjU2OCAwTDQ0NS42NSA1NDB6IiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgICAgICA8Y2lyY2xlIGZpbGwtb3BhY2l0eT0iLjA1IiBmaWxsPSIjMDAwIiBjeD0iMzMxIiBjeT0iMTQ4IiByPSI3MCIvPgogICAgICAgIDxwYXRoIGQ9Ik0xMDgwIDM3OXYxMTMuMTM3TDcyOC4xNjIgMTQwLjMgMzI4LjQ2MiA1NDBIMjE1LjMyNEw2OTkuODc4IDU1LjQ0NmMxNS42Mi0xNS42MiA0MC45NDgtMTUuNjIgNTYuNTY4IDBMMTA4MCAzNzl6IiBmaWxsLW9wYWNpdHk9Ii4yIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgIDwvZz4KPC9zdmc+Cg==';

	/**
	 * @param string $url Image URL or data URI.
	 */
	private static function is_image_accordion_placeholder_src( string $url ): bool {
		return '' !== $url && self::IMAGE_ACCORDION_PLACEHOLDER_SRC === $url;
	}

	/**
	 * Whether Content-tab Image should drive the accordion panel background.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 */
	private static function has_user_content_panel_image( array $attrs ): bool {
		if ( ! isset( $attrs['image_accordion_src'] ) || ! is_array( $attrs['image_accordion_src'] ) ) {
			return false;
		}

		$inner = $attrs['image_accordion_src']['innerContent'] ?? array();
		if ( ! is_array( $inner ) ) {
			return false;
		}

		$url = self::inner_upload_src_string( $inner );

		return '' !== $url && ! self::is_image_accordion_placeholder_src( $url );
	}

	/**
	 * @param mixed $color Raw background color string.
	 */
	private static function is_meaningful_background_color( $color ): bool {
		if ( ! is_string( $color ) ) {
			return false;
		}

		$normalized = strtolower( trim( $color ) );

		if ( '' === $normalized || 'transparent' === $normalized ) {
			return false;
		}

		return 'rgba(0,0,0,0)' !== $normalized && 'rgba(0, 0, 0, 0)' !== $normalized;
	}

	/**
	 * @param array<string, mixed> $val `decoration.background.*.value` node.
	 */
	private static function decoration_background_value_has_visible_layers( array $val ): bool {
		if ( isset( $val['gradient'] ) && is_array( $val['gradient'] ) && array() !== $val['gradient'] ) {
			return true;
		}

		if ( isset( $val['video'] ) && is_array( $val['video'] ) && array() !== $val['video'] ) {
			return true;
		}

		if ( isset( $val['pattern'] ) && is_array( $val['pattern'] ) && array() !== $val['pattern'] ) {
			return true;
		}

		if ( isset( $val['mask'] ) && is_array( $val['mask'] ) && array() !== $val['mask'] ) {
			return true;
		}

		if ( self::is_meaningful_background_color( $val['color'] ?? '' ) ) {
			return true;
		}

		$image = $val['image'] ?? null;
		if ( is_array( $image ) && ( ! isset( $image['enabled'] ) || 'off' !== $image['enabled'] ) ) {
			foreach ( array( 'url', 'src' ) as $image_key ) {
				$url = $image[ $image_key ] ?? '';
				if ( is_string( $url ) && '' !== trim( $url ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Whether the native Background group should drive the panel.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 */
	private static function has_module_decoration_panel_background( array $attrs ): bool {
		$bg = $attrs['module']['decoration']['background'] ?? null;
		if ( ! is_array( $bg ) ) {
			return false;
		}

		foreach ( array( 'desktop', 'tablet', 'phone' ) as $breakpoint ) {
			$breakpoint_node = $bg[ $breakpoint ] ?? null;
			if ( ! is_array( $breakpoint_node ) ) {
				continue;
			}

			$value = $breakpoint_node['value'] ?? null;
			if ( is_array( $value ) && self::decoration_background_value_has_visible_layers( $value ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Grey SVG placeholder for new/empty child panels when Background group is unused.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 */
	private static function should_show_content_panel_placeholder( array $attrs ): bool {
		return ! self::has_user_content_panel_image( $attrs )
			&& ! self::has_module_decoration_panel_background( $attrs );
	}

	/**
	 * Inner content for `image_accordion_src` panel background styles.
	 *
	 * Content-tab Image wins over Background group. Placeholder shows when neither is set.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 * @return array<string, mixed>
	 */
	private static function resolve_image_accordion_src_inner_content_for_style( array $attrs ): array {
		if ( self::has_user_content_panel_image( $attrs ) ) {
			$inner = $attrs['image_accordion_src']['innerContent'] ?? array();

			return is_array( $inner ) ? $inner : array();
		}

		if ( self::should_show_content_panel_placeholder( $attrs ) ) {
			return array(
				'desktop' => array(
					'value' => array(
						'src' => self::IMAGE_ACCORDION_PLACEHOLDER_SRC,
					),
				),
			);
		}

		return array();
	}

	/**
	 * @param array<string, mixed> $attrs Module attrs.
	 */
	private static function should_apply_content_panel_background_image( array $attrs ): bool {
		return array() !== self::resolve_image_accordion_src_inner_content_for_style( $attrs );
	}

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

	/**
	 * Initilises 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.
	 */
	public static function register_module()
	{

		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/image-accordion-child';

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


	public static function image_bg_image_declaration($params)
	{
		$raw = $params['attrValue'] ?? null;
		if ((null === $raw || '' === $raw || array() === $raw) && isset($params['defaultAttrValue'])) {
			$raw = $params['defaultAttrValue'];
		}

		$url = self::inner_upload_src_string($raw);

		if ('' === $url) {
			return '';
		}

		$safe_url = self::escape_css_background_image_url($url);
		if ('' === $safe_url) {
			return '';
		}

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

		$style_declarations->add('background-image', 'url(\'' . str_replace("'", "\\'", $safe_url) . '\')');

		return $style_declarations->value();
	}


	/**
	 * Render module style.
	 *
	 * @param array $args - Arguments.
	 */
	public static function module_styles($args)
	{
		$attrs = $args['attrs'] ?? array();
		$elements = $args['elements'];

		$use_icon_font_size = $attrs['use_ia_icon_font_size']['innerContent']['desktop']['value'] ?? '';

		$styles_list = array(
					// Module.
					$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' => 'image_accordion_title',
						)
					),

					$elements->style(
						array(
							'attrName' => 'image_accordion_desc',
						)
					),

					$elements->style(
						array(
							'attrName' => 'image_accordion_src',
						)
					),

					$elements->style(
						array(
							'attrName' => 'overlay_color',
						)
					),

					$elements->style(
						array(
							'attrName' => 'button',
						)
					),

					$elements->style(
						array(
							'attrName' => 'image_accordion_icon',
						)
					),

					$elements->style(
						array(
							'attrName' => 'ia_icon_color',
						)
					),

					$elements->style(
						array(
							'attrName' => 'ia_icon_font_size',
						)
					),

					$elements->style(
						array(
							'attrName' => 'image_icon',
						)
					),
		);

		if ( self::should_apply_content_panel_background_image( $attrs ) ) {
			$styles_list[] = CommonStyle::style(
				array(
					'selector' => "{$args['orderClass']}.dsm_image_accordion_child",
					'attr' => self::resolve_image_accordion_src_inner_content_for_style( $attrs ),
					'declarationFunction' => array(static::class, 'image_bg_image_declaration'),
				)
			);
		}

		$styles_list = array_merge(
			$styles_list,
			array(
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm_image_accordion_child:before",
							'attr' => $attrs['overlay_color']['innerContent'] ?? array(),
							'property' => 'background',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .et-pb-icon.dsm_image_accordion_icon::after",
							'attr' => $attrs['image_accordion_icon']['innerContent'] ?? array(),
							'declarationFunction' => array( LegacyModuleIconStyles::class, 'build_pseudo_icon_declaration' ),
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .et-pb-icon",
							'attr' => $attrs['ia_icon_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_image_accordion_child_content .dsm_image_accordion_image_icon_wrapper .et-pb-icon",
							'attr' => $attrs['ia_icon_font_size']['innerContent'] ?? array(),
							'property' => 'font-size',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.et_pb_module .dsm_image_accordion_image_icon_wrapper img",
							'attr' => $attrs['image_width']['innerContent'] ?? array(),
							'property' => 'width',
						)
					),
			)
		);

		Style::add(
			array(
				'id' => $args['id'],
				'name' => $args['name'],
				'orderIndex' => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles' => $styles_list,
			)
		);
	}

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

		// Element Script Data Options.
		$elements->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

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

		$horizontal_raw = null;
		if (isset($attrs['ia_align_horizontal']['innerContent']['desktop']['value'])) {
			$horizontal_raw = $attrs['ia_align_horizontal']['innerContent']['desktop']['value'];
		}
		$vertical_raw = null;
		if (isset($attrs['ia_align_vertical']['innerContent']['desktop']['value'])) {
			$vertical_raw = $attrs['ia_align_vertical']['innerContent']['desktop']['value'];
		}

		$content_horizontal_value = (is_string($horizontal_raw) && '' !== $horizontal_raw)
			? $horizontal_raw
			: 'center';
		$content_vertical_value = (is_string($vertical_raw) && '' !== $vertical_raw)
			? $vertical_raw
			: 'center';

		$expand_item = $attrs['expanded_item']['innerContent']['desktop']['value'] ?? 'off';

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

		$classnames_instance->add('dsm_image_accordion_horizontal_' . $content_horizontal_value);
		$classnames_instance->add('dsm_image_accordion_vertical_' . $content_vertical_value);

		if ('on' === $expand_item) {
			$classnames_instance->add('dsm_image_accordion_active_item');
		}
	}

	/**
	 * Render module HTML output.
	 *
	 * @param array     $attrs - Modules Attributes.
	 * @param string    $content - Content.
	 * @param \WP_Block $block - Block.
	 * @param mixed     $elements - Elements.
	 */
	public static function render_callback($attrs, $content, $block, $elements)
	{

		$title_raw     = $attrs['image_accordion_title']['innerContent']['desktop']['value'] ?? null;
		$title_text    = self::inner_heading_plain_text($title_raw);
		$title_tag = $attrs['image_accordion_title']['decoration']['font']['font']['desktop']['value']['headingLevel'] ?? 'h3';
		$description = self::inner_heading_plain_text(
			$attrs['image_accordion_desc']['innerContent']['desktop']['value'] ?? ''
		);
		$show_button = $attrs['show_ia_button']['innerContent']['desktop']['value'] ?? '';
		$button_text = $attrs['ia_button_text']['innerContent']['desktop']['value'] ?? '';
		$button_url = $attrs['ia_button_link']['innerContent']['desktop']['value'] ?? '';
		$button_open_window = $attrs['ia_button_link_target']['innerContent']['desktop']['value'] ?? '';
		$content_image = self::inner_upload_src_string(
			$attrs['image_accordion_icon_image']['innerContent']['desktop']['value'] ?? null
		);
		$content_use_icon_raw = $attrs['use_accordion_icon']['innerContent']['desktop']['value'] ?? null;
		$content_use_icon = (is_string($content_use_icon_raw) && '' !== $content_use_icon_raw)
			? $content_use_icon_raw
			: 'off';
		$content_icon_settings = $attrs['image_accordion_icon']['innerContent']['desktop']['value'] ?? array();
		$content_icon_settings = is_array( $content_icon_settings ) ? $content_icon_settings : array();

		// var_dump($expand_item);

		$image_icon_markup = sprintf(
			'<div class="dsm_image_accordion_image_icon_wrapper">
			  %1$s
			  %2$s
			</div>',
			'off' === $content_use_icon && '' !== $content_image
				? sprintf( '<img src="%1$s" alt="" />', esc_url( $content_image ) )
				: '',
			'on' === $content_use_icon
				? IconModuleRenderBridge::render_icon_host_markup(
					$content_icon_settings,
					array( 'dsm_image_accordion_icon' )
				)
				: '',
		);

		$button_markup = sprintf(
			'<div class="et_pb_button_module_wrapper dsm_image_accordion_button_wrapper">
			   <a class="et_pb_button dsm_ia_button" href="%2$s" %3$s>%1$s</a>
			 </div>',
			$button_text,
			$button_url,
			'on' === $button_open_window ? 'target="_blank" ' : ''
		);

		$container = sprintf(
			'<div class="dsm_image_accordion_child_content">
			  %5$s
			 <%2$s class="dsm_image_accordion_title">%1$s</%2$s>
		     <div class="dsm_image_accordion_description">%3$s</div>
			 %4$s
		    </div>',
			$title_text,
			$title_tag,
			$description,
			'on' === $show_button ? $button_markup : '',
			$image_icon_markup
		);

		// Module Inner.
		// Essentially, this is the module content.
		// Were wrapping the title and content in a div with class `et_pb_module_inner`.
		$module_inner = HTMLUtility::render(
			array(
				'tag' => 'div',
				'attributes' => array(
					'class' => 'dsm_module_inner et_pb_module_inner',
				),
				'childrenSanitizer' => 'et_core_esc_previously',
				'children' => $container,
			)
		);

		// This are the module elements that will be rendered in the frontend.
		$module_elements = $elements->style_components(
			array(
				'attrName' => 'module',
			)
		);

		// This are the children of the module container, which are the module elements and the module inner.
		$module_container_children = $module_elements . $module_inner;

		return Module::render(
			array(
				// FE only.
				'orderIndex' => $block->parsed_block['orderIndex'],
				'storeInstance' => $block->parsed_block['storeInstance'],

				// VB equivalent.
				'attrs' => $attrs,
				'elements' => $elements,
				'id' => $block->parsed_block['id'],
				'moduleClassName' => 'dsm_image_accordion_child et_pb_module',
				'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,
			)
		);
	}
}
