<?php
/**
 * FoodOrd Products Renderer Class
 *
 * @package FoodOrd
 */

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

/**
 * Class Foodord_Products_Renderer
 */
class Foodord_Products_Renderer {


	/**
	 * Render products based on given attributes
	 *
	 * @param array $atts Attributes for rendering products.
	 * @return string HTML output
	 */
	public static function render( $atts = array() ) {
		// Signal that home banner should be shown (for dynamic rendering detection).
		$GLOBALS['foodord_banner_should_show'] = true;

		// Check if WooCommerce is active.
		if ( ! function_exists( 'wc' ) ) {
			return '<p class="foodord-products-notice">' . esc_html__( 'WooCommerce is not active.', 'foodord' ) . '</p>';
		}

		// Parse attributes with defaults.
		$atts = wp_parse_args(
			$atts,
			array(
				'limit'   => -1,
				'columns' => 4,
				'orderby' => 'menu_order',
				'order'   => 'ASC',
			)
		);

		$toggle_slot_markup = '';
		$theme_active       = defined( 'FOODORD_THEME_ACTIVE' ) && FOODORD_THEME_ACTIVE;
		$plugin_only        = ! $theme_active;
		$wrapper_classes    = array( 'foodord-products-shortcode' );
		if ( $plugin_only ) {
			$wrapper_classes[] = 'alignfull';
			$wrapper_classes[] = 'foodord-products-shortcode--plugin-only';
		}

		// Sanitize attributes.
		$limit   = intval( $atts['limit'] );
		$columns = max( 1, min( 6, intval( $atts['columns'] ) ) );
		$orderby = sanitize_text_field( $atts['orderby'] );
		$order   = sanitize_text_field( $atts['order'] );

		// Get products.
		$args = array(
			'post_type'      => 'product',
			'post_status'    => 'publish',
			'posts_per_page' => $limit,
			'orderby'        => $orderby,
			'order'          => $order,
		);

		$products                              = new WP_Query( $args );
		$GLOBALS['foodord_products_available'] = false;

		if ( ! $products->have_posts() ) {
			wp_reset_postdata();
			$empty_wrapper_classes = array_merge( $wrapper_classes, array( 'foodord-products-shortcode--empty' ) );
			return '<div class="' . esc_attr( implode( ' ', $empty_wrapper_classes ) ) . '">'
				. $toggle_slot_markup
				. '<div class="foodord-products-empty" role="status">'
				. '<div class="foodord-products-empty__icon-wrap">'
				. '<svg class="foodord-products-empty__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>'
				. '</div>'
				. '<p class="foodord-products-empty__title">' . esc_html__( 'No products to display', 'foodord' ) . '</p>'
				. '<p class="foodord-products-empty__description">'
				. esc_html__( 'No products are available yet. Start adding your first product. The products you add will appear here afterwards.', 'foodord' )
				. '</p>'
				. '</div>'
				. '</div>';
		}

		$GLOBALS['foodord_products_available'] = true;
		$selected_layout                       = function_exists( 'foodord_get_product_card_layout' ) ? foodord_get_product_card_layout() : 'streamline';

		// Get all categories with their order.
		$all_categories = get_terms(
			array(
				'taxonomy'   => 'product_cat',
				'hide_empty' => false,
				'orderby'    => 'menu_order',
				'order'      => 'ASC',
			)
		);

		$category_order_map = array();
		foreach ( $all_categories as $index => $cat ) {
			if ( ! $cat instanceof WP_Term ) {
				continue;
			}
			$category_order_map[ (string) $cat->term_id ] = $index;
		}

		// Group products by category.
		$products_by_category = array();
		$allergy_info_active  = class_exists( 'Foodord_Allergy_Info' ) ? Foodord_Allergy_Info::is_enabled() && Foodord_Allergy_Info::has_content() : false;

		while ( $products->have_posts() ) {
			$products->the_post();
			global $product;

			$product_terms = wp_get_post_terms(
				get_the_ID(),
				'product_cat',
				array(
					'orderby' => 'menu_order',
					'order'   => 'ASC',
				)
			);

			$primary_term = ( ! empty( $product_terms ) && ! is_wp_error( $product_terms ) ) ? $product_terms[0] : null;
			$category_key = $primary_term instanceof WP_Term ? (string) $primary_term->term_id : 'uncategorized';

			if ( ! isset( $products_by_category[ $category_key ] ) ) {
				$products_by_category[ $category_key ] = array(
					'term'        => $primary_term instanceof WP_Term ? $primary_term : null,
					'product_ids' => array(),
				);
			}

			$products_by_category[ $category_key ]['product_ids'][] = get_the_ID();
		}

		// Sort categories according to WooCommerce menu order.
		uksort(
			$products_by_category,
			function ( $a, $b ) use ( $category_order_map ) {
				$order_a = $category_order_map[ $a ] ?? 999999;
				$order_b = $category_order_map[ $b ] ?? 999999;
				if ( $order_a === $order_b ) {
					return strcmp( (string) $a, (string) $b );
				}
				return $order_a <=> $order_b;
			}
		);

		wp_reset_postdata();

		$plugin_nav_markup = $plugin_only ? self::get_plugin_scroll_navigation_markup() : '';

		$output = '<div class="' . esc_attr( implode( ' ', $wrapper_classes ) ) . '">' . $toggle_slot_markup . $plugin_nav_markup;
		$output .= self::get_products_campaigns_markup();

		// Render products grouped by category.
		foreach ( $products_by_category as $category_data ) {
			if ( ! isset( $category_data['product_ids'] ) || ! is_array( $category_data['product_ids'] ) ) {
				continue;
			}

			$product_ids = array_values(
				array_filter(
					array_map( 'absint', $category_data['product_ids'] ),
					static function ( int $product_id ): bool {
						return ! Foodord_Product_Availability::is_hidden_from_listing( $product_id );
					}
				)
			);

			if ( empty( $product_ids ) ) {
				continue;
			}

			$category_product_count          = count( $product_ids );
			$category_product_count_display  = number_format_i18n( $category_product_count );
			$category_product_count_label    = sprintf(
				/* translators: %s: Number of products in this category. */
				_n( '%s product', '%s products', $category_product_count, 'foodord' ),
				$category_product_count_display
			);
			$term              = isset( $category_data['term'] ) && $category_data['term'] instanceof WP_Term ? $category_data['term'] : null;
			$category_name     = $term ? $term->name : __( 'Uncategorized', 'foodord' );
			$category_desc_raw = '';
			if ( $term ) {
				$category_desc_raw = term_description( (int) $term->term_id );
			}
			$category_desc_raw        = is_string( $category_desc_raw ) ? trim( $category_desc_raw ) : '';
			$has_category_description = '' !== $category_desc_raw;
			$category_title_classes   = array( 'foodord-product-category' );
			if ( $has_category_description ) {
				$category_title_classes[] = 'foodord-product-category--has-description';
			} else {
				$category_title_classes[] = 'foodord-product-category--no-description';
			}

			$output .= '<div class="foodord-category-section">';
			$output .= '<div class="foodord-product-category-row' . ( $has_category_description ? ' foodord-product-category-row--has-description' : ' foodord-product-category-row--no-description' ) . '">';
			$output .= '<h2 class="' . esc_attr( implode( ' ', $category_title_classes ) ) . '">' . esc_html( $category_name ) . '</h2>';
			$output .= '<span class="foodord-product-category-count">' . esc_html( $category_product_count_label ) . '</span>';
			$output .= '</div>';
			if ( $has_category_description ) {
				$category_desc_formatted = self::format_category_description_html( $category_desc_raw );
				$output                 .= '<div class="foodord-product-category-description">' . wp_kses_post( $category_desc_formatted ) . '</div>';
			}

			$output .= '<div class="foodord-category-products">';

			foreach ( $product_ids as $product_id ) {
				$wc_product = wc_get_product( $product_id );

				if ( ! $wc_product ) {
					continue;
				}

				if ( Foodord_Product_Availability::is_product_hidden_from_listing( $wc_product ) ) {
					continue;
				}

				$card_classes = array( 'foodord-product-card', 'foodord-product-card--loading' );
				$card_attrs   = array(
					' data-product-id="' . esc_attr( $product_id ) . '"',
				);
				$is_available = Foodord_Product_Availability::is_available( $product_id );
				$availability_label   = $is_available ? '' : Foodord_Product_Availability::get_unavailable_label( $product_id );
				$availability_message = $is_available ? '' : Foodord_Product_Availability::get_unavailable_message( $product_id );
				$card_attrs[] = ' data-foodord-available="' . ( $is_available ? '1' : '0' ) . '"';
				$card_attrs[] = ' data-foodord-availability-label="' . esc_attr( $availability_label ) . '"';
				$card_attrs[] = ' data-foodord-availability-message="' . esc_attr( $availability_message ) . '"';
				$card_attrs[] = ' aria-disabled="' . ( $is_available ? 'false' : 'true' ) . '"';
				if ( ! $is_available ) {
					$card_classes[] = 'foodord-product-card--unavailable';
				}

				$card_attrs[]      = ' data-foodord-direct-add="0"';
				$status_badge_html = '';
				if ( ! $is_available ) {
					$status_badge_html = '<span class="foodord-product-card__status-badge" aria-hidden="true">' . esc_html( $availability_label ) . '</span>';
				}

				$show_images       = get_option( 'foodord_show_product_card_images', 'yes' ) === 'yes';
				$has_product_image = has_post_thumbnail( $product_id );
				$show_card_image   = $show_images && $has_product_image;
				$image_tag         = '';
				if ( $show_card_image ) {
					$image_tag = self::get_product_image_tag( $product_id );
				}
				if ( ! $show_card_image ) {
					$card_classes[] = 'foodord-product-card--no-image';
				}

				$has_product_allergens = class_exists( 'Foodord_Product_Data_Allergens' ) ? Foodord_Product_Data_Allergens::has_product_allergens( $product_id ) : false;
				$allergy_trigger_html  = self::get_allergy_trigger_html( $product_id, $has_product_allergens, $allergy_info_active );
				$description_text      = self::get_formatted_description( $wc_product );
				$price_html            = self::get_product_price_html( $wc_product );
				$price_label           = $wc_product->is_type( 'variable' ) ? __( 'Price from', 'foodord' ) : __( 'Price', 'foodord' );
				$button_html           = self::get_add_to_cart_button_html( $wc_product, $product_id, $is_available );
				$product_title         = get_the_title( $product_id );
				$product_labels_html   = class_exists( 'Foodord_Product_Labels' ) ? wp_kses( Foodord_Product_Labels::get_product_labels_html( $product_id, 'card' ), Foodord_Product_Labels::get_allowed_html() ) : '';
				if ( '' !== $product_labels_html ) {
					$card_classes[] = 'foodord-product-card--has-labels';
				}

				if ( 'grid' === $selected_layout ) {
					$output .= self::render_grid_card(
						array(
							'card_classes'         => $card_classes,
							'card_attrs'           => $card_attrs,
							'status_badge_html'    => $status_badge_html,
							'image_tag'            => $image_tag,
							'product_title'        => $product_title,
							'allergy_trigger_html' => $allergy_trigger_html,
							'product_labels_html'  => $product_labels_html,
							'description_text'     => $description_text,
							'price_html'           => $price_html,
							'price_label'          => $price_label,
							'button_html'          => $button_html,
							'show_images'          => $show_card_image,
							'highlight_badges'     => self::get_highlight_badges( $wc_product ),
						)
					);
					continue;
				}

				if ( 'streamline' === $selected_layout ) {
					$output .= self::render_streamline_card(
						array(
							'card_classes'         => $card_classes,
							'card_attrs'           => $card_attrs,
							'status_badge_html'    => $status_badge_html,
							'image_tag'            => $image_tag,
							'product_title'        => $product_title,
							'allergy_trigger_html' => $allergy_trigger_html,
							'product_labels_html'  => $product_labels_html,
							'description_text'     => $description_text,
							'price_html'           => $price_html,
							'price_label'          => $price_label,
							'button_html'          => $button_html,
							'show_images'          => $show_card_image,
						)
					);
					continue;
				}

				if ( 'frosted' === $selected_layout ) {
					$output .= self::render_frosted_card(
						array(
							'card_classes'         => $card_classes,
							'card_attrs'           => $card_attrs,
							'status_badge_html'    => $status_badge_html,
							'image_tag'            => $image_tag,
							'product_title'        => $product_title,
							'allergy_trigger_html' => $allergy_trigger_html,
							'product_labels_html'  => $product_labels_html,
							'description_text'     => $description_text,
							'price_html'           => $price_html,
							'button_html'          => $button_html,
							'show_images'          => $show_card_image,
						)
					);
					continue;
				}

				$output .= '<div class="' . esc_attr( implode( ' ', $card_classes ) ) . '" data-foodord-card-layout="classic"' . implode( '', $card_attrs ) . '>';

				if ( '' !== $status_badge_html ) {
					$output .= $status_badge_html;
				}

				if ( $show_card_image && '' !== $image_tag ) {
					$output .= '<div class="product-image-container">';
					$output .= '<div class="product-image-wrapper">';
					$output .= wp_kses_post( $image_tag );
					$output .= '</div>';
					$output .= '</div>';
				}

				$output .= '<div class="product-info">';
				$output .= '<div class="product-top">';
				$output .= '<div class="product-title-row">';
				$output .= '<h3 class="product-title">' . esc_html( $product_title ) . '</h3>';
				$output .= $allergy_trigger_html;
				$output .= '</div>';

				if ( '' !== $description_text ) {
					$output .= '<div class="product-description">';
					$output .= '<p class="product-description-text">' . wp_kses( $description_text, array( 'br' => array() ) ) . '</p>';
					$output .= '</div>';
				}

				$output .= '</div>';
				$output .= '<div class="product-bottom">';
				$output .= '<div class="product-price-container">';
				if ( '' !== $price_html ) {
					$output .= '<div class="foodord-product-card__price-main">';
					$output .= '<div class="product-price">' . wp_kses_post( $price_html ) . '</div>';
					$output .= '</div>';
				}
				$output .= $product_labels_html;
				$output .= '</div>';

				if ( '' !== $button_html ) {
					$output .= '<div class="product-actions">';
					$output .= $button_html;
					$output .= '</div>';
				}

				$output .= '</div>';
				$output .= '</div>';
				$output .= self::get_product_card_skeleton_markup( 'classic' );
				$output .= '</div>';
			}

			$output .= '</div>'; // .foodord-category-products
			$output .= '</div>'; // .foodord-category-section
		}

		$output .= '</div>';

		// Enqueue script to handle view cart button removal.
		wp_enqueue_script(
			'foodord-products-script',
			plugin_dir_url( __DIR__ ) . 'public/js/foodord-products.js',
			array( 'jquery' ),
			FOODORD_VERSION,
			true
		);
		wp_localize_script(
			'foodord-products-script',
			'foodord_products_vars',
			array(
				'ajax_url'            => admin_url( 'admin-ajax.php' ),
				'nonce'               => wp_create_nonce( 'foodord_timed_products_nonce' ),
				'add_to_cart_label'   => __( 'Add to cart', 'foodord' ),
				'not_available_label' => __( 'Not available', 'foodord' ),
				'only_pickup_label'     => __( 'Only Pickup', 'foodord' ),
				'only_delivery_label'   => __( 'Only Delivery', 'foodord' ),
				'only_pickup_message'   => __( 'This product is only available for pickup.', 'foodord' ),
				'only_delivery_message' => __( 'This product is only available for delivery.', 'foodord' ),
			)
		);

		if ( class_exists( 'Foodord_Business_Hours' ) ) {
			Foodord_Business_Hours::enqueue_toast_script();
		}

		return $output;
	}

