<?php
/**
 * Shop With Sidebar widget — query helpers & brand taxonomy resolution.
 *
 * @package storzen
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Resolve WooCommerce “brand” taxonomy (never falls back to product categories).
 *
 * Tries: user preference, then common brand taxonomies (attribute + plugins).
 *
 * @param string $preferred Slug from widget settings (e.g. pa_brand).
 * @return string Taxonomy name or empty if none registered.
 */
function storzen_resolve_brand_taxonomy( $preferred = '' ) {
	$preferred = is_string( $preferred ) ? trim( $preferred ) : '';
	if ( $preferred !== '' ) {
		$key = sanitize_key( $preferred );
		if ( taxonomy_exists( $key ) ) {
			return $key;
		}
	}

	$candidates = array( 'pa_brand', 'product_brand', 'pwb-brand', 'yith_product_brand', 'brand' );
	foreach ( $candidates as $tax ) {
		if ( taxonomy_exists( $tax ) ) {
			return $tax;
		}
	}

	return '';
}

/**
 * Normalize a list of taxonomy term slugs (array, comma string, or single string).
 *
 * @param mixed $value Raw value.
 * @return array<int,string> Unique non-empty slugs.
 */
function storzen_shop_sidebar_normalize_slug_list( $value ) {
	if ( is_array( $value ) ) {
		$out = array();
		foreach ( $value as $v ) {
			$s = sanitize_title( (string) $v );
			if ( $s !== '' ) {
				$out[] = $s;
			}
		}
		return array_values( array_unique( $out ) );
	}
	if ( is_string( $value ) && $value !== '' ) {
		$parts = preg_split( '/[,\s]+/', $value, -1, PREG_SPLIT_NO_EMPTY );
		$out   = array();
		foreach ( $parts as $p ) {
			$s = sanitize_title( $p );
			if ( $s !== '' ) {
				$out[] = $s;
			}
		}
		return array_values( array_unique( $out ) );
	}
	if ( is_scalar( $value ) && (string) $value !== '' ) {
		$s = sanitize_title( (string) $value );
		return $s !== '' ? array( $s ) : array();
	}
	return array();
}

/**
 * Merge legacy single-slug keys into plural array keys for filters.
 *
 * @param array<string,mixed> $filters Filters.
 * @return array<string,mixed>
 */
function storzen_shop_sidebar_coerce_filter_arrays( array $filters ) {
	$pairs = array(
		'product_cat_slugs' => 'product_cat_slug',
		'color_slugs'       => 'color_slug',
		'brand_slugs'       => 'brand_slug',
		'attribute_slugs'   => 'attribute_slug',
		'stock_states'      => 'stock_state',
		'rating_values'     => 'rating_value',
	);
	foreach ( $pairs as $plural => $singular ) {
		if ( ! isset( $filters[ $plural ] ) ) {
			$filters[ $plural ] = array();
		} else {
			$filters[ $plural ] = storzen_shop_sidebar_normalize_slug_list( $filters[ $plural ] );
		}
		if ( empty( $filters[ $plural ] ) && ! empty( $filters[ $singular ] ) ) {
			$filters[ $plural ] = storzen_shop_sidebar_normalize_slug_list( $filters[ $singular ] );
		}
		unset( $filters[ $singular ] );
	}
	return $filters;
}

/**
 * Whitelist widget settings for AJAX (prevents arbitrary query manipulation).
 *
 * @param array<string,mixed> $in Raw settings.
 * @return array<string,mixed>
 */
