<?php
/**
 * Text Rotator module (Divi 5).
 *
 * @package DiviSupreme
 */

declare(strict_types=1);

namespace DiviSupreme\Modules;

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

use DiviSupreme\Includes\DsmFrontendDynamicAssets;
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\Css\CssStyle;
use ET\Builder\Packages\Module\Options\Element\ElementClassnames;
use ET\Builder\Packages\Module\Options\Text\TextStyle;
use ET\Builder\Packages\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\Conversion\ValueExpansion;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;

/**
 * Handles the Supreme Text Rotator module.
 */
class TextRotator implements DependencyInterface {

	/**
	 * Initialize module registration on init.
	 *
	 * @return void
	 */
	public function load(): void {
		add_action( 'init', array( self::class, 'register_module' ) );
	}

	/**
	 * Legacy bootstrap (unused in Divi 5 dependency tree).
	 *
	 * @return void
	 */
	public static function dsm_initialise(): void {
		add_action(
			'divi_module_library_modules_dependency_tree',
			function ( $dependency_tree ) {
				$dependency_tree->add_dependency( new self() );
			}
		);
	}

	/**
	 * Register with Divi module library.
	 *
	 * @return void
	 */
	public static function register_module(): void {
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/text-rotator';

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

	/**
	 * Module style definitions.
	 *
	 * @param array<string, mixed> $args Hook args.
	 * @return void
	 */
	public static function module_styles( array $args ): void {
		$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,
								),
							),
						)
					),
					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' ) ),
					$elements->style( array( 'attrName' => 'before_text' ) ),
					$elements->style( array( 'attrName' => 'after_text' ) ),
					$elements->style( array( 'attrName' => 'text_rotator' ) ),
					$elements->style( array( 'attrName' => 'rotator_text' ) ),
					$elements->style( array( 'attrName' => 'before_padding' ) ),
					$elements->style( array( 'attrName' => 'rotator_padding' ) ),
					$elements->style( array( 'attrName' => 'after_padding' ) ),
					$elements->style( array( 'attrName' => 'before_margin' ) ),
					$elements->style( array( 'attrName' => 'rotator_margin' ) ),
					$elements->style( array( 'attrName' => 'after_margin' ) ),
					$elements->style( array( 'attrName' => 'beforeStyle' ) ),
					$elements->style( array( 'attrName' => 'rotatorStyle' ) ),
					$elements->style( array( 'attrName' => 'afterStyle' ) ),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-rotate-text-before",
							'attr'     => $attrs['before_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-rotate-text-after",
							'attr'     => $attrs['after_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-rotate-text",
							'attr'     => $attrs['rotator_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
				),
			)
		);
	}

	/**
	 * Script data component registration.
	 *
	 * @param array<string, mixed> $args Hook args.
	 * @return void
	 */
	public static function module_script_data( array $args ): void {
		$elements = $args['elements'];
		$elements->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

	/**
	 * FE / VB classnames.
	 *
	 * @param array<string, mixed> $args Hook args.
	 * @return void
	 */
	public static function module_classnames( array $args ): void {
		$classnames_instance = $args['classnamesInstance'];
		$attrs               = $args['attrs'];
		$classnames_instance->add(
			ElementClassnames::classnames(
				array(
					'attrs' => $attrs['module']['decoration'] ?? array(),
				)
			)
		);
	}

	/**
	 * Whitelist wrapper tag for rotator block.
	 *
	 * @param mixed $tag Raw tag string.
	 * @return string
	 */
	private static function sanitize_heading_tag( $tag ): string {
		$allowed = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span' );
		$t       = is_string( $tag ) ? strtolower( trim( $tag ) ) : '';
		if ( in_array( $t, $allowed, true ) ) {
			return $t;
		}
		return 'h2';
	}

	/**
	 * Build pipe-separated words string for ReplaceMe.
	 *
	 * @param mixed $text_rotator InnerContent value (array of rows or legacy).
	 * @return string
	 */
	private static function build_rotator_words_string( $text_rotator ): string {
		if ( ! is_array( $text_rotator ) || array() === $text_rotator ) {
			return '';
		}
		$parts = array();
		foreach ( $text_rotator as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}
			$parts[] = isset( $item['value'] ) ? (string) $item['value'] : '';
		}
		return implode( '|', $parts );
	}

	/**
	 * Normalize rotator rows for FE (same pipeline as outline `sortableListConverter`, plus legacy `&#91;` / `&#93;` shortcode saves).
	 *
	 * @param mixed $raw `text_rotator.innerContent.desktop.value` (array or encoded JSON string).
	 * @return list<array{value: string, dragID: string}>
	 */
	private static function normalize_text_rotator_rows( $raw ): array {
		if ( is_array( $raw ) ) {
			$decoded = $raw;
		} elseif ( is_string( $raw ) && '' !== trim( $raw ) ) {
			$decoded = null;
			try {
				$converted = ValueExpansion::sortableListConverter( $raw );
				if ( is_array( $converted ) && array() !== $converted ) {
					$decoded = $converted;
				}
			} catch ( \Throwable $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
				// `sortableListConverter` may TypeError when JSON decode fails (null passed to array_map).
			}
			if ( null === $decoded ) {
				$json    = str_replace( array( '&#91;', '&#93;' ), array( '[', ']' ), $raw );
				$decoded = json_decode( $json, true );
				if ( ! is_array( $decoded ) ) {
					return array();
				}
			}
		} else {
			return array();
		}

		$out = array();
		foreach ( $decoded as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$val  = isset( $row['value'] ) ? (string) $row['value'] : '';
			$drag = isset( $row['dragID'] ) && null !== $row['dragID'] && '' !== (string) $row['dragID'] ? (string) $row['dragID'] : '';
			if ( '' === $drag ) {
				$drag = wp_generate_uuid4();
			}
			$out[] = array(
				'value'  => $val,
				'dragID' => $drag,
			);
		}

		return $out;
	}

	/**
	 * Render callback.
	 *
	 * @param array<string, mixed> $attrs    Block attrs.
	 * @param string               $content  Inner blocks.
	 * @param \WP_Block            $block    Block instance.
	 * @param mixed                $elements Style/script elements.
	 * @return string
	 */
	public static function render_callback( $attrs, $content, $block, $elements ): string {
		unset( $content );

		if ( ! is_array( $attrs ) || ! $block instanceof \WP_Block || ! is_object( $elements ) ) {
			return '';
		}

		$before_raw   = $attrs['before_text']['innerContent']['desktop']['value'] ?? '';
		$after_raw    = $attrs['after_text']['innerContent']['desktop']['value'] ?? '';
		$before_text  = is_scalar( $before_raw ) ? (string) $before_raw : '';
		$after_text   = is_scalar( $after_raw ) ? (string) $after_raw : '';
		$raw_rotator  = $attrs['text_rotator']['innerContent']['desktop']['value'] ?? array();
		$text_rotator = self::normalize_text_rotator_rows( $raw_rotator );
		$heading_html_tag          = self::sanitize_heading_tag( $attrs['heading_html_tag']['innerContent']['desktop']['value'] ?? 'h2' );
		$text_rotator_animation_in = (string) ( $attrs['text_rotator_animation_in']['innerContent']['desktop']['value'] ?? 'fadeIn' );
		$text_rotator_speed        = (string) ( $attrs['text_rotator_speed']['innerContent']['desktop']['value'] ?? '3000ms' );
		$text_rotator_pause_hover  = (string) ( $attrs['text_rotator_pause_hover']['innerContent']['desktop']['value'] ?? 'off' );
		$text_rotator_click_change = (string) ( $attrs['text_rotator_click_change']['innerContent']['desktop']['value'] ?? 'off' );

		$before_display_type  = (string) ( $attrs['before_display_type']['innerContent']['desktop']['value'] ?? 'inline_block' );
		$rotator_display_type = (string) ( $attrs['rotator_display_type']['innerContent']['desktop']['value'] ?? 'inline_block' );
		$after_display_type   = (string) ( $attrs['after_display_type']['innerContent']['desktop']['value'] ?? 'inline_block' );

		wp_enqueue_script( 'dsm-replace-me' );
		wp_enqueue_script( 'dsm-text-rotator' );
		DsmFrontendDynamicAssets::enqueue_supreme_animate_if_needed( true );

		$text_rotator_output = self::build_rotator_words_string( $text_rotator );
		$first_word          = '';
		if ( '' !== $text_rotator_output ) {
			$words = explode( '|', $text_rotator_output );
			$first_word = $words[0] ?? '';
		}

		$pause_attr = 'off' !== $text_rotator_pause_hover ? '1' : '0';
		$click_attr = 'off' !== $text_rotator_click_change ? '1' : '0';

		$before_html = '';
		if ( is_string( $before_text ) && '' !== $before_text ) {
			$before_html = sprintf(
				'<span class="dsm-rotate-text-before dsm-before-display-%1$s">%2$s</span>',
				esc_attr( $before_display_type ),
				esc_html( $before_text )
			);
		}

		$after_html = '';
		if ( is_string( $after_text ) && '' !== $after_text ) {
			$after_html = sprintf(
				'<span class="dsm-rotate-text-after dsm-after-display-%1$s">%2$s</span>',
				esc_attr( $after_display_type ),
				esc_html( $after_text )
			);
		}

		$first_word_html = '';
		if ( '' !== $first_word ) {
			$first_word_html = sprintf(
				'<span class="dsm-rotate-word">%1$s</span>',
				esc_html( $first_word )
			);
		}

		$rotator_span = sprintf(
			'<span class="dsm-rotate-text dsm-rotator-display-%1$s" data-word="%2$s" data-animation="%3$s" data-pause="%4$s" data-click="%5$s" data-speed="%6$s"></span>',
			esc_attr( $rotator_display_type ),
			esc_attr( $text_rotator_output ),
			esc_attr( $text_rotator_animation_in ),
			esc_attr( $pause_attr ),
			esc_attr( $click_attr ),
			esc_attr( $text_rotator_speed )
		);

		$container = sprintf(
			'<%1$s class="dsm-rotate-text-main et_pb_module_header">%2$s%3$s%4$s%5$s</%1$s>',
			esc_attr( $heading_html_tag ),
			$before_html,
			$first_word_html,
			$rotator_span,
			$after_html
		);

		$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'],
				'moduleClassName'     => 'dsm_text_rotator',
				'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,
			)
		);
	}
}