	/**
	 * Build scroll navigation markup for plugin-only product pages.
	 *
	 * @return string
	 */
	protected static function get_plugin_scroll_navigation_markup(): string {
		if ( defined( 'FOODORD_THEME_ACTIVE' ) && FOODORD_THEME_ACTIVE ) {
			return '';
		}

		if ( 'yes' !== get_option( 'foodord_enable_scroll_nav', 'yes' ) ) {
			return '';
		}

		if ( ! class_exists( 'Foodord_Scroll_Categories_Nav' ) || ! method_exists( 'Foodord_Scroll_Categories_Nav', 'get_navigation_markup' ) ) {
			return '';
		}

		if ( ! empty( $GLOBALS['foodord_plugin_scroll_nav_rendered'] ) ) {
			return '';
		}

		$GLOBALS['foodord_plugin_scroll_nav_rendered'] = true;

		if ( method_exists( 'Foodord_Scroll_Categories_Nav', 'enqueue_navigation_assets' ) ) {
			Foodord_Scroll_Categories_Nav::enqueue_navigation_assets();
		}

		return Foodord_Scroll_Categories_Nav::get_navigation_markup( 'plugin' );
	}

	/**
	 * Build the delivery/pickup toggle slot markup for shortcode pages.
	 *
	 * @return string
	 */
	protected static function get_products_toggle_slot_markup(): string {
		$header  = '<div class="foodord-products-shortcode__header">';
		$header .= '<div class="foodord-products-address-slot" data-foodord-address-slot="products"></div>';
		$header .= '<div class="foodord-products-toggle-slot" data-foodord-mode-toggle="products"></div>';
		$header .= '</div>';

		return $header;
	}