function storzen_shop_sidebar_sanitize_settings( $in ) {
	if ( ! is_array( $in ) ) {
		return array();
	}

	$out = array(
		'ppr'                   => isset( $in['ppr'] ) ? max( 1, min( 48, (int) $in['ppr'] ) ) : 12,
		'column'                => isset( $in['column'] ) ? (string) $in['column'] : '4',
		'order'                 => isset( $in['order'] ) && in_array( strtoupper( (string) $in['order'] ), array( 'ASC', 'DESC' ), true ) ? strtoupper( (string) $in['order'] ) : 'DESC',
		'wc_orderby'            => isset( $in['wc_orderby'] ) ? sanitize_key( (string) $in['wc_orderby'] ) : 'menu_order',
		'select_cat'            => isset( $in['select_cat'] ) && is_array( $in['select_cat'] ) ? array_map( 'absint', $in['select_cat'] ) : array(),
		'exclude_cat'           => isset( $in['exclude_cat'] ) && is_array( $in['exclude_cat'] ) ? array_map( 'absint', $in['exclude_cat'] ) : array(),
		'dtitle'                => isset( $in['dtitle'] ) ? sanitize_key( (string) $in['dtitle'] ) : 'full',
		'title_excerpt_length'  => isset( $in['title_excerpt_length'] ) ? max( 1, (int) $in['title_excerpt_length'] ) : 8,
		'titltag'               => isset( $in['titltag'] ) ? sanitize_key( (string) $in['titltag'] ) : 'h6',
		'open-url'              => isset( $in['open-url'] ) ? sanitize_key( (string) $in['open-url'] ) : 'same-window',
		'enable_pagination'     => ( isset( $in['enable_pagination'] ) && 'yes' === $in['enable_pagination'] ) ? 'yes' : 'no',
		'show_toolbar'          => ( isset( $in['show_toolbar'] ) && 'yes' === $in['show_toolbar'] ) ? 'yes' : 'no',
		'show_grid_list_icons'  => ( isset( $in['show_grid_list_icons'] ) && 'yes' === $in['show_grid_list_icons'] ) ? 'yes' : 'no',
		'color_attr_taxonomy'   => isset( $in['color_attr_taxonomy'] ) ? sanitize_key( (string) $in['color_attr_taxonomy'] ) : 'pa_color',
		'brand_attr_taxonomy'   => isset( $in['brand_attr_taxonomy'] ) ? sanitize_text_field( (string) $in['brand_attr_taxonomy'] ) : 'pa_brand',
		'attribute_taxonomy'    => isset( $in['attribute_taxonomy'] ) ? sanitize_key( (string) $in['attribute_taxonomy'] ) : 'pa_color',
		'sidebar_multi_filter'  => ( isset( $in['sidebar_multi_filter'] ) && 'yes' === $in['sidebar_multi_filter'] ) ? 'yes' : 'no',
		'show_sidebar_attributes' => ( isset( $in['show_sidebar_attributes'] ) && 'yes' === $in['show_sidebar_attributes'] ) ? 'yes' : 'no',
		'show_sidebar_stock'      => ( isset( $in['show_sidebar_stock'] ) && 'yes' === $in['show_sidebar_stock'] ) ? 'yes' : 'no',
		'show_sidebar_ratings'    => ( isset( $in['show_sidebar_ratings'] ) && 'yes' === $in['show_sidebar_ratings'] ) ? 'yes' : 'no',
		'sidebar_attr_title'      => isset( $in['sidebar_attr_title'] ) ? sanitize_text_field( (string) $in['sidebar_attr_title'] ) : __( 'Attributes', 'storzen' ),
		'sidebar_stock_title'     => isset( $in['sidebar_stock_title'] ) ? sanitize_text_field( (string) $in['sidebar_stock_title'] ) : __( 'Stock', 'storzen' ),
		'sidebar_ratings_title'   => isset( $in['sidebar_ratings_title'] ) ? sanitize_text_field( (string) $in['sidebar_ratings_title'] ) : __( 'Ratings', 'storzen' ),
	);

	$allowed_orderby = array( 'menu_order', 'popularity', 'rating', 'date', 'price', 'price-desc' );
	if ( ! in_array( $out['wc_orderby'], $allowed_orderby, true ) ) {
		$out['wc_orderby'] = 'menu_order';
	}

	return $out;
}

/**
 * Build WP_Query args for Shop With Sidebar.
 *
 * @param array<string,mixed> $settings Sanitized widget settings.
 * @param array<string,mixed> $filters  Request filters (AJAX or merged GET).
 * @return array<string,mixed>
 */
