<?php

/**
 * Breadcrumbs 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\Module\Options\Text\TextStyle;
use ET\Builder\Packages\StyleLibrary\Utils\StyleDeclarations;
use ET\Builder\Packages\Module\Options\Css\CssStyle;
use DiviSupreme\Includes\Divi5Conversion\ConversionAttrs;
use DiviSupreme\Extensions\IconLibraries\LegacyModuleIconStyles;


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

	/**
	 * Register module.
	 * `DependencyInterface` interface ensures class method name `load()` is executed for initialization.
	 */
	public function load()
	{
		add_action('init', array(self::class, 'register_module'));
	}

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

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

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

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

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



	public static function align_style_declaration($params)
	{
		$align = self::extract_align_choice_from_style_params($params);
		$justify = self::align_choice_to_justify_content($align);

		$style_declarations = new StyleDeclarations(
			array(
				'returnType' => 'string',
				'important' => array(
					'justify-content' => true,
				),
			)
		);

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

		return $style_declarations->value();
	}

	/**
	 * Alignment value passed into CommonStyle declaration callbacks (string or nested shape).
	 *
	 * @param array<string, mixed> $params Params from CommonStyle.
	 * @return string Raw alignment slug (may be empty).
	 */
	private static function extract_align_choice_from_style_params(array $params)
	{
		$raw = $params['attrValue'] ?? null;

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

		if (is_array($raw) && isset($raw['value']) && is_string($raw['value'])) {
			return $raw['value'];
		}

		$attr = $params['attr'] ?? array();

		if (is_array($attr) && isset($attr['desktop']['value']) && is_string($attr['desktop']['value'])) {
			return $attr['desktop']['value'];
		}

		return '';
	}

	/**
	 * Whitelist alignment slugs from ButtonOptionsContainer / alignmentOptions.
	 *
	 * @param string $align Raw value.
	 * @return string
	 */
	private static function sanitize_align_choice($align)
	{
		$allowed = array('left', 'center', 'right', 'flex-start', 'flex-end');

		if ('' === $align || ! is_string($align)) {
			return 'left';
		}

		return in_array($align, $allowed, true) ? $align : 'left';
	}

	/**
	 * Map alignment option to CSS justify-content.
	 *
	 * @param string $align Sanitized slug.
	 * @return string
	 */
	private static function align_choice_to_justify_content($align)
	{
		$align = self::sanitize_align_choice($align);

		$map = array(
			'left' => 'flex-start',
			'center' => 'center',
			'right' => 'flex-end',
			'flex-start' => 'flex-start',
			'flex-end' => 'flex-end',
		);

		return $map[$align] ?? 'flex-start';
	}

	/**
	 * Render module style.
	 *
	 * @param array $args - Arguments.
	 */
	public static function module_styles($args)
	{
		$attrs = $args['attrs'] ?? array();
		// Deep copy so normalize_range_attr does not mutate stored layout attrs.
		$style_attrs = is_array($attrs) && array() !== $attrs
			? json_decode(wp_json_encode($attrs), true)
			: array();
		ConversionAttrs::normalize_range_attr($style_attrs, 'items_bottom', '10px');
		ConversionAttrs::normalize_range_attr($style_attrs, 'separator_bottom', '10px');
		ConversionAttrs::normalize_range_attr($style_attrs, 'current_bottom', '10px');
		$elements = $args['elements'];

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

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

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

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

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

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

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

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

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

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

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

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_separator_icon::before",
							'attr' => $attrs['separator_icon']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'separator_style_declaration'),
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_breadcrumbs_item .dsm_home_icon::before",
							'attr' => $attrs['home_icon']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'home_icon_style_declaration'),
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_breadcrumbs",
							'attr' => $attrs['align']['innerContent'] ?? array(),
							'declarationFunction' => array(static::class, 'align_style_declaration'),
						)
					),

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_breadcrumbs_separator",
							'attr' => $style_attrs['separator_bottom']['innerContent'] ?? array(),
							'property' => 'margin-bottom',
						)
					),

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_breadcrumbs_item.dsm_breadcrumbs_item_current",
							'attr' => $attrs['current_bottom']['innerContent'] ?? array(),
							'property' => 'margin-bottom',
						)
					),

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

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .dsm_breadcrumbs_item:not(.dsm_breadcrumbs_item_current)",
							'attr' => $style_attrs['items_bottom']['innerContent'] ?? array(),
							'property' => 'margin-bottom',
						)
					),
				),
			)
		);
	}

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

	/**
	 * Build breadcrumb segments for the current query (home, singular, archives, taxonomies, author, search, 404).
	 *
	 * @param array $args {
	 *     Optional. Array of breadcrumb arguments.
	 *
	 *     @type string $home_url  Custom URL for the home page link. Default is home_url().
	 *     @type string $home_text Custom text for the home page link. Default is 'Home'.
	 * }
	 *
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function prepare_breadcrumbs_data($args = array())
	{
		$default_args = array(
			'home_url' => home_url(),
			'home_text' => __('Home', 'dsm-supreme-modules-pro-for-divi'),
		);
		$args = wp_parse_args($args, $default_args);

		// Static front page: only the home label as the current crumb.
		if (is_front_page()) {
			return array(
				array(
					'text' => $args['home_text'],
					'url' => '',
				),
			);
		}

		$trail = array();
		$trail[] = self::get_home_breadcrumb($args);

		if (is_singular()) {
			$post = self::get_singular_post_for_context();
			if ($post instanceof \WP_Post) {
				$trail = array_merge($trail, self::get_singular_trail($post));
			}
		} elseif (is_home()) {
			$trail = array_merge($trail, self::get_posts_page_trail());
		} elseif (is_category() || is_tag() || is_tax()) {
			$trail = array_merge($trail, self::get_taxonomy_term_trail(get_queried_object()));
		} elseif (is_post_type_archive()) {
			$trail = array_merge($trail, self::get_archive_breadcrumbs());
		} elseif (is_date()) {
			$trail = array_merge($trail, self::get_date_archive_trail());
		} elseif (is_author()) {
			$trail[] = self::get_author_breadcrumb();
		} elseif (is_search()) {
			$trail[] = self::get_search_breadcrumb();
		} elseif (is_404()) {
			$trail[] = self::get_404_breadcrumb();
		}

		/**
		 * Filter breadcrumb items before render.
		 *
		 * @param array<int, array{text: string, url: string}> $trail Breadcrumb segments after home.
		 * @param array<string, mixed>                         $args  Parsed args (home_url, home_text).
		 */
		return apply_filters('dsm_breadcrumbs_trail', $trail, $args);
	}

	/**
	 * Resolve the post for singular templates (queried object, then global $post).
	 *
	 * @return \WP_Post|null
	 */
	private static function get_singular_post_for_context()
	{
		$queried = get_queried_object();
		if ($queried instanceof \WP_Post) {
			return $queried;
		}

		global $post;
		if ($post instanceof \WP_Post) {
			return $post;
		}

		return null;
	}

	/**
	 * Blog posts index title when a static front page is set (is_home && ! is_front_page).
	 *
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function get_posts_page_trail()
	{
		$posts_page_id = (int) get_option('page_for_posts');
		if ($posts_page_id > 0) {
			return array(
				array(
					'text' => get_the_title($posts_page_id),
					'url' => '',
				),
			);
		}

		return array();
	}

	/**
	 * Singular trail: pages (ancestor chain), posts (first category + ancestor terms), CPT (archive link + parent posts), then title.
	 *
	 * Custom post types use the post type archive when registered, not automatic taxonomy trails.
	 *
	 * @param \WP_Post $post Current post.
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function get_singular_trail(\WP_Post $post)
	{
		$breadcrumbs = array();

		if ('page' === $post->post_type) {
			if ($post->post_parent) {
				$breadcrumbs = array_merge($breadcrumbs, self::populate_parent_posts_chain((int) $post->ID));
			}
		} elseif ('post' === $post->post_type) {
			$categories = get_the_category($post->ID);
			if (! empty($categories) && isset($categories[0]) && $categories[0] instanceof \WP_Term) {
				$category = $categories[0];
				if (! empty($category->parent)) {
					$breadcrumbs = array_merge($breadcrumbs, self::populate_category_ancestors((int) $category->term_id));
				}
				$breadcrumbs[] = array(
					'text' => $category->name,
					'url' => get_category_link($category->term_id),
				);
			}
		} else {
			$post_type = $post->post_type;
			$post_type_obj = get_post_type_object($post_type);
			$post_type_label = '';
			if ($post_type_obj instanceof \WP_Post_Type) {
				$post_type_label = $post_type_obj->labels->name ?? $post_type_obj->label;
			}
			$archive_link = get_post_type_archive_link($post_type);
			if ($archive_link && '' !== $post_type_label) {
				$breadcrumbs[] = array(
					'text' => $post_type_label,
					'url' => $archive_link,
				);
			}
			if ($post->post_parent) {
				$breadcrumbs = array_merge($breadcrumbs, self::populate_parent_posts_chain((int) $post->ID));
			}
		}

		$breadcrumbs[] = array(
			'text' => get_the_title($post),
			'url' => '',
		);

		return $breadcrumbs;
	}

	/**
	 * Parent page chain from root to immediate parent (published only).
	 *
	 * @param int $post_id Current post ID.
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function populate_parent_posts_chain($post_id)
	{
		$chain = array();
		$parent_id = wp_get_post_parent_id($post_id);

		while ($parent_id) {
			$parent = get_post($parent_id);
			if (! $parent instanceof \WP_Post || 'publish' !== $parent->post_status) {
				break;
			}
			$chain[] = array(
				'text' => get_the_title($parent),
				'url' => get_permalink($parent),
			);
			$parent_id = (int) $parent->post_parent;
		}

		return array_reverse($chain);
	}

	/**
	 * Category ancestors from root to parent of $term_id (excludes $term_id).
	 *
	 * @param int $term_id Category term ID.
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function populate_category_ancestors($term_id)
	{
		$out = array();
		$ancestor_ids = get_ancestors($term_id, 'category');
		if (empty($ancestor_ids)) {
			return $out;
		}
		$ancestor_ids = array_reverse($ancestor_ids);
		foreach ($ancestor_ids as $ancestor_id) {
			$cat = get_category((int) $ancestor_id);
			if ($cat && ! is_wp_error($cat)) {
				$out[] = array(
					'text' => $cat->name,
					'url' => get_category_link($cat->term_id),
				);
			}
		}

		return $out;
	}

	/**
	 * Term archive trail: ancestors + current term.
	 *
	 * @param mixed $term Queried term object.
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function get_taxonomy_term_trail($term)
	{
		if (! $term instanceof \WP_Term) {
			return array();
		}

		$breadcrumbs = array();

		if (! empty($term->parent)) {
			$ancestor_ids = get_ancestors($term->term_id, $term->taxonomy);
			$ancestor_ids = array_reverse($ancestor_ids);
			foreach ($ancestor_ids as $ancestor_id) {
				$ancestor = get_term((int) $ancestor_id, $term->taxonomy);
				if ($ancestor && ! is_wp_error($ancestor)) {
					$tlink = get_term_link($ancestor);
					if (! is_wp_error($tlink)) {
						$breadcrumbs[] = array(
							'text' => $ancestor->name,
							'url' => $tlink,
						);
					}
				}
			}
		}

		$breadcrumbs[] = array(
			'text' => $term->name,
			'url' => '',
		);

		return $breadcrumbs;
	}

	/**
	 * Date archives: year only; or year (link) + month; or year (link) + month (link) + day.
	 *
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function get_date_archive_trail()
	{
		$breadcrumbs = array();
		$year_text = get_the_date('Y');
		$month_text = get_the_date('F');
		$day_num = get_the_date('j');

		if (is_year()) {
			$breadcrumbs[] = array(
				'text' => $year_text,
				'url' => '',
			);
			return $breadcrumbs;
		}

		if (is_month()) {
			$y = get_the_date('Y');
			$breadcrumbs[] = array(
				'text' => $year_text,
				'url' => get_year_link($y),
			);
			$breadcrumbs[] = array(
				'text' => $month_text,
				'url' => '',
			);
			return $breadcrumbs;
		}

		if (is_day()) {
			$y = get_the_date('Y');
			$m = get_the_date('m');
			$breadcrumbs[] = array(
				'text' => $year_text,
				'url' => get_year_link($y),
			);
			$breadcrumbs[] = array(
				'text' => $month_text,
				'url' => get_month_link($y, $m),
			);
			$breadcrumbs[] = array(
				'text' => (string) $day_num,
				'url' => '',
			);
			return $breadcrumbs;
		}

		return $breadcrumbs;
	}

	/**
	 * Author archive: the queried author display name as the current crumb.
	 *
	 * @return array{text: string, url: string}
	 */
	private static function get_author_breadcrumb()
	{
		$author = get_queried_object();
		if ($author instanceof \WP_User && ! empty($author->display_name)) {
			return array(
				'text' => $author->display_name,
				'url' => '',
			);
		}

		return array(
			'text' => __('Author', 'dsm-supreme-modules-pro-for-divi'),
			'url' => '',
		);
	}

	/**
	 * Search results archive label.
	 *
	 * @return array{text: string, url: string}
	 */
	private static function get_search_breadcrumb()
	{
		return array(
			'text' => __('Search results', 'dsm-supreme-modules-pro-for-divi'),
			'url' => '',
		);
	}

	/**
	 * 404 page label.
	 *
	 * @return array{text: string, url: string}
	 */
	private static function get_404_breadcrumb()
	{
		return array(
			'text' => __('Not found', 'dsm-supreme-modules-pro-for-divi'),
			'url' => '',
		);
	}

	/**
	 * Get Home Breadcrumb.
	 *
	 * @param array $args Arguments.
	 * @return array
	 */
	private static function get_home_breadcrumb($args)
	{
		return array(
			'text' => $args['home_text'],
			'url' => $args['home_url'],
		);
	}

	/**
	 * Post type archive label (current).
	 *
	 * @return array<int, array{text: string, url: string}>
	 */
	private static function get_archive_breadcrumbs()
	{
		$post_type_obj = get_post_type_object(get_post_type());
		if (! $post_type_obj instanceof \WP_Post_Type) {
			return array();
		}

		$label = $post_type_obj->labels->name ? $post_type_obj->labels->name : $post_type_obj->label;

		return array(
			array(
				'text' => $label,
				'url' => '',
			),
		);
	}

	/**
	 * Canonical URL for the last BreadcrumbList item (current page), for optional `link itemprop="url"`.
	 *
	 * @return string Escaped URL or empty when not appropriate (e.g. 404).
	 */
	private static function get_canonical_url_for_last_list_item()
	{
		if (is_404()) {
			return '';
		}

		if (function_exists('wp_get_canonical_url')) {
			$post_id = get_queried_object_id();
			if ($post_id > 0) {
				$canonical = wp_get_canonical_url($post_id);
				if (is_string($canonical) && '' !== $canonical) {
					return esc_url($canonical);
				}
			}
		}

		if (is_front_page()) {
			return esc_url(home_url('/'));
		}

		$queried = get_queried_object();

		if ($queried instanceof \WP_Post) {
			return esc_url(get_permalink($queried));
		}

		if ($queried instanceof \WP_Term) {
			$link = get_term_link($queried);
			return is_wp_error($link) ? '' : esc_url($link);
		}

		if ($queried instanceof \WP_User) {
			return esc_url(get_author_posts_url($queried->ID));
		}

		if (is_post_type_archive()) {
			$pt = get_post_type();
			if ($pt) {
				$arch = get_post_type_archive_link($pt);
				return $arch ? esc_url($arch) : '';
			}
		}

		if (is_date()) {
			if (is_year()) {
				$y = (int) get_query_var('year');
				return $y ? esc_url(get_year_link($y)) : '';
			}
			if (is_month()) {
				$y = (int) get_query_var('year');
				$m = (int) get_query_var('monthnum');
				return ($y && $m) ? esc_url(get_month_link($y, $m)) : '';
			}
			if (is_day()) {
				$y = (int) get_query_var('year');
				$m = (int) get_query_var('monthnum');
				$d = (int) get_query_var('day');
				return ($y && $m && $d) ? esc_url(get_day_link($y, $m, $d)) : '';
			}
		}

		if (is_search()) {
			return esc_url(get_search_link());
		}

		global $wp;
		if (isset($wp->request) && is_string($wp->request) && '' !== $wp->request) {
			return esc_url(home_url(user_trailingslashit($wp->request)));
		}

		return esc_url(home_url('/'));
	}

	/**
	 * 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)
	{

		$show_home = $attrs['show_home']['innerContent']['desktop']['value'] ?? 'on';
		$home_text_raw = $attrs['home_text']['innerContent']['desktop']['value'] ?? 'Home';
		$use_custom_home_url_link = $attrs['use_custom_home_url_link']['innerContent']['desktop']['value'] ?? '';
		$home_url_link = $attrs['home_url_link']['innerContent']['desktop']['value'] ?? '';
		$home_url_link_new_window = $attrs['home_url_link_new_window']['innerContent']['desktop']['value'] ?? '';
		$show_home_icon = $attrs['show_home_icon']['innerContent']['desktop']['value'] ?? 'on';
		$show_separator = $attrs['show_separator']['innerContent']['desktop']['value'] ?? 'on';

		$home_icon_settings    = is_array( $attrs['home_icon']['innerContent']['desktop']['value'] ?? null )
			? $attrs['home_icon']['innerContent']['desktop']['value']
			: array();
		$separator_icon_settings = is_array( $attrs['separator_icon']['innerContent']['desktop']['value'] ?? null )
			? $attrs['separator_icon']['innerContent']['desktop']['value']
			: array();

		$home_url = 'on' === $use_custom_home_url_link && ! empty($home_url_link) ? $home_url_link : home_url();

		if ('on' === $show_home) {
			$home_text = is_string($home_text_raw) && '' !== trim($home_text_raw)
				? $home_text_raw
				: __('Home', 'dsm-supreme-modules-pro-for-divi');
		} else {
			$home_text = get_bloginfo('name');
		}

		$breadcrumbs_data = self::prepare_breadcrumbs_data(
			array(
				'home_url' => $home_url,
				'home_text' => $home_text,
			)
		);

		$schema_enabled = 'on' === ($attrs['enable_schema_markup']['innerContent']['desktop']['value'] ?? 'on');

		$ul_schema_attr = $schema_enabled ? ' itemscope itemtype="https://schema.org/BreadcrumbList"' : '';

		$breadcrumbs_html = sprintf(
			'<nav class="dsm_breadcrumbs_wrap" role="navigation" aria-label="%1$s">
			<ul class="dsm_breadcrumbs"%2$s>',
			esc_attr(__('Breadcrumb', 'dsm-supreme-modules-pro-for-divi')),
			$ul_schema_attr
		);

		$total_items = count($breadcrumbs_data);

		foreach ($breadcrumbs_data as $index => $item) {

			$text = esc_html($item['text']);
			$url = esc_url($item['url']);
			$position = $index + 1;

			if ($index === $total_items - 1) {
				if ($schema_enabled) {
					$canonical = self::get_canonical_url_for_last_list_item();
					$url_meta = '' !== $canonical
						? '<link itemprop="url" href="' . esc_url($canonical) . '" />'
						: '';

					$breadcrumbs_html .= sprintf(
						'<li class="dsm_breadcrumbs_item dsm_breadcrumbs_item_current" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"><span class="dsm_breadcrumbs_crumb dsm_breadcrumbs_crumb_current" itemprop="name">%s</span>%s<meta itemprop="position" content="%d" /></li>',
						$text,
						$url_meta,
						$position
					);
				} else {
					$breadcrumbs_html .= sprintf(
						'<li class="dsm_breadcrumbs_item dsm_breadcrumbs_item_current"><span class="dsm_breadcrumbs_crumb dsm_breadcrumbs_crumb_current">%s</span></li>',
						$text
					);
				}
			} else {
				if (0 === $index) {
					$class = 'on' === $show_home
						? 'dsm_breadcrumbs_item dsm_home_item dsm_home_item_custom'
						: 'dsm_breadcrumbs_item dsm_home_item dsm_home_item_default';
					$icon = 'on' === $show_home_icon
						? LegacyModuleIconStyles::render_custom_icon_host( $home_icon_settings, 'dsm_home_icon' )
						: '';
					$target = 'on' === $home_url_link_new_window ? '_blank' : '_self';
				} else {
					$class = 'dsm_breadcrumbs_item dsm_parent_item';
					$icon = '';
					$target = '_self';
				}

				$rel_attr = '_blank' === $target ? ' rel="noopener noreferrer"' : '';

				if ($schema_enabled) {
					$breadcrumbs_html .= sprintf(
						'<li class="%1$s" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"><a href="%2$s" itemprop="item" target="%3$s"%4$s><span class="dsm_home_text_wrapper">%5$s<span class="dsm_home_text" itemprop="name">%6$s</span></span></a><meta itemprop="position" content="%7$d" /></li>',
						esc_attr($class),
						$url,
						esc_attr($target),
						$rel_attr,
						$icon,
						$text,
						$position
					);
				} else {
					$breadcrumbs_html .= sprintf(
						'<li class="%1$s"><a href="%2$s" target="%3$s"%4$s><span class="dsm_home_text_wrapper">%5$s<span class="dsm_home_text">%6$s</span></span></a></li>',
						esc_attr($class),
						$url,
						esc_attr($target),
						$rel_attr,
						$icon,
						$text
					);
				}

				if ('on' === $show_separator) {
					$breadcrumbs_html .= sprintf(
						'<li class="dsm_breadcrumbs_separator" aria-hidden="true">%s</li>',
						LegacyModuleIconStyles::render_custom_icon_host( $separator_icon_settings, 'dsm_separator_icon' )
					);
				}
			}
		}

		$breadcrumbs_html .= '</ul></nav>';

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

		$module_elements = $elements->style_components(
			array(
				'attrName' => 'module',
			)
		);

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