	/**
	 * Build the active campaigns strip shown above product categories.
	 *
	 * @return string
	 */
	protected static function get_products_campaigns_markup(): string {
		if ( ! function_exists( 'foodord_pro_get_active_campaigns' ) ) {
			return '';
		}

		$campaigns = foodord_pro_get_active_campaigns();
		if ( empty( $campaigns ) || ! is_array( $campaigns ) ) {
			return '';
		}

		$items = array();

		foreach ( $campaigns as $campaign ) {
			$title = isset( $campaign['title'] ) ? (string) $campaign['title'] : '';
			if ( '' === trim( wp_strip_all_tags( $title ) ) ) {
				continue;
			}

			$type              = isset( $campaign['type'] ) ? sanitize_html_class( (string) $campaign['type'] ) : 'default';
			$targets           = isset( $campaign['targets'] ) ? trim( (string) $campaign['targets'] ) : '';
			$order_scope_label = isset( $campaign['order_scope_label'] ) ? trim( (string) $campaign['order_scope_label'] ) : '';

			$item  = '<article class="foodord-products-campaigns__card foodord-products-campaigns__card--' . esc_attr( $type ) . '" role="listitem" data-foodord-campaign-card="1" data-foodord-campaign-card-label="' . esc_attr__( 'View active campaigns', 'foodord' ) . '">';
			$item .= '<span class="foodord-products-campaigns__icon" aria-hidden="true">';
			$item .= '<svg viewBox="0 0 64 64" fill="none" focusable="false" aria-hidden="true">';
			$item .= '<path d="M32 7.5c4.3 0 7.8 7 11.3 8.5 3.6 1.5 10.9-1.1 13.9 1.9s.5 10.2 2 13.8c1.4 3.5 8.3 7 8.3 11.3s-6.9 7.8-8.3 11.3c-1.5 3.6 1 10.8-2 13.8s-10.3.4-13.9 1.9c-3.5 1.5-7 8.5-11.3 8.5s-7.8-7-11.3-8.5c-3.6-1.5-10.9 1.1-13.9-1.9s-.5-10.2-2-13.8c-1.4-3.5-8.3-7-8.3-11.3s6.9-7.8 8.3-11.3c1.5-3.6-1-10.8 2-13.8s10.3-.4 13.9-1.9C24.2 14.5 27.7 7.5 32 7.5Z" stroke="currentColor" stroke-width="4.2" stroke-linecap="round" stroke-linejoin="round"/>';
			$item .= '<path d="M32 23.5c1.7 5.3 5.8 9.5 11.1 11.1-5.3 1.7-9.4 5.9-11.1 11.2-1.7-5.3-5.8-9.5-11.1-11.2 5.3-1.6 9.4-5.8 11.1-11.1Z" stroke="currentColor" stroke-width="4.2" stroke-linecap="round" stroke-linejoin="round"/>';
			$item .= '</svg>';
			$item .= '</span>';
			$item .= '<div class="foodord-products-campaigns__content">';

			if ( '' !== $order_scope_label ) {
				$item .= '<div class="foodord-products-campaigns__meta">';
				$item .= '<span class="foodord-products-campaigns__scope">' . esc_html( $order_scope_label ) . '</span>';
				$item .= '</div>';
			}

			$item .= '<div class="foodord-products-campaigns__body">';
			$item .= '<p class="foodord-products-campaigns__headline">' . wp_kses_post( $title ) . '</p>';

			if ( '' !== $targets ) {
				$item .= '<p class="foodord-products-campaigns__targets">' . esc_html( $targets ) . '</p>';
			}

			$item .= '</div>';
			$item .= '</div>';
			$item .= '</article>';
			$items[] = $item;
		}

		if ( empty( $items ) ) {
			return '';
		}

		$prev_label = esc_attr__( 'Scroll campaigns left', 'foodord' );
		$next_label = esc_attr__( 'Scroll campaigns right', 'foodord' );

		$output  = '<section class="foodord-products-campaigns" aria-label="' . esc_attr__( 'Active campaigns', 'foodord' ) . '">';
		$output .= '<div class="foodord-products-campaigns__rail" data-foodord-campaigns-rail>';
		$output .= '<button type="button" class="foodord-products-campaigns__arrow foodord-products-campaigns__arrow--prev" data-foodord-campaigns-prev aria-label="' . $prev_label . '">';
		$output .= '<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" focusable="false"><path d="M11.5 4.5L6 10l5.5 5.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" /></svg>';
		$output .= '</button>';
		$output .= '<div class="foodord-products-campaigns__track" data-foodord-campaigns-track role="list">';
		$output .= implode( '', $items );
		$output .= '</div>';
		$output .= '<button type="button" class="foodord-products-campaigns__arrow foodord-products-campaigns__arrow--next" data-foodord-campaigns-next aria-label="' . $next_label . '">';
		$output .= '<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" focusable="false"><path d="M8.5 4.5L14 10l-5.5 5.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" /></svg>';
		$output .= '</button>';
		$output .= '</div>';
		$output .= '</section>';

		return $output;
	}

