<?php

/**
 * Block Reveal Text 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;

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

	/**
	 * 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/block-reveal-text';

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

	/**
	 * Allowed heading wrapper tags (matches module settings).
	 *
	 * @param string $tag Raw tag.
	 * @return string
	 */
	private static function sanitize_heading_html_tag( string $tag ): string {
		$allowed = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span', 'p' );
		$tag     = strtolower( $tag );

		return in_array( $tag, $allowed, true ) ? $tag : 'h2';
	}

	/**
	 * Block reveal animation slug for wrapper classnames.
	 *
	 * @param string $anim Raw value.
	 * @return string
	 */
	private static function sanitize_block_reveal_animation( string $anim ): string {
		$allowed = array( 'lr', 'rl', 'tb', 'bt' );

		return in_array( $anim, $allowed, true ) ? $anim : 'lr';
	}

	/**
	 * Fill `headingLevel` from legacy `heading_html_tag` when the headingLink decoration omits it (D4 migration, older saves).
	 *
	 * @param array<string, mixed> $attrs Module attrs by reference (same array as `ModuleElements` uses).
	 * @return void
	 */
	private static function ensure_block_reveal_heading_level_from_heading_html_tag( array &$attrs ): void {
		if ( ! isset( $attrs['block_reveal_text'] ) || ! is_array( $attrs['block_reveal_text'] ) ) {
			return;
		}

		$heading_raw = 'h2';
		if ( isset( $attrs['heading_html_tag']['innerContent']['desktop']['value'] ) && is_string( $attrs['heading_html_tag']['innerContent']['desktop']['value'] ) ) {
			$heading_raw = $attrs['heading_html_tag']['innerContent']['desktop']['value'];
		}
		$fallback = self::sanitize_heading_html_tag( $heading_raw );

		$br = &$attrs['block_reveal_text'];
		foreach ( array( 'desktop', 'tablet', 'phone' ) as $bp ) {
			$existing = null;
			if ( isset( $br['decoration']['font']['font'][ $bp ]['value']['headingLevel'] ) ) {
				$existing = $br['decoration']['font']['font'][ $bp ]['value']['headingLevel'];
			}
			if ( is_string( $existing ) && '' !== trim( $existing ) ) {
				continue;
			}

			$tag = $fallback;
			if ( isset( $attrs['heading_html_tag']['innerContent'][ $bp ]['value'] ) && is_string( $attrs['heading_html_tag']['innerContent'][ $bp ]['value'] ) ) {
				$tag = self::sanitize_heading_html_tag( $attrs['heading_html_tag']['innerContent'][ $bp ]['value'] );
			}

			if ( ! isset( $br['decoration'] ) || ! is_array( $br['decoration'] ) ) {
				$br['decoration'] = array();
			}
			if ( ! isset( $br['decoration']['font'] ) || ! is_array( $br['decoration']['font'] ) ) {
				$br['decoration']['font'] = array();
			}
			if ( ! isset( $br['decoration']['font']['font'] ) || ! is_array( $br['decoration']['font']['font'] ) ) {
				$br['decoration']['font']['font'] = array();
			}
			if ( ! isset( $br['decoration']['font']['font'][ $bp ] ) || ! is_array( $br['decoration']['font']['font'][ $bp ] ) ) {
				$br['decoration']['font']['font'][ $bp ] = array();
			}
			if ( ! isset( $br['decoration']['font']['font'][ $bp ]['value'] ) || ! is_array( $br['decoration']['font']['font'][ $bp ]['value'] ) ) {
				$br['decoration']['font']['font'][ $bp ]['value'] = array();
			}
			$br['decoration']['font']['font'][ $bp ]['value']['headingLevel'] = $tag;
		}
	}

	/**
	 * 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(
					$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' => 'block_reveal_text',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_text_wrapper .dsm_block_text_reveal",
							'attr'     => $attrs['block_reveal_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_text_wrapper .dsm_block_image_reveal_front",
							'attr'     => $attrs['block_reveal_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
				),
			)
		);
	}

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

		$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'];

		$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.
	 */
	public static function render_callback( $attrs, $content, $block, $elements ) {
		unset( $content );

		// Must run before `elements->render`: `ModuleElements` shares this attrs array.
		self::ensure_block_reveal_heading_level_from_heading_html_tag( $attrs );

		$block_reveal_animation = self::sanitize_block_reveal_animation(
			(string) ( $attrs['block_reveal_animation']['innerContent']['desktop']['value'] ?? 'lr' )
		);
		$block_reveal_color     = (string) ( $attrs['block_reveal_color']['innerContent']['desktop']['value'] ?? '' );
		$block_reveal_delay     = (string) ( $attrs['block_reveal_delay']['innerContent']['desktop']['value'] ?? '' );
		$block_reveal_viewport  = (string) ( $attrs['block_reveal_viewport']['innerContent']['desktop']['value'] ?? '' );

		wp_enqueue_script( 'dsm-reveal-library' );
		wp_enqueue_script( 'dsm-block-reveal-text' );

		// Single heading markup from headingLink (classes come from module metadata — no extra wrapper).
		$heading_inner = $elements->render(
			array(
				'attrName'    => 'block_reveal_text',
				'attrSubName' => 'text',
			)
		);

		// FOUC: hide until RevealFx runs; `block-reveal-text.js` clears `.dsm_block_reveal_fe_preinit` in onCover / fallback.
		$heading_inner = '<span class="dsm_block_reveal_fe_preinit" style="opacity:0">' . $heading_inner . '</span>';

		$container = sprintf(
			'<div class="dsm_block_reveal_text_wrapper dsm_block_text_reveal_%6$s dsm-block-reveal-frontend" data-dsm-block-reveal-animation="%2$s" data-dsm-block-reveal-color="%3$s" data-dsm-block-reveal-delay="%4$s" data-dsm-block-reveal-viewport="%5$s">%1$s</div>',
			$heading_inner,
			esc_attr( $block_reveal_animation ),
			esc_attr( $block_reveal_color ),
			esc_attr( $block_reveal_delay ),
			esc_attr( $block_reveal_viewport ),
			esc_attr( $block_reveal_animation )
		);

		$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;

		return Module::render(
			array(
				'orderIndex'           => $block->parsed_block['orderIndex'],
				'storeInstance'        => $block->parsed_block['storeInstance'],
				'attrs'                => $attrs,
				'elements'             => $elements,
				'id'                   => $block->parsed_block['id'],
				'name'                 => $block->block_type->name,
				'moduleClassName'      => 'dsm_block_reveal_text',
				'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,
			)
		);
	}
}
