<?php
/**
 * Module: JetEngine Listing .
 */
namespace Jet_Engine_Divi_Views\Components\Jet_Engine\Modules;

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

use ET\Builder\Framework\DependencyManagement\Interfaces\DependencyInterface;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;
use Jet_Engine_Divi_Views\Module_Render;
use ET\Builder\FrontEnd\Module\Style;
use Jet_Engine_Divi_Views\Utils;

class Listing_Grid implements DependencyInterface {

	/**
	 * Try to extract module CSS ID from Divi htmlAttributes payload.
	 *
	 * @param mixed $html_attributes Divi htmlAttributes payload.
	 *
	 * @return string
	 */
	private static function get_divi_css_id( $html_attributes ) {

		$normalize = static function( $value ) {
			if ( ! is_scalar( $value ) ) {
				return '';
			}

			$value = trim( (string) $value );

			if ( '' === $value || 'false' === strtolower( $value ) || '0' === $value ) {
				return '';
			}

			return $value;
		};

		$find_id = static function( $value ) use ( &$find_id, $normalize ) {

			if ( is_array( $value ) ) {

				foreach ( [ 'id', 'value', 'default' ] as $priority_key ) {
					if ( array_key_exists( $priority_key, $value ) ) {
						$found = $find_id( $value[ $priority_key ] );

						if ( $found ) {
							return $found;
						}
					}
				}

				foreach ( $value as $nested_key => $nested_value ) {
					if ( in_array( $nested_key, [ 'desktop', 'tablet', 'phone', 'hover', 'value', 'default', 'id' ], true ) ) {
						$found = $find_id( $nested_value );

						if ( $found ) {
							return $found;
						}
					}
				}
			}

			return $normalize( $value );
		};

		return $find_id( $html_attributes );
	}

	/**
	 * Duplicate default JetSmartFilters provider data under alternative query ID.
	 *
	 * @param string $query_id Alternative query ID.
	 *
	 * @return void
	 */
	private static function duplicate_jsf_provider_state( $query_id ) {

		if ( ! $query_id || ! function_exists( 'jet_smart_filters' ) ) {
			return;
		}

		$provider_id = 'jet-engine';

		$default_queries = jet_smart_filters()->query->get_default_queries();
		$default_query   = $default_queries[ $provider_id ]['default'] ?? null;

		if ( ! empty( $default_query ) ) {
			jet_smart_filters()->query->store_provider_default_query( $provider_id, $default_query, $query_id, true );
		}

		$provider_settings = jet_smart_filters()->providers->get_provider_settings();
		$default_settings  = $provider_settings[ $provider_id ]['default'] ?? null;

		if ( ! empty( $default_settings ) ) {
			if ( ! empty( $default_settings['_element_id'] ) ) {
				$default_settings['_element_id'] = $query_id;
			}

			jet_smart_filters()->providers->add_provider_settings( $provider_id, $default_settings, $query_id );
		}

		$query_props   = jet_smart_filters()->query->get_query_props();
		$default_props = $query_props[ $provider_id ]['default'] ?? null;

		if ( ! empty( $default_props ) ) {
			jet_smart_filters()->query->set_props( $provider_id, $default_props, $query_id );
		}
	}