	/**
	 * Ensure category description paragraphs carry theme class.
	 *
	 * @param string $content Raw HTML content.
	 * @return string
	 */
	protected static function format_category_description_html( $content ) {
		if ( '' === $content ) {
			return '';
		}

		return preg_replace_callback(
			'/<p(?![^>]*\bclass=)([^>]*)>/i',
			function ( $matches ) {
				$attributes = trim( $matches[1] );
				if ( '' === $attributes ) {
					return '<p class="foodord-product-category-description__text">';
				}

				return '<p class="foodord-product-category-description__text" ' . ltrim( $attributes ) . '>';
			},
			$content
		);
	}

	/**
	 * Retrieve the product image markup (thumbnail or placeholder).
	 *
	 * @param int $product_id Product ID.
	 */
	private static function get_product_image_tag( int $product_id ): string {
		if ( has_post_thumbnail( $product_id ) ) {
			return get_the_post_thumbnail( $product_id, 'woocommerce_thumbnail', array( 'class' => 'product-image' ) );
		}

		$placeholder_url = FOODORD_URL . 'public/images/no-picture.svg';
		return '<img src="' . esc_url( $placeholder_url ) . '" alt="' . esc_attr__( 'No image available', 'foodord' ) . '" class="product-image product-image-placeholder" />';
	}

