<?php
/**
 * Text Notation module (Divi 5).
 *
 * @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\Layout\Components\StyleCommon\CommonStyle;
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\ModuleLibrary\ModuleRegistration;

/**
 * Supreme Text Notation (Rough Notation) module.
 */
class TextNotation implements DependencyInterface {

	/**
	 * {@inheritdoc}
	 *
	 * @return void
	 */
	public function load(): void {
		add_action( 'init', array( self::class, 'register_module' ) );
	}

	/**
	 * Legacy bootstrap hook (optional).
	 *
	 * @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 module JSON with Divi.
	 *
	 * @return void
	 */
	public static function register_module(): void {
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/text-notation';

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

	/**
	 * Allowed wrapper tags for the notation block.
	 *
	 * @param mixed $tag Raw tag.
	 * @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 'h1';
	}

	/**
	 * Extract usable color for SVG / data attributes.
	 *
	 * @param string $color_value Raw innerContent value.
	 * @return string
	 */
	private static function extract_color_value( string $color_value ): string {
		if ( str_starts_with( $color_value, '$variable' ) ) {
			$variable_data = substr( $color_value, 10, -2 );
			$decoded_data  = json_decode( $variable_data, true );
			if ( is_array( $decoded_data ) && isset( $decoded_data['value']['name'] ) ) {
				return 'var(--' . (string) $decoded_data['value']['name'] . ')';
			}
		}
		return $color_value;
	}

	/**
	 * Module styles component.
	 *
	 * @param array<string, mixed> $args Hook arguments.
	 * @return void
	 */
	public static function module_styles( array $args ): void {
		$args = \DiviSupreme\Includes\DsmDiviBuilderCssScope::normalize_module_styles_args( $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' => 'before_text' ) ),
					$elements->style( array( 'attrName' => 'notation_text' ) ),
					$elements->style( array( 'attrName' => 'after_text' ) ),
					$elements->style( array( 'attrName' => 'header' ) ),
					$elements->style( array( 'attrName' => 'before_margin' ) ),
					$elements->style( array( 'attrName' => 'before_padding' ) ),
					$elements->style( array( 'attrName' => 'after_margin' ) ),
					$elements->style( array( 'attrName' => 'after_padding' ) ),
					$elements->style( array( 'attrName' => 'notation_margin' ) ),
					$elements->style( array( 'attrName' => 'notation_padding' ) ),
					TextStyle::style(
						array(
							'selector' => "{$args['orderClass']}",
							'attr'     => $attrs['module']['advanced']['text'] ?? array(),
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-text-notation-before",
							'attr'     => $attrs['before_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-text-notation-middle",
							'attr'     => $attrs['notation_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-text-notation-after",
							'attr'     => $attrs['after_background_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
				),
			)
		);
	}

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

	/**
	 * Classnames callback.
	 *
	 * @param array<string, mixed> $args Hook arguments.
	 * @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(),
				)
			)
		);
	}

	/**
	 * Render callback.
	 *
	 * @param array<string, mixed> $attrs    Attributes.
	 * @param string               $content  Inner blocks (unused).
	 * @param \WP_Block            $block    Block instance.
	 * @param object               $elements Style 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 : '';

		$notation_raw  = $attrs['notation_text']['innerContent']['desktop']['value'] ?? '';
		$notation_text = is_scalar( $notation_raw ) ? (string) $notation_raw : '';
		$heading_tag   = self::sanitize_heading_tag( $attrs['heading_html_tag']['innerContent']['desktop']['value'] ?? 'h1' );
		$notation_type  = (string) ( $attrs['notation_type']['innerContent']['desktop']['value'] ?? 'underline' );
		$notation_color = self::extract_color_value( (string) ( $attrs['notation_color']['innerContent']['desktop']['value'] ?? '' ) );
		$notation_width = (string) ( $attrs['notation_width']['innerContent']['desktop']['value'] ?? '1' );
		$notation_iter  = (string) ( $attrs['notation_iterations']['innerContent']['desktop']['value'] ?? '2' );
		$viewport       = (string) ( $attrs['notation_viewport']['innerContent']['desktop']['value'] ?? '80%' );
		$delay_raw      = (string) ( $attrs['notation_delay']['innerContent']['desktop']['value'] ?? '0ms' );
		$bracket_raw    = (string) ( $attrs['notation_bracket_style']['innerContent']['desktop']['value'] ?? 'left|right' );

		$before_display_type   = (string) ( $attrs['before_display_type']['innerContent']['desktop']['value'] ?? 'inline-block' );
		$notation_display_type = (string) ( $attrs['notation_display_type']['innerContent']['desktop']['value'] ?? 'inline-block' );
		$after_display_type    = (string) ( $attrs['after_display_type']['innerContent']['desktop']['value'] ?? 'inline-block' );

		wp_enqueue_script( 'dsm-notation-library' );
		wp_enqueue_script( 'dsm-text-notation' );

		$middle_html = '';
		if ( '' !== $notation_text ) {
			$middle_html = sprintf(
				'<div class="dsm-text-notation-middle display-type-%1$s" data-type="%2$s" data-color="%3$s" data-width="%4$s" data-iterations="%5$s" data-bracket-style="%6$s" data-viewport="%7$s" data-delay="%8$s">%9$s</div>',
				esc_attr( $notation_display_type ),
				esc_attr( $notation_type ),
				esc_attr( $notation_color ),
				esc_attr( $notation_width ),
				esc_attr( $notation_iter ),
				esc_attr( $bracket_raw ),
				esc_attr( $viewport ),
				esc_attr( $delay_raw ),
				esc_html( $notation_text )
			);
		}

		$container = sprintf(
			'<%1$s class="dsm-text-notation-main et_pb_module_header">%2$s%3$s%4$s</%1$s>',
			esc_attr( $heading_tag ),
			'' !== $before_text ? sprintf(
				'<div class="dsm-text-notation-before display-type-%2$s">%1$s</div>',
				esc_html( $before_text ),
				esc_attr( $before_display_type )
			) : '',
			$middle_html,
			'' !== $after_text ? sprintf(
				'<div class="dsm-text-notation-after display-type-%2$s">%1$s</div>',
				esc_html( $after_text ),
				esc_attr( $after_display_type )
			) : ''
		);

		$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',
			)
		);

		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_text_notation',
				'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_elements . $module_inner,
			)
		);
	}
}
