<?php

/**
 * Dual Heading Module.
 *
 * @package DiviSupreme.
 */

declare(strict_types=1);

namespace DiviSupreme\Modules;

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

use DiviSupreme\Includes\ModuleRegisterAttrsNormalize;

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\ModuleUtils\ModuleUtils;
use ET\Builder\Packages\Module\Layout\Components\ModuleElements\ModuleElements;
use ET\Builder\Packages\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\Module\Options\Text\TextStyle;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;


/**
 * Class that handles "DualHeading Module" module.
 */
class DualHeading 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 dual-heading innerContent after merge (display types, heading HTML tag).
	 *
	 * @param array $module_attrs Merged module attrs.
	 * @param array $filter_args   Contains `name`, `attrs`, `id`, `storeInstance`.
	 * @return array
	 */
	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 (($filter_args['name'] ?? '') !== 'dsm/dual-heading') {
			return $module_attrs;
		}

		ModuleRegisterAttrsNormalize::dual_heading_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/dual-heading';

		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)
	{
		$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,
								),
							),
						)
					),

					TextStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr' => $attrs['module']['advanced']['text'] ?? array(),
						)
					),

					CssStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr' => $attrs['css'] ?? array(),
						)
					),

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

					CommonStyle::style(
						array(
							'selector' => $args['orderClass'] . ' .dsm-dual-heading-parts',
							'attr' => $attrs['main_text']['decoration']['font']['font'] ?? array(),
							'declarationFunction' => array(
								self::class,
								'parts_main_text_flex_alignment_declaration',
							),
						)
					),

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

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

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

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

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

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

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

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

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

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

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

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-dual-heading-before",
							'attr' => $attrs['before_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-dual-heading-middle",
							'attr' => $attrs['middle_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-dual-heading-after",
							'attr' => $attrs['after_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-dual-heading-parts",
							'attr' => $attrs['parts_column_gap']['innerContent'] ?? array(),
							'property' => 'column-gap',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-dual-heading-parts",
							'attr' => $attrs['parts_row_gap']['innerContent'] ?? array(),
							'property' => 'row-gap',
						)
					),
				),
			)
		);
	}

	/**
	 * Map Main Text font text-align to `justify-content` on `.dsm-dual-heading-parts` (flex row).
	 *
	 * Mirrors `dualHeadingPartsJustifyFromMainFont` in `module-styles.js`. `text-align` does not
	 * distribute flex items on the parts wrapper; static CSS must emit `justify-content` like the VB.
	 *
	 * @param array<string, mixed> $params Passed by CommonStyle; `attrValue` is the resolved font leaf.
	 * @return string
	 */
	public static function parts_main_text_flex_alignment_declaration(array $params)
	{
		$font_attr = isset($params['attrValue']) && is_array($params['attrValue'])
			? $params['attrValue']
			: array();

		$text_align = isset($font_attr['textAlign']) ? $font_attr['textAlign'] : null;

		$style_declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				'important'  => false,
			)
		);

		if (! $text_align) {
			return $style_declarations->value();
		}

		switch ($text_align) {
			case 'left':
				$style_declarations->add('justify-content', 'flex-start');
				break;
			case 'center':
				$style_declarations->add('justify-content', 'center');
				break;
			case 'right':
				$style_declarations->add('justify-content', 'flex-end');
				break;
			case 'justify':
				$style_declarations->add('justify-content', 'space-between');
				break;
			default:
				$style_declarations->add('justify-content', 'flex-start');
				break;
		}

		return $style_declarations->value();
	}

	/**
	 * 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'];

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

	/**
	 * Wrap segment markup in the same span + display classes as the Visual Builder (shortcodes run via ModuleElements::render).
	 *
	 * @param string $markup          Rendered inner HTML from `$elements->render()`.
	 * @param string $base_class      e.g. `dsm-dual-heading-before`.
	 * @param string $display_prefix  e.g. `before` → `dsm-before-display-*`.
	 * @param mixed  $display_type    D5 innerContent desktop value (`inline_block` default, `block` adds class — matches D4 only styling non-inline-block).
	 * @return string
	 */
	private static function wrap_dual_heading_segment($markup, $base_class, $display_prefix, $display_type)
	{
		if ('' === (string) $markup) {
			return '';
		}

		$modifier = '';
		if (is_string($display_type) && 'block' === $display_type) {
			$modifier = sprintf(' dsm-%s-display-%s', $display_prefix, esc_attr($display_type));
		}

		return HTMLUtility::render(
			array(
				'tag' => 'span',
				'attributes' => array(
					'class' => trim($base_class . $modifier),
				),
				'children' => $markup,
				'childrenSanitizer' => 'et_core_esc_previously',
			)
		);
	}

	/**
	 * 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)
	{
		$heading_html_tag_raw = $attrs['heading_html_tag']['innerContent']['desktop']['value'] ?? 'h2';
		$allowed_heading_tags = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span', 'p');
		$heading_html_tag = is_string($heading_html_tag_raw) && in_array(strtolower($heading_html_tag_raw), $allowed_heading_tags, true)
			? strtolower($heading_html_tag_raw)
			: 'h2';

		$before_display_type = $attrs['before_display_type']['innerContent']['desktop']['value'] ?? '';
		$middle_display_type = $attrs['middle_display_type']['innerContent']['desktop']['value'] ?? '';
		$after_display_type = $attrs['after_display_type']['innerContent']['desktop']['value'] ?? '';

		$before_segment = self::wrap_dual_heading_segment(
			$elements->render(
				array(
					'attrName' => 'before_text',
				)
			),
			'dsm-dual-heading-before',
			'before',
			$before_display_type
		);

		$middle_segment = self::wrap_dual_heading_segment(
			$elements->render(
				array(
					'attrName' => 'middle_text',
				)
			),
			'dsm-dual-heading-middle',
			'middle',
			$middle_display_type
		);

		$after_segment = self::wrap_dual_heading_segment(
			$elements->render(
				array(
					'attrName' => 'after_text',
				)
			),
			'dsm-dual-heading-after',
			'after',
			$after_display_type
		);

		$parts_inner = $before_segment . $middle_segment . $after_segment;

		$parts_dir_raw = $attrs['parts_flex_direction']['innerContent']['desktop']['value'] ?? 'row';
		$parts_dir     = (is_string($parts_dir_raw) && 'column' === strtolower(trim($parts_dir_raw))) ? 'column' : 'row';

		$parts_align_raw = $attrs['parts_column_align']['innerContent']['desktop']['value'] ?? 'stretch';
		$parts_align     = is_string($parts_align_raw) ? strtolower(trim($parts_align_raw)) : 'stretch';
		$parts_cross_mod = '';
		if ('column' === $parts_dir && in_array($parts_align, array('start', 'center', 'end'), true)) {
			$parts_cross_mod = ' dsm-dual-heading-parts-column-cross-' . $parts_align;
		}

		$parts_class = 'dsm-dual-heading-parts' . ('column' === $parts_dir ? ' dsm-dual-heading-parts-is-column' : '') . $parts_cross_mod;

		$parts_markup = HTMLUtility::render(
			array(
				'tag' => 'span',
				'attributes' => array(
					'class' => $parts_class,
				),
				'children' => $parts_inner,
				'childrenSanitizer' => 'et_core_esc_previously',
			)
		);

		$container = sprintf(
			'<%1$s class="dsm-dual-heading-main et_pb_module_header">%2$s</%1$s>',
			esc_attr($heading_html_tag),
			$parts_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' => '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,
			)
		);
	}
}
