<?php

/**
 * Price List 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\Module\Options\Css\CssStyle;
use DiviSupreme\Includes\Divi5Conversion\ConversionAttrs;
use DiviSupreme\Includes\ModuleRegisterAttrsNormalize;


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


	/**
	 * 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 rich `content` innerContent after merged attrs (D4 migration / legacy string saves).
	 *
	 * @param array<string, mixed> $module_attrs Merged module attrs.
	 * @param array<string, mixed> $filter_args  Filter context.
	 * @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();
		}

		if ( 'dsm/price-list-child' !== ( $filter_args['name'] ?? '' ) ) {
			return $module_attrs;
		}

		ModuleRegisterAttrsNormalize::price_list_child_merged( $module_attrs );

		return $module_attrs;
	}

	/**
	 * 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/price-list-child';

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

	/**
	 * Render module style.
	 *
	 * @param array $args - Arguments.
	 */
	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();
		$elements = $args['elements'];

		Style::add(
			array(
				'id' => $args['id'],
				'name' => $args['name'],
				'orderIndex' => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles' => 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_max_width',
						)
					),

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

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

					CommonStyle::style(
						array(
							'selector' => $sc( '.dsm_price_list', ' .dsm-pricelist-image' ),
							'attr' => $attrs['image_max_width']['innerContent'] ?? array(),
							'property' => 'max-width',
						)
					),

					CommonStyle::style(
						array(
							'selector' => $sc( '.dsm_price_list', ' .dsm-pricelist-image' ),
							'attr' => $attrs['image_spacing']['innerContent'] ?? array(),
							'property' => 'margin-right',
						)
					),
				),
			)
		);
	}

	/**
	 * 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',
			)
		);
		$elements->script_data(
			array(
				'attrName' => 'image',
			)
		);
	}

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

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

	/**
	 * Merge legacy `alt` innerContent into the image upload value when image alt is empty.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 * @return array<string, mixed> Image element attr slice for `elements->render()`.
	 */
	private static function get_image_element_attr_for_render( array $attrs, $elements ): array
	{
		$image_attr  = is_array( $attrs['image'] ?? null ) ? $attrs['image'] : array();
		$legacy_alt  = trim( wp_strip_all_tags( (string) $elements->render( array( 'attrName' => 'alt' ) ) ) );

		if ('' === $legacy_alt) {
			return $image_attr;
		}

		$desktop_value = $image_attr['innerContent']['desktop']['value'] ?? null;
		if (!is_array($desktop_value)) {
			if (is_string($desktop_value) && '' !== $desktop_value) {
				$desktop_value = array('src' => $desktop_value);
			} else {
				$desktop_value = array();
			}
		}

		$existing_alt = isset($desktop_value['alt']) && is_string($desktop_value['alt'])
			? trim($desktop_value['alt'])
			: '';

		if ('' === $existing_alt) {
			$desktop_value['alt'] = $legacy_alt;
			$image_attr['innerContent']['desktop']['value'] = $desktop_value;
		}

		return $image_attr;
	}

	/**
	 * Front-end pricelist image via Divi image element (dynamic content + srcset).
	 *
	 * @param array<string, mixed> $attrs    Module attrs.
	 * @param mixed                $elements Module elements.
	 * @return string Safe HTML (empty when no image).
	 */
	private static function render_pricelist_image_html(array $attrs, $elements): string
	{
		$image_markup = $elements->render(
			array(
				'attrName' => 'image',
				'elementAttr' => self::get_image_element_attr_for_render( $attrs, $elements ),
			)
		);

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

		return sprintf('<div class="dsm-pricelist-image">%s</div>', $image_markup);
	}

	/**
	 * 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)
	{
		unset( $content );

		// Rich text `content` may store a bare string at `innerContent.*.value`; MultiView requires `{ text }`.
		ConversionAttrs::ensure_inner_content_text_shape_for_module_elements( $attrs, $elements, 'content' );

		$price_text   = $elements->render( array( 'attrName' => 'price' ) );
		$title_text   = $elements->render( array( 'attrName' => 'title' ) );
		$content_text = $elements->render(
			array(
				'attrName'    => 'content',
				'attrSubName' => 'text',
			)
		);
		$image_html = self::render_pricelist_image_html( $attrs, $elements );

		$container = sprintf(
			'%4$s
			<div class="dsm_pricelist_item_wrapper">
			  <div class="dsm-pricelist-header">
			    <div class="dsm-pricelist-title et_pb_module_header">
				  %2$s
				</div>
				<div class="dsm-pricelist-separator"></div>
				<div class="dsm-pricelist-price">%1$s</div>
			  </div>
			  <div class="dsm-pricelist-description">%3$s</div>
			</div>
			
			',
			et_core_esc_previously( $price_text ),
			et_core_esc_previously( $title_text ),
			et_core_esc_previously( $content_text ),
			$image_html
		);

		// 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' => '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' => 'd5_tut_simple_quick_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,
			)
		);
	}
}