	/**
	 * Format the short description with a forced break.
	 *
	 * @param WC_Product $wc_product Product instance.
	 */
	public static function get_formatted_description( WC_Product $wc_product ): string {
		$short_description_raw = $wc_product->get_short_description();
		$short_description     = trim( wp_strip_all_tags( $short_description_raw ) );
		if ( '' === $short_description ) {
			return '';
		}

		$words = preg_split( '/\s+/', $short_description );
		if ( is_array( $words ) && count( $words ) > 14 ) {
			$first_part = array_slice( $words, 0, 7 );
			$rest_part  = array_slice( $words, 7, 7 );
			return implode( ' ', $first_part ) . '<br>' . implode( ' ', $rest_part ) . '...';
		}

		if ( is_array( $words ) && count( $words ) > 7 ) {
			$first_part = array_slice( $words, 0, 7 );
			$rest_part  = array_slice( $words, 7 );
			return implode( ' ', $first_part ) . '<br>' . implode( ' ', $rest_part );
		}

		return $short_description;
	}

	/**
	 * Build WooCommerce price markup with variation handling.
	 *
	 * @param WC_Product $wc_product Product instance.
	 * @return string
	 */
	public static function get_product_price_html( WC_Product $wc_product ): string {
		if ( ! $wc_product->get_price() ) {
			return '';
		}

		$price_html = $wc_product->get_price_html();
		if ( $wc_product->is_type( 'variable' ) ) {
			$min_variation_id = 0;
			$min_variation    = null;
			$variation_prices = method_exists( $wc_product, 'get_variation_prices' ) ? $wc_product->get_variation_prices() : array();
			if ( isset( $variation_prices['price'] ) && is_array( $variation_prices['price'] ) ) {
				foreach ( $variation_prices['price'] as $variation_id => $variation_price ) {
					if ( '' === $variation_price ) {
						continue;
					}
					$variation_price_value = (float) $variation_price;
					if ( $variation_price_value <= 0 ) {
						continue;
					}
					if ( null === $min_variation || $variation_price_value < $min_variation ) {
						$min_variation_id = (int) $variation_id;
						$min_variation    = $variation_price_value;
					}
				}
			}

			if ( $min_variation_id ) {
				$min_variation_product = wc_get_product( $min_variation_id );
				if ( $min_variation_product instanceof WC_Product ) {
					$variation_price_html = $min_variation_product->get_price_html();
					if ( '' !== $variation_price_html ) {
						$price_html = '<span class="foodord-price-prefix">' . esc_html__( 'from: ', 'foodord' ) . '</span>' . $variation_price_html;
					}
				}
			}
		}

		return $price_html;
	}

	/**
	 * Build add to cart button markup.
	 *
	 * @param WC_Product $wc_product Product instance.
	 * @param int        $product_id Product ID.
	 * @param bool       $is_available Availability flag.
	 */
	private static function get_add_to_cart_button_html( WC_Product $wc_product, int $product_id, bool $is_available ): string {
		$button_classes = array( 'button', 'add_to_cart_button', 'ajax_add_to_cart' );
		$aria_label     = __( 'Add to cart', 'foodord' );
		$button_inner   = '<span class="screen-reader-text">' . esc_html__( 'Add to cart', 'foodord' ) . '</span>';
		$disabled_attr  = '';

		if ( ! $is_available ) {
			$button_classes[] = 'is-disabled';
			$button_classes[] = 'foodord-product-card__button--disabled';
			$aria_label       = __( 'Not available', 'foodord' );
			$button_inner     = '<span class="screen-reader-text">' . esc_html__( 'Not available', 'foodord' ) . '</span>';
			$disabled_attr    = ' disabled="disabled" aria-disabled="true" tabindex="-1"';
		}

		return '<button class="' . esc_attr( implode( ' ', $button_classes ) ) . '" type="button" data-product_id="' . esc_attr( $product_id ) . '" data-product_sku="' . esc_attr( $wc_product->get_sku() ) . '" data-quantity="1" aria-label="' . esc_attr( $aria_label ) . '" data-foodord-direct-add="0"' . $disabled_attr . '>' . $button_inner . '</button>';
	}