	/**
	 * Module's style components.
	 *
	 * This function is equivalent of JS function ModuleStyles located in
	 * module/styles.tsx.
	 *
	 * @since ??
	 *
	 * @param array $args {
	 *     An array of arguments.
	 *
	 *      @type string $id                Module ID. In VB, the ID of module is UUIDV4. In FE, the ID is order index.
	 *      @type string $name              Module name.
	 *      @type string $attrs             Module attributes.
	 *      @type string $parentAttrs       Parent attrs.
	 *      @type string $orderClass        Selector class name.
	 *      @type string $parentOrderClass  Parent selector class name.
	 *      @type string $wrapperOrderClass Wrapper selector class name.
	 *      @type string $settings          Custom settings.
	 *      @type string $state             Attributes state.
	 *      @type string $mode              Style mode.
	 *      @type ModuleElements $elements  ModuleElements instance.
	 * }
	 */
	public static function module_styles( $args ) {

		$elements                    = $args['elements'];
		$settings                    = $args['settings'] ?? [];
		$default_printed_style_attrs = $args['defaultPrintedStyleAttrs'] ?? [];
		$attrs                       = $args['attrs'] ?? [];
		$order_class                 = $args['orderClass'] ?? '';

			$selectors = [
				'grid'                  => "{$order_class} .jet-listing-grid",
				'items'                 => "{$order_class} :is(.jet-listing-grid > .jet-listing-grid__items, .jet-listing-grid > [data-slider_options] > .jet-listing-grid__items > .slick-list > .slick-track, .jet-listing-grid > .jet-listing-grid__scroll-slider > .jet-listing-grid__items) > .jet-listing-grid__item",
				'items_wrap'            => "{$order_class} :is(.jet-listing-grid, .jet-listing-grid > [data-slider_options], .jet-listing-grid > .jet-listing-grid__scroll-slider) > .jet-listing-grid__items",
				'collapse_gap'          => "{$order_class} .jet-listing-grid__items.grid-collapse-gap",
				'loader'                => "{$order_class} .jet-listing-grid__loader",
			'loader_text'           => "{$order_class} .jet-listing-grid__loader-text",
			'slider_icon'           => "{$order_class} .jet-listing-grid__slider-icon",
			'slider_icon_hover'     => "{$order_class} .jet-listing-grid__slider-icon:hover",
			'slider_icon_svg_path'  => "{$order_class} .jet-listing-grid__slider-icon svg path",
			'slider_icon_path_hover'=> "{$order_class} .jet-listing-grid__slider-icon:hover svg path",
			'dots'                  => "{$order_class} .jet-listing-grid__slider .jet-slick-dots li",
			'dots_hover'            => "{$order_class} .jet-listing-grid__slider .jet-slick-dots li:hover",
			'dots_active'           => "{$order_class} .jet-listing-grid__slider .jet-slick-dots li.slick-active",
			'not_found'             => "{$order_class} .jet-listing-not-found",
		];

		$loader_text_typography = Utils\extract_typography_attrs( $attrs['module']['decoration']['loaderTextTypography'] ?? null );
		$loader_text_typography_styles = Utils\build_typography_styles(
			$selectors['loader_text'],
			$loader_text_typography
		);

			$not_found_typography = Utils\extract_typography_attrs( $attrs['module']['decoration']['notFoundTypography'] ?? null );
			$not_found_typography_styles = Utils\build_typography_styles(
				$selectors['not_found'],
				$not_found_typography
			);
			$horizontal_gap = $attrs['module']['decoration']['horizontalGap'] ?? null;
			$horizontal_gap_half = self::map_responsive_value( $horizontal_gap, static function( $value ) {
				return "calc({$value} / 2)";
			} );
			$horizontal_gap_negative_half = self::map_responsive_value( $horizontal_gap, static function( $value ) {
				return "calc(-{$value} / 2)";
			} );
			$horizontal_gap_width = self::map_responsive_value( $horizontal_gap, static function( $value ) {
				return "calc(100% + {$value})";
			} );
			$vertical_gap = $attrs['module']['decoration']['verticalGap'] ?? null;
			$vertical_gap_half = self::map_responsive_value( $vertical_gap, static function( $value ) {
				return "calc({$value} / 2)";
			} );
			$vertical_gap_negative_half = self::map_responsive_value( $vertical_gap, static function( $value ) {
				return "calc(-{$value} / 2)";
			} );
			$arrows_z_index = self::map_responsive_value( $attrs['module']['decoration']['arrowsZIndex'] ?? null, static function( $value ) {
				return preg_replace( '/[^0-9.\-]/', '', (string) $value );
			} );

		Style::add(
			[
				'id'            => $args['id'],
				'name'          => $args['name'],
				'orderIndex'    => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles'        => [
					$elements->style(
						[
							'attrName'   => 'module',
							'styleProps' => [
								'defaultPrintedStyleAttrs' => $default_printed_style_attrs['module']['decoration'] ?? [],
								'disabledOn'               => [
									'disabledModuleVisibility' => $settings['disabledModuleVisibility'] ?? null,
								],
								'advancedStyles'           => array_merge(
									$loader_text_typography_styles,
									$not_found_typography_styles,
									[
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['grid'],
													'attr'     => $attrs['module']['decoration']['horizontalGap'] ?? [],
													'property' => '--jet-listing-grid-h-gap',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items'],
													'attr'     => $horizontal_gap_half,
													'property' => 'padding-left',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items'],
													'attr'     => $horizontal_gap_half,
													'property' => 'padding-right',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items_wrap'],
													'attr'     => $horizontal_gap_negative_half,
													'property' => 'margin-left',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items_wrap'],
													'attr'     => $horizontal_gap_negative_half,
													'property' => 'margin-right',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items_wrap'],
													'attr'     => $horizontal_gap_width,
													'property' => 'width',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['grid'],
													'attr'     => $attrs['module']['decoration']['verticalGap'] ?? [],
													'property' => '--jet-listing-grid-v-gap',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items'],
													'attr'     => $vertical_gap_half,
													'property' => 'padding-top',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['items'],
													'attr'     => $vertical_gap_half,
													'property' => 'padding-bottom',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['collapse_gap'],
													'attr'     => $vertical_gap_negative_half,
													'property' => 'margin-top',
												],
											],
											[
												'componentName' => 'divi/common',
												'props'         => [
													'selector' => $selectors['collapse_gap'],
													'attr'     => $vertical_gap_negative_half,
													'property' => 'margin-bottom',
												],
											],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['grid'],
												'attr'     => $attrs['module']['decoration']['centerModePadding'] ?? [],
												'property' => '--jet-listing-grid-center-padding',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['grid'],
												'attr'     => $attrs['module']['decoration']['arrowsBoxSize'] ?? [],
												'property' => '--jet-listing-grid-arrow-box-size',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['grid'],
												'attr'     => $attrs['module']['decoration']['arrowsSize'] ?? [],
												'property' => '--jet-listing-grid-arrow-size',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['grid'],
												'attr'     => $attrs['module']['decoration']['dotsSize'] ?? [],
												'property' => '--jet-listing-grid-dots-size',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['grid'],
												'attr'     => $attrs['module']['decoration']['dotsGap'] ?? [],
												'property' => '--jet-listing-grid-dots-gap',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['loader'],
												'attr'     => $attrs['module']['decoration']['loaderColor'] ?? [],
												'property' => '--spinner-color',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['loader'],
												'attr'     => $attrs['module']['decoration']['loaderSize'] ?? [],
												'property' => '--spinner-size',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['loader_text'],
												'attr'     => $attrs['module']['decoration']['loaderTextColor'] ?? [],
												'property' => 'color',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon'],
												'attr'     => $arrows_z_index,
												'property' => 'z-index',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon'],
												'attr'     => $attrs['module']['decoration']['arrowColor'] ?? [],
												'property' => 'color',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon_svg_path'],
												'attr'     => $attrs['module']['decoration']['arrowColor'] ?? [],
												'property' => 'fill',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon'],
												'attr'     => $attrs['module']['decoration']['arrowBgColor'] ?? [],
												'property' => 'background',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon_hover'],
												'attr'     => $attrs['module']['decoration']['arrowColorHover'] ?? [],
												'property' => 'color',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon_path_hover'],
												'attr'     => $attrs['module']['decoration']['arrowColorHover'] ?? [],
												'property' => 'fill',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['slider_icon_hover'],
												'attr'     => $attrs['module']['decoration']['arrowBgColorHover'] ?? [],
												'property' => 'background',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['dots'],
												'attr'     => $attrs['module']['decoration']['dotsBgColor'] ?? [],
												'property' => 'background',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['dots_hover'],
												'attr'     => $attrs['module']['decoration']['dotsBgColorHover'] ?? [],
												'property' => 'background',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['dots_active'],
												'attr'     => $attrs['module']['decoration']['dotsBgColorActive'] ?? [],
												'property' => 'background',
											],
										],
										[
											'componentName' => 'divi/common',
											'props'         => [
												'selector' => $selectors['not_found'],
												'attr'     => $attrs['module']['decoration']['notFoundColor'] ?? [],
												'property' => 'color',
											],
										],
									]
								),
							],
						]
					),
				],
			]
		);
	}

	/**
	 * Set script data of used module options.
	 *
	 * @since ??
	 *
	 * @param array $args {
	 *   Array of arguments.
	 *
	 *   @type string $id       Module id.
	 *   @type string $selector Module selector.
	 *   @type array  $attrs    Module attributes.
	 * }
	 */
		public static function module_script_data( $args ) {
		}

		/**
		 * Map responsive attr values and keep breakpoint structure.
		 *
		 * @param mixed    $attr
		 * @param callable $map
		 *
		 * @return array|null
		 */
		private static function map_responsive_value( $attr, $map ) {
			if ( empty( $attr ) ) {
				return null;
			}

			$breakpoints = [ 'desktop', 'tablet', 'phone' ];
			$has_bp      = false;

			foreach ( $breakpoints as $bp ) {
				if ( isset( $attr[ $bp ] ) ) {
					$has_bp = true;
					break;
				}
			}

			if ( $has_bp ) {
				$out = $attr;

				foreach ( $breakpoints as $bp ) {
					if ( ! isset( $attr[ $bp ] ) ) {
						continue;
					}

					$val = $attr[ $bp ]['value'] ?? $attr[ $bp ]['default'] ?? null;

					if ( null === $val || '' === $val ) {
						continue;
					}

					$mapped = $map( (string) $val );
					$out[ $bp ]['value']   = $mapped;
					$out[ $bp ]['default'] = $mapped;
				}

				return $out;
			}

			$val = $attr['value'] ?? $attr['default'] ?? null;

			if ( null === $val || '' === $val ) {
				return $attr;
			}

			$mapped          = $map( (string) $val );
			$attr['value']   = $mapped;
			$attr['default'] = $mapped;

			return $attr;
		}

	/**
	 * Module classnames function for Dynamic module.
	 *
	 * This function is equivalent of JS function moduleClassnames located in
	 * src/components/dynamic-module/module-classnames.ts.
	 *
	 * @since ??
	 *
	 * @param array $args {
	 *     An array of arguments.
	 *
	 *     @type object $classnamesInstance Instance of ET\Builder\Packages\Module\Layout\Components\Classnames.
	 *     @type array  $attrs              Block attributes data that being rendered.
	 * }
	 */
	public static function module_classnames( $args ) {
	}

	/**
	 * Dynamic module render callback which outputs server side rendered HTML on the Front-End.
	 *
	 * @since ??
	 *
	 * @param array          $attrs    Block attributes that were saved by VB.
	 * @param string         $content  Block content.
	 * @param \WP_Block      $block    Parsed block object that being rendered.
	 * @param ModuleElements $elements ModuleElements instance.
	 *
	 * @return string HTML rendered of module.
	 */
	public static function render_callback( $attrs, $content, $block, $elements ) {

		$prepared_attrs = [];

		foreach ( $attrs as $key => $value ) {
			if ( isset( $value['innerContent']['desktop']['value'] ) ) {
				$prepared_attrs[ $key ] = $value['innerContent']['desktop']['value'];
			}
		}

		$toggle_keys = array(
			'is_archive_template',
			'use_random_posts_num',
			'is_masonry',
			'use_custom_post_types',
			'equal_columns_height',
			'custom_query',
			'lazy_load',
			'use_load_more',
			'loader_spinner',
			'carousel_enabled',
			'arrows',
			'dots',
			'autoplay',
			'pause_on_hover',
			'infinite',
			'center_mode',
			'scroll_slider_enabled',
			'terms_hide_empty',
			'inject_alternative_items',
		);

		foreach ( $toggle_keys as $toggle_key ) {
			if ( ! isset( $prepared_attrs[ $toggle_key ] ) ) {
				continue;
			}

			$value = $prepared_attrs[ $toggle_key ];

			if ( is_bool( $value ) ) {
				continue;
			}

			if ( in_array( $value, array( 'on', 'true', '1', 1, true, 'yes' ), true ) ) {
				$prepared_attrs[ $toggle_key ] = true;
			} elseif ( in_array( $value, array( 'off', 'false', '0', 0, false, 'no', '' ), true ) ) {
				$prepared_attrs[ $toggle_key ] = false;
			}
		}

		$normalize_element_id = static function( $value ) {
			if ( ! is_scalar( $value ) ) {
				return '';
			}

			$value = (string) $value;

			if ( '' === $value || 'false' === strtolower( $value ) || '0' === $value ) {
				return '';
			}

			return $value;
		};

		$block_id = $normalize_element_id( $prepared_attrs['_block_id'] ?? '' );

		if ( ! $block_id ) {
			$block_id = wp_unique_id( 'jet-grid-' );
		}

		$prepared_attrs['_block_id'] = $block_id;

		$element_id = $normalize_element_id( $prepared_attrs['_element_id'] ?? '' );
		$prepared_attrs['_element_id'] = $element_id;

		if ( empty( $prepared_attrs['lisitng_id'] ) ) {
			return 'Please select a base listing item';
		}

		$render = self::get_jet_engine_render_instance( $prepared_attrs );

		$divi_module_render = new Module_Render();

		$content = $divi_module_render->render_content(
			$render,
			$attrs,
			$block,
			$elements,
			self::class
		);

		if ( ! $element_id ) {
			$divi_css_id = self::get_divi_css_id( $attrs['module']['advanced']['htmlAttributes'] ?? [] );

			if ( $divi_css_id ) {
				self::duplicate_jsf_provider_state( $divi_css_id );
			}
		}

		$wrapper_class        = 'jet-listing-grid--blocks';
		$element_id           = esc_attr( $prepared_attrs['_block_id'] );
		$widget_settings_json = esc_attr( wp_json_encode( $prepared_attrs ) );

		return sprintf(
			'<div class="%1$s" data-element-id="%2$s" data-widget-settings="%3$s" data-listing-type="blocks" data-is-block="jet-engine/listing-grid">%4$s</div>',
			esc_attr( $wrapper_class ),
			$element_id,
			$widget_settings_json,
			$content
		);
	}

	/**
	 * Get JetEngine render class instance
	 */
	public static function get_jet_engine_render_instance( $attributes ) {
		return jet_engine()->listings->get_render_instance( self::get_jet_engine_render_name(), $attributes );
	}

	/**
	 * Return name of JetEngine renderer for current module
	 */
	public static function get_jet_engine_render_name() {
		return 'listing-grid';
	}

	/**
	 * Loads `DynamicField` and registers Front-End render callback and REST API Endpoints.
	 *
	 * @since ??
	 *
	 * @return void
	 */
	public function load() {

		$module_json_folder_path = JET_ENGINE_DIVI_VIEWS_MODULES_JSON_PATH . 'listing-grid/';

		add_action(
			'init',
			function() use ( $module_json_folder_path ) {
				ModuleRegistration::register_module(
					$module_json_folder_path,
					[
						'render_callback' => [ Listing_Grid::class, 'render_callback' ],
					]
				);
			}
		);
	}
}
