<?php
/**
 * Star Rating 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\Module\Options\Text\TextStyle;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;

/**
 * Class that handles "Star Rating Module" module.
 */
class StarRating implements DependencyInterface
{

	/**
	 * Flex row alignment for inline display (text-align alone does not move flex items).
	 *
	 * @param array<string, mixed> $params Style declaration params.
	 * @return string
	 */
	public static function star_rating_inline_row_alignment_declaration($params)
	{
		$raw = $params['attrValue'] ?? 'left';
		if (is_array($raw)) {
			$raw = $raw['value'] ?? 'left';
		}

		$slug = is_string($raw) ? strtolower(trim($raw)) : 'left';
		$justify = 'flex-start';
		$text_align = $slug;

		if ('center' === $slug) {
			$justify = 'center';
		} elseif ('right' === $slug) {
			$justify = 'flex-end';
		} elseif (in_array($slug, array( 'justified', 'justify' ), true)) {
			$justify = 'space-between';
			$text_align = 'left';
		}

		$style_declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
			)
		);

		$style_declarations->add('justify-content', $justify);
		$style_declarations->add('text-align', $text_align);

		return $style_declarations->value();
	}

	/**
	 * Register module.
	 * `DependencyInterface` interface ensures class method name `load()` is executed for initialization.
	 */
	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.
	 */
	public static function register_module()
	{
		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/star-rating';

		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'];

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

					TextStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr' => $attrs['module']['advanced']['text'] ?? array(),
						)
					),

					$elements->style(
						array(
							'attrName' => 'title',
						)
					),

					$elements->style(
						array(
							'attrName' => 'rating_text',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-block .dsm-star-rating-wrapper",
							'attr' => $attrs['star_alignment']['innerContent'] ?? array(),
							'property' => 'text-align',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-inline-block .dsm-star-rating-wrapper",
							'attr' => $attrs['star_alignment']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'star_rating_inline_row_alignment_declaration'),
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-star-rating",
							'attr' => $attrs['stars_size']['innerContent'] ?? array(),
							'property' => 'font-size',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-star-rating i:not(:last-of-type)",
							'attr' => $attrs['stars_gap']['innerContent'] ?? array(),
							'property' => 'margin-right',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm-star-rating, {$args['orderClass']} .dsm-star-rating i::before, {$args['orderClass']} .dsm-star-rating i::after",
							'attr' => $attrs['stars_color']['innerContent'] ?? array(),
							'property' => 'color',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-inline-block.dsm-star-title-position-left .dsm-star-rating-title",
							'attr' => $attrs['title_gap']['innerContent'] ?? array(),
							'property' => 'margin-right',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-inline-block.dsm-star-title-position-right .dsm-star-rating-title",
							'attr' => $attrs['title_gap']['innerContent'] ?? array(),
							'property' => 'margin-left',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-block.dsm-star-title-position-top .dsm-star-rating-title",
							'attr' => $attrs['title_gap']['innerContent'] ?? array(),
							'property' => 'margin-bottom',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']}.dsm-star-display-type-block.dsm-star-title-position-bottom .dsm-star-rating-title",
							'attr' => $attrs['title_gap']['innerContent'] ?? array(),
							'property' => 'margin-top',
						)
					),
				),
			)
		);
	}

	/**
	 * Build star icons HTML (parity with Divi 4 `DSM_Star_Rating::get_starrating`).
	 *
	 * @param array<string, mixed> $args Arguments.
	 * @return string
	 */
	public static function get_starrating($args = array())
	{
		$defaults = array(
			'rating_scale' => '5',
			'rating' => '5',
			'show_number' => 'off',
			'stars_schema_markup' => 'off',
		);

		$args = wp_parse_args($args, $defaults);

		$rating_scale = max(1, (int) $args['rating_scale']);
		$rating_float = (float) $args['rating'];
		$int_rating = (int) floor($rating_float);
		$output = '';

		for ($star_index = 1; $star_index <= $rating_scale; $star_index++) {
			if ($star_index <= $int_rating) {
				$output .= '<i class="dsm-star-full"></i>';
			} elseif ($int_rating + 1 === $star_index && (float) $int_rating !== $rating_float) {
				$partial = (int) round(($rating_float - $int_rating) * 10);
				$output .= '<i class="dsm-star-' . $partial . '"></i>';
			} else {
				$output .= '<i class="dsm-star-empty"></i>';
			}
		}

		// Match D4: schema markup adds microdata to the number line; else optional plain number.
		if ('on' === $args['stars_schema_markup']) {
			$output .= ' <span class="dsm-star-rating-text"><meta itemprop="worstRating" content="1">( <span itemprop="ratingValue">' . $int_rating . '</span> / <span itemprop="bestRating">' . $rating_scale . '</span> )</span>';
		} elseif ('on' === $args['show_number']) {
			$output .= ' <span class="dsm-star-rating-text"><span>( <span>' . $int_rating . '</span> / <span>' . $rating_scale . '</span> )</span>';
		}

		return $output;
	}

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

		$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'];
		$stars_display_type = $attrs['stars_display_type']['innerContent']['desktop']['value'] ?? '';
		if ('' === $stars_display_type) {
			$stars_display_type = 'inline-block';
		}

		$title_inline_position = $attrs['title_inline_position']['innerContent']['desktop']['value'] ?? 'left';
		$title_stacked_position = $attrs['title_stacked_position']['innerContent']['desktop']['value'] ?? 'bottom';
		$text_color = $attrs['module']['advanced']['text']['text']['desktop']['value']['color'] ?? 'light';

		$title_position_slug = ('inline-block' === $stars_display_type)
			? ( '' !== $title_inline_position ? $title_inline_position : 'left' )
			: ( '' !== $title_stacked_position ? $title_stacked_position : 'bottom' );

		$classnames_instance->add(
			ElementClassnames::classnames(
				array(
					'attrs' => $attrs['module']['decoration'] ?? array(),
				)
			)
		);

		$classnames_instance->add('et_pb_bg_layout_' . $text_color);
		$classnames_instance->add('dsm-star-display-type-' . $stars_display_type);
		$classnames_instance->add('dsm-star-title-position-' . $title_position_slug);
	}

	/**
	 * 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)
	{
		$title_inline_position = $attrs['title_inline_position']['innerContent']['desktop']['value'] ?? '';
		$title_stacked_position = $attrs['title_stacked_position']['innerContent']['desktop']['value'] ?? '';
		$show_number = $attrs['show_number']['innerContent']['desktop']['value'] ?? '';
		$rating_scale = $attrs['rating_scale']['innerContent']['desktop']['value'] ?? '5';
		$rating = $attrs['rating']['innerContent']['desktop']['value'] ?? '5';
		$stars_schema_markup = $attrs['stars_schema_markup']['innerContent']['desktop']['value'] ?? '';
		$stars_schema_author = $attrs['stars_schema_author']['innerContent']['desktop']['value'] ?? '';
		$stars_schema_product_item_name = $attrs['stars_schema_product_item_name']['innerContent']['desktop']['value'] ?? '';
		$stars_schema_num_of_rating = $attrs['stars_schema_num_of_rating']['innerContent']['desktop']['value'] ?? '';
		$stars_display_type = $attrs['stars_display_type']['innerContent']['desktop']['value'] ?? '';
		if ('' === $stars_display_type) {
			$stars_display_type = 'inline-block';
		}

		$title_rendered = $elements->render(
			array(
				'attrName' => 'title',
			)
		);

		$has_title = '' !== trim(wp_strip_all_tags((string) $title_rendered));

		$title_left = ('inline-block' === $stars_display_type && 'left' === $title_inline_position && $has_title)
			? $title_rendered
			: '';
		$title_right = ('inline-block' === $stars_display_type && 'right' === $title_inline_position && $has_title)
			? $title_rendered
			: '';
		$title_top = ('block' === $stars_display_type && 'top' === $title_stacked_position && $has_title)
			? $title_rendered
			: '';
		$title_bottom = ('block' === $stars_display_type && 'bottom' === $title_stacked_position && $has_title)
			? $title_rendered
			: '';

		$stars_output = self::get_starrating(
			array(
				'rating_scale' => $rating_scale,
				'rating' => $rating,
				'show_number' => $show_number,
				'stars_schema_markup' => $stars_schema_markup,
			)
		);

		$schema_author = esc_html((string) $stars_schema_author);
		$schema_product = esc_html((string) $stars_schema_product_item_name);
		$schema_count = esc_html((string) $stars_schema_num_of_rating);

		$schema_without_markup = sprintf(
			'<div class="dsm-star-rating-wrapper">
			  %2$s
			  %4$s
			 <div class="dsm-star-rating">
			   %1$s
			 </div>
			 %3$s
			 %5$s
			</div>',
			$stars_output,
			$title_left,
			$title_right,
			$title_top,
			$title_bottom
		);

		$rating_display = esc_html((string) $rating);
		$scale_display = esc_html((string) $rating_scale);

		$schema_with_markup = sprintf(
			'<div itemscope itemtype="https://schema.org/Review" class="dsm-star-rating-wrapper">
			  <div style="display:none" itemprop="itemReviewed" itemscope="" itemtype="http://schema.org/Product">
					<span itemprop="name">%7$s</span>
					<span itemprop="aggregateRating" itemscope="" itemtype="https://schema.org/AggregateRating">
						<span itemprop="ratingValue">%9$s</span>
						<span itemprop="ratingCount">%8$s</span>
					</span>
			   </div>
			  %2$s
			  %4$s
			 <div class="dsm-star-rating" itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" title="%9$s / %10$s">
			   %1$s
			 </div>
			 %3$s
			 %5$s
			   <span style="display:none" itemprop="author" itemscope="" itemtype="https://schema.org/Person">
				  	<span itemprop="name">%6$s</span>
			   </span>
			</div>',
			$stars_output,
			$title_left,
			$title_right,
			$title_top,
			$title_bottom,
			$schema_author,
			$schema_product,
			$schema_count,
			$rating_display,
			$scale_display
		);

		$inner_markup = 'off' === $stars_schema_markup ? $schema_without_markup : $schema_with_markup;

		$module_inner = HTMLUtility::render(
			array(
				'tag' => 'div',
				'attributes' => array(
					'class' => 'et_pb_module_inner',
				),
				'childrenSanitizer' => 'et_core_esc_previously',
				'children' => $inner_markup,
			)
		);

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