	/**
	 * Build allergy trigger markup when available.
	 *
	 * @param int  $product_id Product ID.
	 * @param bool $has_product_allergens Whether specific allergens exist.
	 * @param bool $allergy_info_active   Whether global allergy info is available.
	 */
	private static function get_allergy_trigger_html( int $product_id, bool $has_product_allergens, bool $allergy_info_active ): string {
		$show_allergy_trigger = $has_product_allergens || $allergy_info_active;
		if ( ! $show_allergy_trigger ) {
			return '';
		}

		return '<button type="button" class="foodord-allergy-trigger" aria-label="' . esc_attr__( 'Open Allergy Info', 'foodord' ) . '" data-allergy-trigger="true" data-product-id="' . esc_attr( $product_id ) . '" data-has-product-allergens="' . ( $has_product_allergens ? '1' : '0' ) . '"><span class="foodord-allergy-trigger__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span></button>';
	}

	/**
	 * Highlight badges for featured/on-sale products.
	 *
	 * @param WC_Product $product Product instance.
	 * @return array<int,array<string,string>>
	 */
	private static function get_highlight_badges( WC_Product $product ): array {
		$badges = array();

		if ( $product->is_featured() ) {
			$badges[] = array(
				'label' => esc_html__( 'Popular', 'foodord' ),
				'class' => 'badge-popular',
			);
		}

		if ( $product->is_on_sale() ) {
			$badges[] = array(
				'label' => esc_html__( 'On sale', 'foodord' ),
				'class' => 'badge-sale',
			);
		}

		return $badges;
	}

	/**
	 * Render the grid version of the product card.
	 *
	 * @param array $card Contextual card data.
	 */
	private static function render_grid_card( array $card ): string {
		$card_classes         = isset( $card['card_classes'] ) && is_array( $card['card_classes'] ) ? $card['card_classes'] : array( 'foodord-product-card' );
		$card_classes[]       = 'foodord-product-card--grid';
		$card_classes[]       = 'foodord-product-card-alt';
		$card_attrs           = isset( $card['card_attrs'] ) && is_array( $card['card_attrs'] ) ? $card['card_attrs'] : array();
		$attr_string          = implode( '', $card_attrs );
		$status_badge_html    = isset( $card['status_badge_html'] ) ? (string) $card['status_badge_html'] : '';
		$image_tag            = isset( $card['image_tag'] ) ? (string) $card['image_tag'] : '';
		$product_title        = isset( $card['product_title'] ) ? (string) $card['product_title'] : '';
		$allergy_trigger_html = isset( $card['allergy_trigger_html'] ) ? (string) $card['allergy_trigger_html'] : '';
		$product_labels_html  = isset( $card['product_labels_html'] ) ? (string) $card['product_labels_html'] : '';
		$description_text     = isset( $card['description_text'] ) ? (string) $card['description_text'] : '';
		$price_html           = isset( $card['price_html'] ) ? (string) $card['price_html'] : '';
		$price_label          = isset( $card['price_label'] ) ? (string) $card['price_label'] : '';
		$button_html          = isset( $card['button_html'] ) ? (string) $card['button_html'] : '';
		$show_images          = ! empty( $card['show_images'] );
		$highlight_badges     = isset( $card['highlight_badges'] ) && is_array( $card['highlight_badges'] ) ? $card['highlight_badges'] : array();

		$output = '<div class="' . esc_attr( implode( ' ', $card_classes ) ) . '" data-foodord-card-layout="grid"' . $attr_string . '>';
		if ( '' !== $status_badge_html ) {
			$output .= $status_badge_html;
		}

		if ( $show_images && '' !== $image_tag ) {
			$output .= '<div class="card-image-wrap">';
			if ( ! empty( $highlight_badges ) ) {
				$output .= '<div class="card-badges">';
				foreach ( $highlight_badges as $badge ) {
					$label = isset( $badge['label'] ) ? (string) $badge['label'] : '';
					$class = isset( $badge['class'] ) ? (string) $badge['class'] : '';
					if ( '' === $label ) {
						continue;
					}
					$output .= '<span class="' . esc_attr( $class ) . '">' . esc_html( $label ) . '</span>';
				}
				$output .= '</div>';
			}
			$output .= '<div class="product-image-container"><div class="product-image-wrapper">' . wp_kses_post( $image_tag ) . '</div></div>';
			$output .= '</div>';
		}

		$output .= '<div class="card-content">';
		$output .= '<div class="product-info">';
		$output .= '<div class="product-top">';
		$output .= '<div class="product-title-row card-header">';
		$output .= '<h3 class="product-title card-title">' . esc_html( $product_title ) . '</h3>';
		$output .= $allergy_trigger_html;
		$output .= '</div>';

		if ( '' !== $description_text ) {
			$output .= '<div class="product-description">';
			$output .= '<p class="product-description-text card-description">' . wp_kses( $description_text, array( 'br' => array() ) ) . '</p>';
			$output .= '</div>';
		}

		$output .= '</div>';
		$output .= '<div class="product-bottom card-footer">';
		if ( '' !== $price_html || '' !== $product_labels_html ) {
			$output .= '<div class="product-price-container card-price">';
			if ( '' !== $price_html ) {
				$output .= '<div class="foodord-product-card__price-main">';
				if ( '' !== $price_label ) {
					$output .= '<span class="price-label">' . esc_html( $price_label ) . '</span>';
				}
				$output .= '<div class="product-price price-value">' . wp_kses_post( $price_html ) . '</div>';
				$output .= '</div>';
			}
			$output .= $product_labels_html;
			$output .= '</div>';
		}

		if ( '' !== $button_html ) {
			$output .= '<div class="product-actions">';
			$output .= $button_html;
			$output .= '</div>';
		}

		$output .= '</div>';
		$output .= '</div>';
		$output .= '</div>';
		$output .= self::get_product_card_skeleton_markup( 'grid' );
		$output .= '</div>';

		return $output;
	}

