<?php
/**
 * Supreme Line Hover (`dsm/line-hover`).
 *
 * @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\ModuleLibrary\ModuleRegistration;

/**
 * Line hover link text with animated underline / stroke effects.
 */
class LineHover implements DependencyInterface {

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

	/**
	 * Register on Divi module library dependency tree.
	 *
	 * @return void
	 */
	public static function dsm_initialise(): void {
		add_action(
			'divi_module_library_modules_dependency_tree',
			static function ( $dependency_tree ): void {
				$dependency_tree->add_dependency( new self() );
			}
		);
	}

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

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

	/**
	 * Render module style.
	 *
	 * @param array<string, mixed> $args 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'];
		$animation_effect = $attrs['animation_effect']['innerContent']['desktop']['value'] ?? 'effect_1';

		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' => 'header',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-line-hover-container",
							'attr'     => $attrs['align']['innerContent'] ?? array(),
							'property' => 'text-align',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-line-hover-link::before, {$args['orderClass']} .dsm-line-hover-link::after",
							'attr'     => $attrs['animation_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					'line_bends_animation' === $animation_effect || 'line_expands_inward' === $animation_effect || 'zigzag_line_animation' === $animation_effect
						? CommonStyle::style(
							array(
								'selector' => "{$args['orderClass']} .dsm-line-hover-link",
								'attr'     => $attrs['animation_color']['innerContent'] ?? array(),
								'property' => 'stroke',
							)
						)
						: '',
				),
			)
		);
	}

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

	/**
	 * Decoration classnames.
	 *
	 * @param array<string, mixed> $args 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(),
				)
			)
		);
	}

	/**
	 * Normalize Line Offset for `--dsm-line-hover-gap` (px).
	 *
	 * @param mixed $leaf Stored innerContent leaf (string or range object).
	 * @return string Safe length token, e.g. `4px`.
	 */
	private static function normalize_line_offset( $leaf ): string {
		$fallback = '4px';

		if ( is_array( $leaf ) && isset( $leaf['value'] ) ) {
			$unit = isset( $leaf['unit'] ) && is_string( $leaf['unit'] ) ? $leaf['unit'] : 'px';
			$v    = trim( (string) $leaf['value'] );

			if ( '' === $v ) {
				return $fallback;
			}

			$leaf = $v . ( '' !== $unit ? $unit : 'px' );
		}

		if ( ! is_string( $leaf ) ) {
			return $fallback;
		}

		$t = trim( $leaf );

		if ( '' === $t ) {
			return $fallback;
		}

		if ( 1 === preg_match( '/^(-?\d+(?:\.\d+)?)(px)?$/i', $t, $m ) ) {
			return $m[1] . 'px';
		}

		return $fallback;
	}

	/**
	 * Frontend / VB markup.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 * @param string               $content Inner blocks (unused).
	 * @param \WP_Block            $block Parsed block.
	 * @param mixed                $elements Elements API.
	 * @return string
	 */
	public static function render_callback( $attrs, $content, $block, $elements ): string {
		$header_val = $attrs['header']['innerContent']['desktop']['value'] ?? '';
		/** @var string $button_text */
		$button_text = \is_array( $header_val )
			? (string) ( $header_val['text'] ?? '' )
			: (string) $header_val;

		$animation_effect   = $attrs['animation_effect']['innerContent']['desktop']['value'] ?? 'effect_1';
		$animation_duration = $attrs['animation_duration']['innerContent']['desktop']['value'] ?? '';
		$animation_delay    = $attrs['animation_delay']['innerContent']['desktop']['value'] ?? '';
		$line_offset        = self::normalize_line_offset( $attrs['line_offset']['innerContent']['desktop']['value'] ?? '4px' );
		$link_url           = $attrs['link']['innerContent']['desktop']['value'] ?? '';
		$link_url_target    = $attrs['link_url_target']['innerContent']['desktop']['value'] ?? '';

		$text = $elements->render(
			array(
				'attrName' => 'header',
			)
		);

		$style_parts = array(
			sprintf( '--dsm-line-hover-gap:%s', esc_attr( $line_offset ) ),
		);

		if ( ! empty( $animation_duration ) ) {
			$style_parts[] = sprintf( '--dsm-animation-duration:%s', esc_attr( (string) $animation_duration ) );
			$style_parts[] = sprintf( '--dsm-animation-delay:%s', esc_attr( (string) $animation_delay ) );
		}

		$container_style = sprintf( 'style="%s;"', implode( ';', $style_parts ) );

		$container = sprintf(
			'<div class="dsm-line-hover-container" %9$s>
			  <a %7$s %8$s class="dsm-line-hover-link dsm_%2$s" %6$s>
			    %1$s
				%3$s
				%4$s
				%5$s
			  </a>
			  </div>',
			$text,
			esc_attr( (string) $animation_effect ),
			'line_bends_animation' === $animation_effect ? '<svg class="link__graphic link__graphic--slide" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none"><path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path></svg>' : '',
			'line_expands_inward' === $animation_effect ? '<svg class="link__graphic link__graphic--stroke link__graphic--arc" width="100%" height="1.2em" viewBox="0 0 59 18" preserveAspectRatio="none"><path d="M.945.149C12.3 16.142 43.573 22.572 58.785 10.842" pathLength="1"></path></svg>' : '',
			'zigzag_line_animation' === $animation_effect ? '<svg class="link__graphic link__graphic--stroke link__graphic--scribble" width="100%" height="0.6em" viewBox="0 0 101 9" preserveAspectRatio="none"><path d="M.426 1.973C4.144 1.567 17.77-.514 21.443 1.48 24.296 3.026 24.844 4.627 27.5 7c3.075 2.748 6.642-4.141 10.066-4.688 7.517-1.2 13.237 5.425 17.59 2.745C58.5 3 60.464-1.786 66 2c1.996 1.365 3.174 3.737 5.286 4.41 5.423 1.727 25.34-7.981 29.14-1.294" pathLength="1"></path></svg>' : '',
			'underline_swipe_text_shift' === $animation_effect ? sprintf( 'data-text="%s" ', esc_attr( $button_text ) ) : '',
			( ! empty( $link_url ) && \is_string( $link_url ) ) ? sprintf( 'href="%s" ', esc_url( $link_url ) ) : '',
			'_blank' === $link_url_target ? 'target="_blank" rel="noopener noreferrer"' : '',
			$container_style
		);

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