<?php

/**
 * Block Reveal Image 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\IconLibrary\IconFont\Utils;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use DiviSupreme\Extensions\IconLibraries\LegacyModuleIconStyles;

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

	/**
	 * Placeholder image when `src` is omitted from stored attrs (matches defaultAttrs / VB).
	 */
	private const PLACEHOLDER_IMAGE_SRC = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4MCIgaGVpZ2h0PSI1NDAiIHZpZXdCb3g9IjAgMCAxMDgwIDU0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPHBhdGggZmlsbD0iI0VCRUJFQiIgZD0iTTAgMGgxMDgwdjU0MEgweiIvPgogICAgICAgIDxwYXRoIGQ9Ik00NDUuNjQ5IDU0MGgtOTguOTk1TDE0NC42NDkgMzM3Ljk5NSAwIDQ4Mi42NDR2LTk4Ljk5NWwxMTYuMzY1LTExNi4zNjVjMTUuNjItMTUuNjIgNDAuOTQ3LTE1LjYyIDU2LjU2OCAwTDQ0NS42NSA1NDB6IiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgICAgICA8Y2lyY2xlIGZpbGwtb3BhY2l0eT0iLjA1IiBmaWxsPSIjMDAwIiBjeD0iMzMxIiBjeT0iMTQ4IiByPSI3MCIvPgogICAgICAgIDxwYXRoIGQ9Ik0xMDgwIDM3OXYxMTMuMTM3TDcyOC4xNjIgMTQwLjMgMzI4LjQ2MiA1NDBIMjE1LjMyNEw2OTkuODc4IDU1LjQ0NmMxNS42Mi0xNS42MiA0MC45NDgtMTUuNjIgNTYuNTY4IDBMMTA4MCAzNzl6IiBmaWxsLW9wYWNpdHk9Ii4yIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgIDwvZz4KPC9zdmc+Cg==';

	/**
	 * Register module.
	 *
	 * @return void
	 */
	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.
	 *
	 * @return void
	 */
	public static function register_module() {
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/block-reveal-image';

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

	/**
	 * 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 ): string {
		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 '';
	}

	/**
	 * Resolve media-library attachment ID from upload innerContent (shape or URL lookup).
	 *
	 * @param mixed $raw `src.innerContent.desktop.value`.
	 * @return int Attachment ID or 0.
	 */
	private static function resolve_upload_attachment_id( $raw ): int {
		if ( is_array( $raw ) && ! empty( $raw['id'] ) ) {
			return absint( $raw['id'] );
		}

		$url = self::resolve_upload_src_string( $raw );
		if ( '' === $url ) {
			return 0;
		}

		if ( function_exists( 'et_get_attachment_id_by_url' ) ) {
			$found = (int) et_get_attachment_id_by_url( $url );

			return $found > 0 ? $found : 0;
		}

		$found = (int) attachment_url_to_postid( $url );

		return $found > 0 ? $found : 0;
	}

	/**
	 * Escape image URL for HTML `src` / `href`. `esc_url()` strips `data:` URIs (default placeholder SVG).
	 *
	 * @param string $src Raw URL or data URI.
	 * @return string
	 */
	private static function esc_img_src_for_attribute( string $src ): string {
		if ( str_starts_with( $src, 'data:' ) ) {
			return esc_attr( $src );
		}

		return esc_url( $src );
	}

	/**
	 * Front-end <img> for the reveal stack: responsive srcset/sizes when attachment-based (like Divi Image / wp_get_attachment_image).
	 *
	 * @param string $src   Resolved URL.
	 * @param mixed  $raw   Original upload value (for ID).
	 * @return string Safe HTML.
	 */
	private static function render_block_reveal_image_img_markup( string $src, $raw ): string {
		if ( '' === $src ) {
			return '';
		}

		$attachment_id = self::resolve_upload_attachment_id( $raw );

		if ( $attachment_id > 0 && wp_attachment_is_image( $attachment_id ) ) {
			$img = wp_get_attachment_image(
				$attachment_id,
				'full',
				false,
				array(
					'class' => 'dsm_block_reveal_fe_preinit',
					'style' => 'opacity:0',
				)
			);

			if ( is_string( $img ) && '' !== $img ) {
				return $img;
			}
		}

		$img_html = sprintf(
			'<img class="dsm_block_reveal_fe_preinit" style="opacity:0" src="%s" alt="" />',
			self::esc_img_src_for_attribute( $src )
		);

		if ( function_exists( 'et_image_add_srcset_and_sizes' ) ) {
			return et_image_add_srcset_and_sizes( $img_html, false );
		}

		return $img_html;
	}

	/**
	 * Image alignment: VB/FE use `align.innerContent`; D4→D5 migrator may only fill `module.advanced.align`.
	 *
	 * @param array<string, mixed> $attrs Module attrs.
	 * @return array<string, mixed>
	 */
	private static function get_align_inner_content_for_styles( array $attrs ): array {
		if ( isset( $attrs['align']['innerContent'] ) && is_array( $attrs['align']['innerContent'] ) ) {
			return $attrs['align']['innerContent'];
		}
		if ( isset( $attrs['module']['advanced']['align'] ) && is_array( $attrs['module']['advanced']['align'] ) ) {
			return $attrs['module']['advanced']['align'];
		}

		return array();
	}

	/**
	 * @param array<string, mixed> $params Params.
	 * @return string
	 */
	public static function icon_style_declaration( $params ) {
		return LegacyModuleIconStyles::build_pseudo_icon_declaration( $params );
	}

	/**
	 * Render module style.
	 *
	 * @param array<string, mixed> $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' => 'overlay',
						)
					),
					CommonStyle::style(
						array(
							'selector'            => "{$args['orderClass']} .et_pb_inline_icon::before",
							'attr'                => $attrs['hover_icon']['innerContent'] ?? array(),
							'declarationFunction' => array( static::class, 'icon_style_declaration' ),
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .et_pb_inline_icon::before",
							'attr'     => $attrs['overlay_icon_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_image_wrapper .dsm_block_image_reveal",
							'attr'     => $attrs['block_reveal_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_image_wrapper .dsm_block_image_reveal_front",
							'attr'     => $attrs['block_reveal_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_image_wrapper .et_overlay",
							'attr'     => $attrs['hover_overlay_color']['innerContent'] ?? array(),
							'property' => 'background-color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-lightbox-custom .mfp-close",
							'attr'     => $attrs['lightbox_close_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-lightbox-custom .mfp-content",
							'attr'     => $attrs['lightbox_max_width']['innerContent'] ?? array(),
							'property' => 'max-width',
						)
					),
					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_block_reveal_image_wrapper",
							'attr'     => self::get_align_inner_content_for_styles( $attrs ),
							'property' => 'text-align',
						)
					),
				),
			)
		);
	}

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

		$elements->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

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

	/**
	 * @param string $anim Animation slug.
	 * @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';
	}

	/**
	 * Render module HTML output.
	 *
	 * @param array<string, mixed> $attrs Attributes.
	 * @param string               $content Content.
	 * @param \WP_Block            $block Block.
	 * @param mixed                $elements Elements API.
	 */
	public static function render_callback( $attrs, $content, $block, $elements ) {
		unset( $content );

		$src_raw = $attrs['src']['innerContent']['desktop']['value'] ?? '';

		$src = self::resolve_upload_src_string( $src_raw );
		if ( '' === $src ) {
			$src     = self::PLACEHOLDER_IMAGE_SRC;
			$src_raw = array(
				'src' => self::PLACEHOLDER_IMAGE_SRC,
			);
		}

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

		$use_overlay            = (string) ( $attrs['use_overlay']['innerContent']['desktop']['value'] ?? 'off' );
		$overlay_on_hover       = (string) ( $attrs['overlay_on_hover']['innerContent']['desktop']['value'] ?? 'on' );
		$use_icon               = (string) ( $attrs['use_icon']['innerContent']['desktop']['value'] ?? 'on' );
		$show_in_lightbox       = (string) ( $attrs['show_in_lightbox']['innerContent']['desktop']['value'] ?? 'off' );
		$show_lightbox_other_img = (string) ( $attrs['show_lightbox_other_img']['innerContent']['desktop']['value'] ?? 'off' );
		$other_src_raw          = $attrs['show_lightbox_other_img_src']['innerContent']['desktop']['value'] ?? '';
		$show_lightbox_other_img_src = self::resolve_upload_src_string( $other_src_raw );
		$use_video_popup        = (string) ( $attrs['use_video_popup']['innerContent']['desktop']['value'] ?? 'off' );
		$url                    = (string) ( $attrs['url']['innerContent']['desktop']['value'] ?? '' );
		$url_new_window         = (string) ( $attrs['url_new_window']['innerContent']['desktop']['value'] ?? 'off' );
		$force_fullwidth        = (string) ( $attrs['force_fullwidth']['innerContent']['desktop']['value'] ?? 'off' );

		if ( 'on' === $show_in_lightbox || 'on' === $use_video_popup ) {
			if ( function_exists( 'dsm_ensure_registered_script_dependency' ) && function_exists( 'dsm_get_active_magnific_popup_script_handle' ) ) {
				dsm_ensure_registered_script_dependency(
					'dsm-block-reveal-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-reveal-library' );
		wp_enqueue_script( 'dsm-block-reveal-image' );

		$order_index = isset( $block->parsed_block['orderIndex'] ) ? (string) $block->parsed_block['orderIndex'] : '0';

		$lightbox_image_url = ( 'on' === $show_lightbox_other_img && '' !== $show_lightbox_other_img_src )
			? $show_lightbox_other_img_src
			: $src;

		$img_html = self::render_block_reveal_image_img_markup( $src, $src_raw );

		$reveal_span = '<span class="dsm_block_image_reveal"></span>';

		$overlay_html = '';
		if ( 'on' === $use_overlay ) {
			$icon_class   = 'on' === $use_icon ? 'et_pb_inline_icon' : 'dsm_block_reveal_image_icon_empty';
			$overlay_html = sprintf( '<span class="et_overlay %s"></span>', esc_attr( $icon_class ) );
		}

		$wrap_classes = sprintf(
			'et_pb_image_wrap dsm_block_image_%1$s dsm_block_image_reveal_%1$s',
			esc_attr( $block_reveal_animation )
		);

		$image_stack = sprintf(
			'<span class="%1$s" 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">%6$s%7$s%8$s</span>',
			esc_attr( $wrap_classes ),
			esc_attr( $block_reveal_animation ),
			esc_attr( $block_reveal_color ),
			esc_attr( $block_reveal_delay ),
			esc_attr( $block_reveal_viewport ),
			$img_html,
			$reveal_span,
			$overlay_html
		);

		$inner_markup = '';

		if ( 'off' === $show_in_lightbox && 'off' === $use_video_popup ) {
			$inner_markup = $image_stack;
		} elseif ( 'on' === $show_in_lightbox && 'off' === $use_video_popup ) {
			$inner_markup = sprintf(
				'<a class="dsm-image-lightbox" href="%1$s" data-image-url="%2$s" data-order-class="%3$s">%4$s</a>',
				self::esc_img_src_for_attribute( $src ),
				self::esc_img_src_for_attribute( $lightbox_image_url ),
				esc_attr( 'dsm_block_reveal_image_' . $order_index ),
				$image_stack
			);
		} elseif ( 'off' === $show_in_lightbox && 'on' === $use_video_popup && '' !== $url ) {
			$target_attr = 'on' === $url_new_window ? ' target="_blank" rel="noopener noreferrer"' : '';
			$inner_markup = sprintf(
				'<a href="%1$s" class="dsm-video-lightbox"%2$s data-order-class="%3$s">%4$s</a>',
				esc_url( $url ),
				$target_attr,
				esc_attr( 'dsm_block_reveal_image_' . $order_index ),
				$image_stack
			);
		} else {
			$inner_markup = $image_stack;
		}

		$wrapper_classes = array( 'dsm_block_reveal_image_wrapper' );
		if ( 'on' === $use_overlay ) {
			$wrapper_classes[] = 'et_pb_has_overlay';
			if ( 'off' === $overlay_on_hover ) {
				$wrapper_classes[] = 'dsm_block_reveal_image_overlay_off';
			}
		}

		$module_classes = array( 'dsm_block_reveal_image' );
		if ( 'on' === $force_fullwidth ) {
			$module_classes[] = 'dsm_br_img_force_fullwidth';
		}

		$container = sprintf(
			'<div class="%1$s">%2$s</div>',
			esc_attr( implode( ' ', $wrapper_classes ) ),
			$inner_markup
		);

		$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'   => implode( ' ', $module_classes ),
				'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,
			)
		);
	}
}