	/**
	 * Render the streamline version of the product card.
	 *
	 * @param array $card Contextual card data.
	 */
	private static function render_streamline_card( array $card ): string {
		$card_classes         = isset( $card['card_classes'] ) && is_array( $card['card_classes'] ) ? $card['card_classes'] : array( 'foodord-product-card' );
		$card_classes[]       = 'foodord-product-card--streamline';
		$card_attrs           = isset( $card['card_attrs'] ) && is_array( $card['card_attrs'] ) ? $card['card_attrs'] : array();
		$attr_string          = implode( '', $card_attrs );
		$status_badge_html    = isset( $card['status_badge_html'] ) ? (string) $card['status_badge_html'] : '';
		$image_tag            = isset( $card['image_tag'] ) ? (string) $card['image_tag'] : '';
		$product_title        = isset( $card['product_title'] ) ? (string) $card['product_title'] : '';
		$allergy_trigger_html = isset( $card['allergy_trigger_html'] ) ? (string) $card['allergy_trigger_html'] : '';
		$product_labels_html  = isset( $card['product_labels_html'] ) ? (string) $card['product_labels_html'] : '';
		$description_text     = isset( $card['description_text'] ) ? (string) $card['description_text'] : '';
		$price_html           = isset( $card['price_html'] ) ? (string) $card['price_html'] : '';
		$button_html          = isset( $card['button_html'] ) ? (string) $card['button_html'] : '';
		$show_images          = ! empty( $card['show_images'] );

		$output = '<div class="' . esc_attr( implode( ' ', $card_classes ) ) . '" data-foodord-card-layout="streamline"' . $attr_string . '>';

		if ( '' !== $status_badge_html ) {
			$output .= $status_badge_html;
		}

		$output .= '<div class="product-info">';
		$output .= '<div class="product-top">';
		$output .= '<div class="product-title-row">';
		$output .= '<h3 class="product-title">' . esc_html( $product_title ) . '</h3>';
		$output .= $allergy_trigger_html;
		$output .= '</div>';

		if ( '' !== $description_text ) {
			$output .= '<div class="product-description">';
			$output .= '<p class="product-description-text">' . wp_kses( $description_text, array( 'br' => array() ) ) . '</p>';
			$output .= '</div>';
		}
		$output .= '</div>';

		$output .= '<div class="product-bottom">';
		if ( '' !== $price_html || '' !== $product_labels_html ) {
			$output .= '<div class="product-price-container">';
			if ( '' !== $price_html ) {
				$output .= '<div class="foodord-product-card__price-main">';
				$output .= '<div class="product-price">' . wp_kses_post( $price_html ) . '</div>';
				$output .= '</div>';
			}
			$output .= $product_labels_html;
			$output .= '</div>';
		}
		$output .= '</div>';
		$output .= '</div>'; // .product-info

		if ( $show_images && '' !== $image_tag ) {
			$output .= '<div class="product-image-container">';
			$output .= '<div class="product-image-wrapper">';
			$output .= wp_kses_post( $image_tag );
			$output .= '</div>';
			$output .= '</div>';
		}

		if ( '' !== $button_html ) {
			$output .= '<div class="product-actions">';
			$output .= $button_html;
			$output .= '</div>';
		}

		$output .= self::get_product_card_skeleton_markup( 'streamline' );
		$output .= '</div>';

		return $output;
	}

	/**
	 * Render the frosted version of the product card.
	 *
	 * @param array $card Contextual card data.
	 */
	private static function render_frosted_card( array $card ): string {
		$card_classes         = isset( $card['card_classes'] ) && is_array( $card['card_classes'] ) ? $card['card_classes'] : array( 'foodord-product-card' );
		$card_classes[]       = 'foodord-product-card--frosted';
		$card_attrs           = isset( $card['card_attrs'] ) && is_array( $card['card_attrs'] ) ? $card['card_attrs'] : array();
		$attr_string          = implode( '', $card_attrs );
		$status_badge_html    = isset( $card['status_badge_html'] ) ? (string) $card['status_badge_html'] : '';
		$image_tag            = isset( $card['image_tag'] ) ? (string) $card['image_tag'] : '';
		$product_title        = isset( $card['product_title'] ) ? (string) $card['product_title'] : '';
		$allergy_trigger_html = isset( $card['allergy_trigger_html'] ) ? (string) $card['allergy_trigger_html'] : '';
		$product_labels_html  = isset( $card['product_labels_html'] ) ? (string) $card['product_labels_html'] : '';
		$description_text     = isset( $card['description_text'] ) ? (string) $card['description_text'] : '';
		$price_html           = isset( $card['price_html'] ) ? (string) $card['price_html'] : '';
		$button_html          = isset( $card['button_html'] ) ? (string) $card['button_html'] : '';
		$show_images          = ! empty( $card['show_images'] );

		$output = '<div class="' . esc_attr( implode( ' ', $card_classes ) ) . '" data-foodord-card-layout="frosted"' . $attr_string . '>';
		$output .= '<div class="foodord-product-card__surface" aria-hidden="true"></div>';

		if ( '' !== $status_badge_html ) {
			$output .= $status_badge_html;
		}

		if ( $show_images && '' !== $image_tag ) {
			$output .= '<div class="product-image-container">';
			$output .= '<div class="product-image-wrapper">';
			$output .= wp_kses_post( $image_tag );
			$output .= '</div>';
			$output .= '</div>';
		}

		$output .= '<div class="product-info">';
		$output .= '<div class="product-top">';
		$output .= '<div class="product-title-row">';
		$output .= '<h3 class="product-title">' . esc_html( $product_title ) . '</h3>';
		$output .= $allergy_trigger_html;
		$output .= '</div>';

		if ( '' !== $description_text ) {
			$output .= '<div class="product-description">';
			$output .= '<p class="product-description-text">' . wp_kses( $description_text, array( 'br' => array() ) ) . '</p>';
			$output .= '</div>';
		}

		$output .= '</div>';
		$output .= '<div class="product-bottom">';
		if ( '' !== $price_html || '' !== $product_labels_html ) {
			$output .= '<div class="product-price-container">';
			if ( '' !== $price_html ) {
				$output .= '<div class="foodord-product-card__price-main">';
				$output .= '<div class="product-price">' . wp_kses_post( $price_html ) . '</div>';
				$output .= '</div>';
			}
			$output .= $product_labels_html;
			$output .= '</div>';
		}

		if ( '' !== $button_html ) {
			$output .= '<div class="product-actions">';
			$output .= $button_html;
			$output .= '</div>';
		}

		$output .= '</div>';
		$output .= '</div>';
		$output .= self::get_product_card_skeleton_markup( 'frosted' );
		$output .= '</div>';

		return $output;
	}

