<?php

/**
 * ScrollImage 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\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\IconLibrary\IconFont\Utils;
use ET\Builder\Packages\Module\Options\Text\TextStyle;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use ET\Builder\Packages\Module\Options\Attributes\AttributeUtils;
use DiviSupreme\Extensions\IconLibraries\LegacyModuleIconStyles;

/**
 * Class that handles "ScrollImage Module" module.
 */
class ScrollImage 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'));
	}

	/**
	 * Keep Scroll Image toggles and ranges coherent after D5 merges stored JSON + defaults (matches D4 default_on_front).
	 *
	 * @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/scroll-image') {
			return $module_attrs;
		}

		ModuleRegisterAttrsNormalize::scroll_image_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());
			}
		);
	}

	public static function scroll_image_speed_func($params)
	{
		$speed_value = $params['attrValue'] ?? array();

		$style_declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				// 'important'  => array(
				// 	'transition' => true,
				// ),
			)
		);

		$style_declarations->add('transition', "all $speed_value");

		return $style_declarations->value();
	}

	public static function icon_style_declaration($params)
	{
		return LegacyModuleIconStyles::build_pseudo_icon_declaration( $params );
	}


	/**
	 * Register module.
	 */
	public static function register_module()
	{

		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/scroll-image';

		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'];
		$use_icon = $attrs['use_icon']['innerContent']['desktop']['value'] ?? '';

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

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm_scroll_image img",
							'attr' => $attrs['scroll_image_speed']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'scroll_image_speed_func'),
						)
					),

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

					'on' === $use_icon ?
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .et_overlay::before",
							'attr' => $attrs['hover_icon']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'icon_style_declaration'),
						)
					) : '',

					'on' === $use_icon ?
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .et_overlay::before",
							'attr' => $attrs['overlay_icon_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					) : '',

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .mfp-content .mfp-close",
							'attr' => $attrs['lightbox_close_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .mfp-content",
							'attr' => $attrs['lightbox_max_width']['innerContent'] ?? array(),
							'property' => 'width',
						)
					),
				),
			)
		);
	}

	/**
	 * 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(),
				)
			)
		);
	}

	/**
	 * 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)
	{
		$raw_src_inner = $attrs['image']['innerContent']['desktop']['value'] ?? null;
		if (null === $raw_src_inner && isset($attrs['src']['innerContent']['desktop']['value'])) {
			$raw_src_inner = $attrs['src']['innerContent']['desktop']['value'];
		}
		$imageUrl = self::resolve_upload_src_string($raw_src_inner);
		$alt = $attrs['alt']['innerContent']['desktop']['value'] ?? '';
		if (!is_string($alt)) {
			$alt = '';
		}
		$show_in_lightbox = $attrs['show_in_lightbox']['innerContent']['desktop']['value'] ?? '';
		$use_overlay = $attrs['use_overlay']['innerContent']['desktop']['value'] ?? '';
		$scroll_image_reverse_direction = $attrs['scroll_image_reverse_direction']['innerContent']['desktop']['value'] ?? '';
		$scroll_image_direction = $attrs['scroll_image_direction']['innerContent']['desktop']['value'] ?? 'vertical';
		if (!in_array($scroll_image_direction, array('vertical', 'horizontal'), true)) {
			$scroll_image_direction = 'vertical';
		}
		$scroll_mobile_behavior = $attrs['scroll_image_mobile_behavior']['innerContent']['desktop']['value'] ?? 'auto';
		if (!in_array($scroll_mobile_behavior, array('auto', 'hover', 'static'), true)) {
			$scroll_mobile_behavior = 'auto';
		}
		$overlay_on_hover = $attrs['overlay_on_hover']['innerContent']['desktop']['value'] ?? '';
		$show_lightbox_other_img = $attrs['show_lightbox_other_img']['innerContent']['desktop']['value'] ?? '';
		$show_lightbox_other_img_src = self::resolve_upload_src_string($attrs['show_lightbox_other_img_src']['innerContent']['desktop']['value'] ?? '');
		$use_video_popup = $attrs['use_video_popup']['innerContent']['desktop']['value'] ?? '';
		$use_icon = $attrs['use_icon']['innerContent']['desktop']['value'] ?? '';
		$url = $attrs['url']['innerContent']['desktop']['value'] ?? '';
		if (!is_string($url)) {
			$url = '';
		}

		$needs_scroll_magnific = ( 'on' === $show_in_lightbox || 'on' === $use_video_popup );
		if ( $needs_scroll_magnific ) {
			if (
				function_exists( 'dsm_ensure_registered_script_dependency' )
				&& function_exists( 'dsm_get_active_magnific_popup_script_handle' )
				&& wp_script_is( 'dsm-scroll-image', 'registered' )
			) {
				dsm_ensure_registered_script_dependency(
					'dsm-scroll-image',
					dsm_get_active_magnific_popup_script_handle()
				);
			}
			if ( function_exists( 'dsm_enqueue_magnific_popup_assets' ) ) {
				dsm_enqueue_magnific_popup_assets();
			}
		}
		wp_enqueue_script('dsm-scroll-image');

		$img_attributes = self::build_scroll_image_img_attributes($attrs, $imageUrl, $raw_src_inner, $alt);
		$img_html       = HTMLUtility::render(
			array(
				'tag' => 'img',
				'attributes' => $img_attributes,
			)
		);
		$overlay_html = '';
		if ('on' === $use_overlay) {
			$overlay_html = sprintf(
				'<div class="et_overlay %1$s"></div>',
				'on' !== $use_icon ? 'dsm-scroll-image-icon-empty' : ''
			);
		}

		$inner_content = '';
		if ('on' === $show_in_lightbox && 'off' === $use_video_popup) {
			$lightbox_target = 'off' === $show_lightbox_other_img ? $imageUrl : $show_lightbox_other_img_src;
			$inner_content = sprintf(
				'<a data-order-class="%1$s" data-image-url="%2$s" class="dsm_lightbox_image"><span class="et_pb_image_wrap">%3$s%4$s</span></a>',
				esc_attr('dsm_scroll_image_' . $block->parsed_block['orderIndex']),
				esc_attr($lightbox_target),
				$img_html,
				$overlay_html
			);
		} elseif ('off' === $show_in_lightbox && 'on' === $use_video_popup) {
			$inner_content = sprintf(
				'<a href="%1$s" data-order-class="%2$s" class="dsm_lightbox_video"><span class="et_pb_image_wrap">%3$s%4$s</span></a>',
				esc_url($url),
				esc_attr('dsm_scroll_image_' . $block->parsed_block['orderIndex']),
				$img_html,
				$overlay_html
			);
		} elseif ('off' === $show_in_lightbox && 'off' === $use_video_popup) {
			$inner_content = sprintf(
				'<span class="et_pb_image_wrap">%1$s%2$s</span>',
				$img_html,
				$overlay_html
			);
		}

		$direction_class = 'horizontal' === $scroll_image_direction ? 'dsm-scroll-direction-horizontal' : 'dsm-scroll-direction-vertical';
		$reverse_class = 'on' === $scroll_image_reverse_direction ? 'dsm-scroll-reverse-direction' : '';
		$overlay_hover_class = 'on' === $overlay_on_hover ? 'dsm-scroll-image-overlay-on' : 'dsm-scroll-image-overlay-off';
		$mobile_class = 'dsm-scroll-mobile-' . $scroll_mobile_behavior;

		$wrapper_classes = trim(implode(
			' ',
			array_filter(
				array(
					'dsm-scroll-image-wrapper',
					$direction_class,
					$reverse_class,
					$overlay_hover_class,
					$mobile_class,
				)
			)
		));

		$wrapper_attributes = array(
			'class' => $wrapper_classes,
		);
		if ('auto' === $scroll_mobile_behavior) {
			$wrapper_attributes['tabindex'] = '0';
		}

		$container = HTMLUtility::render(
			array(
				'tag' => 'div',
				'attributes' => $wrapper_attributes,
				'childrenSanitizer' => 'et_core_esc_previously',
				'children' => $inner_content,
			)
		);

		// 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' => 'dsm_scroll_image',
				'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,
			)
		);
	}

	/**
	 * Escape image URL or data URI for an img src attribute.
	 *
	 * @param string $src Raw src.
	 * @return string
	 */
	private static function escape_img_src_attribute($src)
	{
		$src = (string) $src;
		if (preg_match('#^data:image/[a-zA-Z0-9.+-]+;base64,[a-zA-Z0-9+/=]+$#', $src)) {
			return esc_attr($src);
		}

		return esc_url($src);
	}

	/**
	 * Custom attributes for the main `<img>` from Advanced → Attributes (target: image).
	 *
	 * @param array $attrs Module attrs.
	 * @return array<string, string>
	 */
	private static function get_scroll_image_src_target_attributes_from_decoration(array $attrs): array
	{
		$dec = $attrs['module']['decoration']['attributes'] ?? array();
		if (empty($dec) || ! is_array($dec)) {
			return array();
		}

		$separated = AttributeUtils::separate_attributes_by_target_element($dec);
		$target    = $separated['image'] ?? array();
		if (empty($target) && isset($separated['src']) && is_array($separated['src'])) {
			$target = $separated['src'];
		}

		return is_array($target) ? $target : array();
	}

	/**
	 * Build `<img>` attribute map: decoration target `src` + content alt fallback + lazy-loading hooks.
	 *
	 * @param array  $attrs          Module attrs.
	 * @param string $resolved_src   Image URL.
	 * @param mixed  $raw_src_inner  `src.innerContent.desktop.value`.
	 * @param string $content_alt    Alt from `alt` innerContent.
	 * @return array<string, string>
	 */
	private static function build_scroll_image_img_attributes(array $attrs, string $resolved_src, $raw_src_inner, string $content_alt): array
	{
		$extra       = self::get_scroll_image_src_target_attributes_from_decoration($attrs);
		$legacy_alt  = $content_alt;
		if (! isset($extra['alt']) && '' !== $legacy_alt) {
			$extra['alt'] = $legacy_alt;
		}

		$img_attributes = array(
			'class' => 'dsm_scroll_image_img',
			'src' => self::escape_img_src_attribute($resolved_src),
			'decoding' => 'async',
		);

		foreach ($extra as $key => $val) {
			if (! is_string($key) || '' === $key || 'class' === $key || 'src' === $key) {
				continue;
			}
			if (! is_scalar($val)) {
				continue;
			}
			$s = trim((string) $val);
			if ('' === $s && 'alt' !== $key) {
				continue;
			}
			$img_attributes[ $key ] = esc_attr($s);
		}

		if (! isset($img_attributes['alt'])) {
			$img_attributes['alt'] = esc_attr($legacy_alt);
		}

		$attachment_id = 0;
		if (is_array($raw_src_inner) && ! empty($raw_src_inner['id'])) {
			$attachment_id = absint($raw_src_inner['id']);
		}

		$classes = array('dsm_scroll_image_img');
		if ($attachment_id > 0) {
			$classes[] = 'wp-image-' . $attachment_id;
			$meta      = wp_get_attachment_metadata($attachment_id);
			if (! empty($meta['width']) && ! empty($meta['height'])) {
				$img_attributes['width']  = (string) (int) $meta['width'];
				$img_attributes['height'] = (string) (int) $meta['height'];
				$img_attributes['loading'] = 'lazy';
			}
		}

		$img_attributes['class'] = esc_attr(implode(' ', $classes));

		return $img_attributes;
	}

	/**
	 * Normalize upload field value to a URL string (legacy string or `{ src, id }`).
	 *
	 * @param mixed $raw InnerContent desktop value.
	 * @return string
	 */
	private static function resolve_upload_src_string($raw)
	{
		if (is_string($raw) && '' !== $raw) {
			return $raw;
		}
		if (!is_array($raw)) {
			return '';
		}
		if (!empty($raw['src']) && is_string($raw['src'])) {
			return $raw['src'];
		}
		if (!empty($raw['url']) && is_string($raw['url'])) {
			return $raw['url'];
		}
		$id = isset($raw['id']) ? absint($raw['id']) : 0;
		if ($id > 0) {
			$url = wp_get_attachment_image_url($id, 'full');
			return is_string($url) ? $url : '';
		}
		return '';
	}

}