function storzen_shop_sidebar_build_query_args( array $settings, array $filters = array() ) {
	$paged = isset( $filters['paged'] ) ? max( 1, (int) $filters['paged'] ) : 1;

	$args = array(
		'post_type'           => 'product',
		'post_status'         => 'publish',
		'ignore_sticky_posts' => 1,
		'posts_per_page'      => (int) $settings['ppr'],
		'paged'               => $paged,
	);

	$orderby = isset( $filters['orderby'] ) && $filters['orderby'] !== ''
		? sanitize_key( (string) $filters['orderby'] )
		: ( isset( $settings['wc_orderby'] ) ? (string) $settings['wc_orderby'] : 'menu_order' );

	$allowed_orderby = array( 'menu_order', 'popularity', 'rating', 'date', 'price', 'price-desc' );
	if ( ! in_array( $orderby, $allowed_orderby, true ) ) {
		$orderby = 'menu_order';
	}

	$order = isset( $filters['order'] ) && $filters['order'] !== ''
		? strtoupper( (string) $filters['order'] )
		: ( isset( $settings['order'] ) ? (string) $settings['order'] : 'DESC' );
	if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
		$order = 'DESC';
	}

	if ( function_exists( 'WC' ) && WC()->query && method_exists( WC()->query, 'get_catalog_ordering_args' ) ) {
		$ordering = WC()->query->get_catalog_ordering_args( $orderby, $order );
		$args['orderby'] = $ordering['orderby'];
		$args['order']   = $ordering['order'];
		if ( ! empty( $ordering['meta_key'] ) ) {
			// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
		// Intentional: meta_key value comes from WC's own catalog ordering API;
		// required for price and sales sorting.
		$args['meta_key'] = $ordering['meta_key'];
		// phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
		}
	} else {
		$args['orderby'] = 'date';
		$args['order']   = $order;
	}

	$keyword = isset( $filters['keyword'] ) ? sanitize_text_field( (string) $filters['keyword'] ) : '';
	$args['s'] = $keyword;

	$tax_query = array();

	$filters = storzen_shop_sidebar_coerce_filter_arrays( $filters );

	$product_cat_slugs = ! empty( $filters['product_cat_slugs'] ) ? storzen_shop_sidebar_normalize_slug_list( $filters['product_cat_slugs'] ) : array();
	if ( ! empty( $product_cat_slugs ) ) {
		$tax_query[] = array(
			'taxonomy' => 'product_cat',
			'field'    => 'slug',
			'terms'    => $product_cat_slugs,
			'operator' => 'IN',
		);
	} elseif ( ! empty( $settings['select_cat'] ) ) {
		$tax_query[] = array(
			'taxonomy' => 'product_cat',
			'field'    => 'term_id',
			'terms'    => array_values( (array) $settings['select_cat'] ),
		);
	}

	if ( ! empty( $settings['exclude_cat'] ) ) {
		$tax_query[] = array(
			'taxonomy' => 'product_cat',
			'field'    => 'term_id',
			'terms'    => array_values( (array) $settings['exclude_cat'] ),
			'operator' => 'NOT IN',
		);
	}

	$color_tax = isset( $settings['color_attr_taxonomy'] ) ? sanitize_key( (string) $settings['color_attr_taxonomy'] ) : 'pa_color';
	if ( $color_tax && taxonomy_exists( $color_tax ) ) {
		$color_slugs = ! empty( $filters['color_slugs'] ) ? storzen_shop_sidebar_normalize_slug_list( $filters['color_slugs'] ) : array();
		if ( ! empty( $color_slugs ) ) {
			$tax_query[] = array(
				'taxonomy' => $color_tax,
				'field'    => 'slug',
				'terms'    => $color_slugs,
				'operator' => 'IN',
			);
		}
	}

	$brand_tax = storzen_resolve_brand_taxonomy( isset( $settings['brand_attr_taxonomy'] ) ? (string) $settings['brand_attr_taxonomy'] : '' );
	if ( $brand_tax ) {
		$brand_slugs = ! empty( $filters['brand_slugs'] ) ? storzen_shop_sidebar_normalize_slug_list( $filters['brand_slugs'] ) : array();
		if ( ! empty( $brand_slugs ) ) {
			$tax_query[] = array(
				'taxonomy' => $brand_tax,
				'field'    => 'slug',
				'terms'    => $brand_slugs,
				'operator' => 'IN',
			);
		}
	}

	$attribute_tax = isset( $settings['attribute_taxonomy'] ) ? sanitize_key( (string) $settings['attribute_taxonomy'] ) : '';
	if ( $attribute_tax && taxonomy_exists( $attribute_tax ) ) {
		$attribute_slugs = ! empty( $filters['attribute_slugs'] ) ? storzen_shop_sidebar_normalize_slug_list( $filters['attribute_slugs'] ) : array();
		if ( ! empty( $attribute_slugs ) ) {
			$tax_query[] = array(
				'taxonomy' => $attribute_tax,
				'field'    => 'slug',
				'terms'    => $attribute_slugs,
				'operator' => 'IN',
			);
		}
	}

	if ( count( $tax_query ) > 1 ) {
		$tax_query['relation'] = 'AND';
	}
	if ( ! empty( $tax_query ) ) {
		// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_tax_query
		// Intentional: tax_query powers the AJAX sidebar filter for categories,
		// attributes, and brands. There is no WP_Query alternative for taxonomy filtering.
		$args['tax_query'] = $tax_query;
		// phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_tax_query
	}

	$min_p = isset( $filters['min_price'] ) ? (float) $filters['min_price'] : null;
	$max_p = isset( $filters['max_price'] ) ? (float) $filters['max_price'] : null;

	if ( null !== $min_p && null !== $max_p && $max_p >= $min_p && $max_p > 0 ) {
		$args['meta_query'][] = array(
			'key'     => '_price',
			'value'   => array( $min_p, $max_p ),
			'compare' => 'BETWEEN',
			'type'    => 'DECIMAL',
		);
	}

	$stock_states = ! empty( $filters['stock_states'] ) ? storzen_shop_sidebar_normalize_slug_list( $filters['stock_states'] ) : array();
	$allowed_stock = array( 'instock', 'outofstock', 'onbackorder' );
	$stock_states = array_values( array_intersect( $stock_states, $allowed_stock ) );
	if ( ! empty( $stock_states ) ) {
		$args['meta_query'][] = array(
			'key'     => '_stock_status',
			'value'   => $stock_states,
			'compare' => 'IN',
		);
	}

	$rating_values = isset( $filters['rating_values'] ) && is_array( $filters['rating_values'] ) ? $filters['rating_values'] : array();
	$rating_values = array_values(
		array_filter(
			array_map(
				static function( $v ) {
					$i = (int) $v;
					return ( $i >= 1 && $i <= 5 ) ? $i : 0;
				},
				$rating_values
			)
		)
	);
	if ( ! empty( $rating_values ) ) {
		$rating_query = array( 'relation' => 'OR' );
		foreach ( $rating_values as $rv ) {
			$rating_query[] = array(
				'key'     => '_wc_average_rating',
				'value'   => (float) $rv,
				'compare' => '>=',
				'type'    => 'DECIMAL(3,2)',
			);
		}
		$args['meta_query'][] = $rating_query;
	}

	return $args;
}