	/**
	 * Build skeleton overlay markup for product card layouts.
	 *
	 * @param string $layout Layout slug.
	 * @return string
	 */
	private static function get_product_card_skeleton_markup( string $layout ): string {
		$layout = in_array( $layout, array( 'classic', 'grid', 'streamline', 'frosted' ), true ) ? $layout : 'classic';

		$output  = '<div class="foodord-product-card__skeleton-overlay" aria-hidden="true">';
		$output .= '<div class="foodord-product-card__skeleton foodord-product-card__skeleton--' . esc_attr( $layout ) . '">';

		if ( 'grid' === $layout ) {
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-image foodord-product-card__skeleton-image--grid"></div>';
			$output .= '<div class="foodord-product-card__skeleton-content">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-title foodord-product-card__skeleton-title--wide"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-title foodord-product-card__skeleton-title--short"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text"></div>';
			$output .= '<div class="foodord-product-card__skeleton-footer">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-price"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-button foodord-product-card__skeleton-button--icon"></div>';
			$output .= '</div>';
			$output .= '</div>';
		} elseif ( 'streamline' === $layout ) {
			$output .= '<div class="foodord-product-card__skeleton-content foodord-product-card__skeleton-content--streamline">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-title foodord-product-card__skeleton-title--wide"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text foodord-product-card__skeleton-text--short"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-price"></div>';
			$output .= '</div>';
			$output .= '<div class="foodord-product-card__skeleton-side">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-image foodord-product-card__skeleton-image--streamline"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-button foodord-product-card__skeleton-button--fab"></div>';
			$output .= '</div>';
		} elseif ( 'frosted' === $layout ) {
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-image foodord-product-card__skeleton-image--frosted"></div>';
			$output .= '<div class="foodord-product-card__skeleton-content foodord-product-card__skeleton-content--frosted">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-title foodord-product-card__skeleton-title--wide"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text foodord-product-card__skeleton-text--short"></div>';
			$output .= '<div class="foodord-product-card__skeleton-footer">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-price"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-button"></div>';
			$output .= '</div>';
			$output .= '</div>';
		} else {
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-image foodord-product-card__skeleton-image--classic"></div>';
			$output .= '<div class="foodord-product-card__skeleton-content">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-title foodord-product-card__skeleton-title--wide"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-text foodord-product-card__skeleton-text--short"></div>';
			$output .= '<div class="foodord-product-card__skeleton-footer">';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-price"></div>';
			$output .= '<div class="foodord-skeleton foodord-product-card__skeleton-button"></div>';
			$output .= '</div>';
			$output .= '</div>';
		}

		$output .= '</div>';
		$output .= '</div>';

		return $output;
	}

	/**
	 * Get products data for block rendering
	 *
	 * @param array $atts Attributes for getting products data.
	 * @return array Products data
	 */
	public static function get_products_data( $atts = array() ) {
		// Check if WooCommerce is active.
		if ( ! function_exists( 'wc' ) ) {
			return array();
		}

		// Parse attributes with defaults.
		$atts = wp_parse_args(
			$atts,
			array(
				'limit'   => -1,
				'columns' => 4,
				'orderby' => 'menu_order',
				'order'   => 'ASC',
			)
		);

		// Sanitize attributes.
		$limit   = intval( $atts['limit'] );
		$orderby = sanitize_text_field( $atts['orderby'] );
		$order   = sanitize_text_field( $atts['order'] );

		// Get products.
		$args = array(
			'post_type'      => 'product',
			'post_status'    => 'publish',
			'posts_per_page' => $limit,
			'orderby'        => $orderby,
			'order'          => $order,
		);

		$products      = new WP_Query( $args );
		$products_data = array();

		if ( $products->have_posts() ) {
			while ( $products->have_posts() ) {
				$products->the_post();
				global $product;

				// Prepare price HTML with variable product handling for editor/consumer parity.
				$price_html = $product->get_price_html();
				if ( $product->is_type( 'variable' ) ) {
					$min_price           = $product->get_variation_price( 'min', false );
					$display_min_price   = wc_get_price_to_display( $product, array( 'price' => $min_price ) );
					$formatted_min_price = wc_price( $display_min_price );
					$price_html          = '<span class="foodord-price-prefix">' . esc_html__( 'from: ', 'foodord' ) . '</span>' . $formatted_min_price;
				}

				$products_data[] = array(
					'id'         => get_the_ID(),
					'title'      => get_the_title(),
					'permalink'  => get_permalink(),
					'image'      => has_post_thumbnail() ? get_the_post_thumbnail_url( get_the_ID(), 'woocommerce_thumbnail' ) : '',
					'price_html' => $price_html,
					'has_price'  => ! empty( $product->get_price() ),
				);
			}
		}

		// Reset post data.
		wp_reset_postdata();

		return $products_data;
	}
}