/**
 * WooCommerce-style query var for layered attribute filters (`filter_{attr_name}`).
 *
 * @param string $taxonomy Attribute taxonomy e.g. pa_color.
 * @return string Query var key or empty.
 */
function storzen_shop_sidebar_wc_filter_query_var( $taxonomy ) {
	$tax = sanitize_key( (string) $taxonomy );
	if ( $tax === '' ) {
		return '';
	}
	$name = str_replace( 'pa_', '', $tax );
	return 'filter_' . $name;
}

/**
 * Merge URL query vars into filter array for initial page load.
 *
 * @param array<string,mixed> $filters  Base filters.
 * @param array<string,mixed> $settings Optional sanitized widget settings (for attribute filter keys).
 * @return array<string,mixed>
 */
function storzen_shop_sidebar_filters_from_request( array $filters = array(), array $settings = array() ) {
	// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only catalog query args.
	if ( isset( $_GET['orderby'] ) ) {
		$ob = sanitize_key( wp_unslash( $_GET['orderby'] ) );
		$allowed_orderby = array( 'menu_order', 'popularity', 'rating', 'date', 'price', 'price-desc' );
		if ( in_array( $ob, $allowed_orderby, true ) ) {
			$filters['orderby'] = $ob;
		}
	}
	if ( isset( $_GET['keyword'] ) ) {
		$filters['keyword'] = sanitize_text_field( wp_unslash( $_GET['keyword'] ) );
	}
	if ( isset( $_GET['min_price'], $_GET['max_price'] ) ) {
		$filters['min_price'] = (float) wc_clean( wp_unslash( $_GET['min_price'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via wc_clean().
		$filters['max_price'] = (float) wc_clean( wp_unslash( $_GET['max_price'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via wc_clean().
	}
	if ( isset( $_GET['product_cat'] ) && '' !== $_GET['product_cat'] ) {
		$raw = wp_unslash( $_GET['product_cat'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; sanitized by normalize_slug_list().
		if ( is_array( $raw ) ) {
			$filters['product_cat_slugs'] = storzen_shop_sidebar_normalize_slug_list( $raw );
		} else {
			$filters['product_cat_slugs'] = storzen_shop_sidebar_normalize_slug_list( (string) $raw );
		}
	}

	if ( ! empty( $settings ) ) {
		$color_tax = isset( $settings['color_attr_taxonomy'] ) ? sanitize_key( (string) $settings['color_attr_taxonomy'] ) : '';
		if ( $color_tax && taxonomy_exists( $color_tax ) ) {
			$qk = storzen_shop_sidebar_wc_filter_query_var( $color_tax );
			if ( $qk && isset( $_GET[ $qk ] ) && '' !== $_GET[ $qk ] ) {
				$raw = wp_unslash( $_GET[ $qk ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; sanitized by normalize_slug_list().
				$filters['color_slugs'] = is_array( $raw )
					? storzen_shop_sidebar_normalize_slug_list( $raw )
					: storzen_shop_sidebar_normalize_slug_list( (string) $raw );
			}
		}
		if ( function_exists( 'storzen_resolve_brand_taxonomy' ) ) {
			$brand_tax = storzen_resolve_brand_taxonomy( isset( $settings['brand_attr_taxonomy'] ) ? (string) $settings['brand_attr_taxonomy'] : '' );
			if ( $brand_tax ) {
				$qk = storzen_shop_sidebar_wc_filter_query_var( $brand_tax );
				if ( $qk && isset( $_GET[ $qk ] ) && '' !== $_GET[ $qk ] ) {
					$raw = wp_unslash( $_GET[ $qk ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; sanitized by normalize_slug_list().
					$filters['brand_slugs'] = is_array( $raw )
						? storzen_shop_sidebar_normalize_slug_list( $raw )
						: storzen_shop_sidebar_normalize_slug_list( (string) $raw );
				}
			}
		}
		$attribute_tax = isset( $settings['attribute_taxonomy'] ) ? sanitize_key( (string) $settings['attribute_taxonomy'] ) : '';
		if ( $attribute_tax && taxonomy_exists( $attribute_tax ) ) {
			$qk = storzen_shop_sidebar_wc_filter_query_var( $attribute_tax );
			if ( $qk && isset( $_GET[ $qk ] ) && '' !== $_GET[ $qk ] ) {
				$raw = wp_unslash( $_GET[ $qk ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; sanitized by normalize_slug_list().
				$filters['attribute_slugs'] = is_array( $raw )
					? storzen_shop_sidebar_normalize_slug_list( $raw )
					: storzen_shop_sidebar_normalize_slug_list( (string) $raw );
			}
		}
	}

	if ( isset( $_GET['stock_status'] ) && '' !== $_GET['stock_status'] ) {
		$raw = wp_unslash( $_GET['stock_status'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; sanitized by normalize_slug_list().
		$filters['stock_states'] = is_array( $raw )
			? storzen_shop_sidebar_normalize_slug_list( $raw )
			: storzen_shop_sidebar_normalize_slug_list( (string) $raw );
	}
	if ( isset( $_GET['rating_filter'] ) && '' !== $_GET['rating_filter'] ) {
		$raw = wp_unslash( $_GET['rating_filter'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public filter; cast to int below.
		$list = is_array( $raw ) ? $raw : preg_split( '/[,\s]+/', (string) $raw, -1, PREG_SPLIT_NO_EMPTY );
		$filters['rating_values'] = array_values(
			array_filter(
				array_map(
					static function( $v ) {
						$i = (int) $v;
						return ( $i >= 1 && $i <= 5 ) ? $i : 0;
					},
					(array) $list
				)
			)
		);
	}

	$filters = storzen_shop_sidebar_coerce_filter_arrays( $filters );
	// phpcs:enable WordPress.Security.NonceVerification.Recommended
	return $filters;
}

/**
 * HTML for product star rating row (list layout).
 *
 * @param \WC_Product $product Product.
 * @return string
 */
function storzen_shop_sidebar_star_rating_html( $product ) {
	if ( ! $product instanceof \WC_Product ) {
		return '';
	}
	$average = (float) $product->get_average_rating();
	$full    = (int) floor( $average );
	$half    = ( ( $average - $full ) >= 0.5 ) ? 1 : 0;
	$empty   = max( 0, 5 - $full - $half );
	$out     = '';
	for ( $i = 0; $i < $full; $i++ ) {
		$out .= '<i class="fas fa-star rated"></i>';
	}
	if ( $half ) {
		$out .= '<i class="fas fa-star-half-alt rated"></i>';
	}
	for ( $i = 0; $i < $empty; $i++ ) {
		$out .= '<i class="fas fa-star"></i>';
	}
	return $out;
}

/**
 * Add to cart link for list layout (theme-btn style-two + basket icon).
 *
 * @param \WC_Product $product Product.
 * @return string
 */
function storzen_shop_sidebar_list_add_to_cart_html( $product ) {
	if ( ! $product instanceof \WC_Product ) {
		return '';
	}
	$ajax_class = ( get_option( 'woocommerce_enable_ajax_add_to_cart' ) === 'yes' ? 'storzen_ajax' : '' );
	if ( $product->is_type( 'variable' ) ) {
		return sprintf(
			'<a href="%s" class="%s"><i class="fas fa-shopping-basket"></i> %s</a>',
			esc_url( $product->add_to_cart_url() ),
			esc_attr(
				implode(
					' ',
					array_filter(
						array(
							'theme-btn',
							'style-two',
							'button',
							'product_type_' . $product->get_type(),
							'swp-readmore-arrow',
							'swp-add-to-cart',
							'swp-readmore-arrow-btn',
							$ajax_class,
						)
					)
				)
			),
			esc_html( $product->add_to_cart_text() )
		);
	}

	return sprintf(
		'<a href="%s" data-quantity="1" class="%s" %s><i class="fas fa-shopping-basket"></i> %s</a>',
		esc_url( $product->add_to_cart_url() ),
		esc_attr(
			implode(
				' ',
				array_filter(
					array(
						'theme-btn',
						'style-two',
						'button',
						'product_type_' . $product->get_type(),
						$product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
						$product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : '',
						'swp-readmore-arrow',
						'swp-add-to-cart',
						'swp-readmore-arrow-btn',
						$ajax_class,
					)
				)
			)
		),
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WooCommerce wc_implode_html_attributes is sanitized.
		wc_implode_html_attributes(
			array(
				'data-product_id'  => $product->get_id(),
				'data-product_sku' => $product->get_sku(),
				'aria-label'       => $product->add_to_cart_description(),
				'rel'              => 'nofollow',
			)
		),
		esc_html( $product->add_to_cart_text() )
	);
}

if ( ! function_exists( 'storzen_product_card_compare_wishlist_icons' ) ) {
	/**
	 * WT Product Compare button + WT Wishlist Pro icon shortcode (same as shop-sidebar-main product row).
	 *
	 * @param int $product_id Product ID; use in list layouts — default is current post in the loop.
	 */
	function storzen_product_card_compare_wishlist_icons( $product_id = 0 ) {
		$product_id = $product_id ? absint( $product_id ) : absint( get_the_ID() );
		if ( ! $product_id ) {
			return;
		}
		if ( function_exists( 'wtpc_render_icon_button' ) ) {
			wtpc_render_icon_button( $product_id );
		}
		if ( shortcode_exists( 'wtwp_icon' ) ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WT Wishlist Pro shortcode.
			echo do_shortcode( '[wtwp_icon product_id="' . $product_id . '"]' );
		}
	}
}

