<?php
/**
 * Product Grid Widget — render functions
 * All renderer methods from the old GGM_Commerce_Product_Grid_Renderer class,
 * converted to standalone functions prefixed ggmc_product_grid_.
 *
 * Also contains:
 *   ggmc_product_grid_get_available_components() — moved from controls class
 *   ggmc_product_grid_map_settings_to_args()     — moved from widget class
 */

// If this file is called directly, abort.
if (!defined('ABSPATH')) {
    exit;
}

// -----------------------------------------------------------------------
// Data-handler instantiation helper (used by render functions that need it)
// -----------------------------------------------------------------------
function ggmc_product_grid_get_data_handler() {
    static $handler = null;
    if ($handler === null) {
        $handler = new GGM_Commerce_Product_Data_Handler();
    }
    return $handler;
}

// -----------------------------------------------------------------------
// Available components (moved from GGM_Commerce_Product_Grid_Controls)
// -----------------------------------------------------------------------

/**
 * Get available components based on activated premium features.
 *
 * @return array List of available components for the component_order repeater.
 */
function ggmc_product_grid_get_available_components() {
    // Check premium feature activation
    $wishlist_active = \GGM_Commerce_License::instance()->is_widget_activated('wishlist');
    $compare_active  = \GGM_Commerce_License::instance()->is_widget_activated('compare');
    $reviews_active  = \GGM_Commerce_License::instance()->is_widget_activated('product-reviews');

    $components = [
        [
            'component_id'    => 'image',
            'component_label' => __('Image', 'ggm-commerce'),
        ],
        [
            'component_id'    => 'badges',
            'component_label' => __('Badges', 'ggm-commerce'),
        ],
        [
            'component_id'    => 'categories',
            'component_label' => __('Categories', 'ggm-commerce'),
        ],
        [
            'component_id'    => 'title',
            'component_label' => __('Title', 'ggm-commerce'),
        ],
    ];

    // Only include rating if product-reviews is activated
    if ($reviews_active) {
        $components[] = [
            'component_id'    => 'rating',
            'component_label' => __('Rating', 'ggm-commerce'),
        ];
    }

    $components[] = [
        'component_id'    => 'price',
        'component_label' => __('Price', 'ggm-commerce'),
    ];

    $components[] = [
        'component_id'    => 'description',
        'component_label' => __('Description', 'ggm-commerce'),
    ];

    $components[] = [
        'component_id'    => 'stock',
        'component_label' => __('Stock Status', 'ggm-commerce'),
    ];

    $components[] = [
        'component_id'    => 'details_link',
        'component_label' => __('Details Link', 'ggm-commerce'),
    ];

    // Build actions label based on what's available
    $actions_parts = [__('Cart', 'ggm-commerce')];
    if ($wishlist_active) {
        $actions_parts[] = __('Wishlist', 'ggm-commerce');
    }
    if ($compare_active) {
        $actions_parts[] = __('Compare', 'ggm-commerce');
    }
    $actions_label = implode(', ', $actions_parts);

    $components[] = [
        'component_id'    => 'actions',
        'component_label' => sprintf(
            /* translators: %s: actions label, e.g. "Cart, Wishlist" */
            __('Actions (%s)', 'ggm-commerce'),
            $actions_label
        ),
    ];

    return $components;
}

// -----------------------------------------------------------------------
// Settings-to-args mapper (moved from widget class)
// -----------------------------------------------------------------------

/**
 * Map Elementor widget settings to data handler arguments.
 *
 * @param array $settings Widget settings from get_settings_for_display().
 * @return array Args array suitable for GGM_Commerce_Product_Data_Handler::get_products().
 */
function ggmc_product_grid_map_settings_to_args( $settings ) {
    // For client-side filtering to work, we need ALL products loaded.
    // JavaScript will handle pagination display.
    // DO NOT apply category/tag filters here - they should only be applied client-side
    // by the filter widget, otherwise filtered products won't be in the DOM.
    $per_page = intval($settings['products_per_page']) ?: 12;

    $category_filter = [];
    $tag_filter      = [];

    // Check if filtering by current taxonomy (for archive pages)
    if (($settings['filter_by_current_taxonomy'] ?? 'no') === 'yes') {
        $current_category_id = GGM_Commerce_Helpers::get_current_category_id();
        if ($current_category_id) {
            $category_filter = [$current_category_id];
        }

        $current_tag_id = GGM_Commerce_Helpers::get_current_tag_id();
        if ($current_tag_id) {
            $tag_filter = [$current_tag_id];
        }
    }

    return [
        'posts_per_page'  => -1, // Load ALL products for client-side filtering
        'orderby'         => $settings['orderby'] ?? 'created_at',
        'order'           => $settings['order'] ?? 'DESC',
        'featured_only'   => ($settings['featured_only'] ?? 'no') === 'yes',
        'category_filter' => $category_filter,
        'tag_filter'      => $tag_filter,
        'stock_status'    => $settings['stock_filter'] ?? 'all',
        'price_min'       => $settings['price_min'] ?? '',
        'price_max'       => $settings['price_max'] ?? '',
        'search'          => $settings['search_term'] ?? '',
        'attribute_filters' => !empty($settings['filter_by_attribute']) && !empty($settings['filter_by_attribute_value'])
            ? [intval($settings['filter_by_attribute']) => $settings['filter_by_attribute_value']]
            : [],
        'offset'          => 0, // No offset needed when loading all
    ];
}

// -----------------------------------------------------------------------
// Main render entry-point
// -----------------------------------------------------------------------

/**
 * Render the complete product grid.
 *
 * @param \GGM_Commerce_Product_Grid_Widget $widget The widget instance.
 */
function ggmc_product_grid_render( $widget ) {
    $settings = $widget->get_settings_for_display();
    $args     = ggmc_product_grid_map_settings_to_args( $settings );

    $data_handler = ggmc_product_grid_get_data_handler();
    $products     = $data_handler->get_products( $args );

    if (empty($products)) {
        ggmc_product_grid_render_empty_state();
        return;
    }

    ggmc_product_grid_render_grid_wrapper_start( $settings );
    ggmc_product_grid_render_products( $products, $settings );
    ggmc_product_grid_render_pagination( $settings );
    ggmc_product_grid_render_grid_wrapper_end();
    ggmc_product_grid_render_grid_styles( $settings, $widget );
}

// -----------------------------------------------------------------------
// Grid structure helpers
// -----------------------------------------------------------------------

/**
 * Render empty state when no products found.
 */
function ggmc_product_grid_render_empty_state() {
    if (ggmc_is_editor_mode()) {
        echo '<p style="text-align: center; color: var(--ggmc-text-muted, #999); padding: var(--ggmc-spacing-xl, 32px);">No products found. Create some products to see them here.</p>';
    }
}

/**
 * Render grid wrapper opening tags.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_grid_render_grid_wrapper_start( $settings ) {
    $layout_class    = 'layout-' . ($settings['grid_style'] ?? 'standard');
    $per_page        = intval($settings['products_per_page']) ?: 12;
    $sorting_enabled = ($settings['enable_sorting'] ?? 'no') === 'yes' ? 'yes' : 'no';

    echo '<div class="ggmc-product-grid-container ' . esc_attr($layout_class) . '">';

    if ($settings['enable_sorting'] === 'yes') {
        ggmc_product_grid_render_sorting_controls( $settings );
    }

    echo '<div class="ggmc-products-grid" ';
    echo 'data-products-per-page="' . esc_attr($per_page) . '" ';
    echo 'data-sorting-enabled="' . esc_attr($sorting_enabled) . '">';
}

/**
 * Render grid wrapper closing tags.
 * (ggmc-products-grid and ggmc-product-grid-container are closed in render_pagination)
 */
function ggmc_product_grid_render_grid_wrapper_end() {
    // Both divs are already closed inside ggmc_product_grid_render_pagination().
}

// -----------------------------------------------------------------------
// Products loop
// -----------------------------------------------------------------------

/**
 * Render all products.
 *
 * @param array $products List of product objects.
 * @param array $settings Widget settings.
 */
function ggmc_product_grid_render_products( $products, $settings ) {
    $hover_class = !empty($settings['item_hover_animation']) ? 'elementor-animation-' . $settings['item_hover_animation'] : '';
    $per_page    = intval($settings['products_per_page']) ?: 12;
    $index       = 0;

    foreach ($products as $product) {
        $index++;
        $initial_hide = ($index > $per_page) ? ' style="display: none;"' : '';
        ggmc_product_grid_render_product_item( $product, $settings, $hover_class, $initial_hide );
    }
}

/**
 * Render a single product item.
 *
 * @param object $product      Product object.
 * @param array  $settings     Widget settings.
 * @param string $hover_class  CSS class for hover animation.
 * @param string $initial_hide Inline style string or empty string.
 */
function ggmc_product_grid_render_product_item( $product, $settings, $hover_class, $initial_hide = '' ) {
    $product_url = GGM_Commerce_Helpers::get_product_details_url($product->id);
    $grid_style  = $settings['grid_style'] ?? 'standard';

    $columns         = $settings['columns'] ?? '3';
    $is_single_column = ($columns === '1');
    $layout_style    = $is_single_column ? ($settings['layout_style'] ?? 'horizontal') : '';
    $image_position  = $is_single_column ? ($settings['image_position'] ?? 'left') : '';

    $taxonomy          = GGM_Commerce_Taxonomy::get_instance();
    $product_categories = $taxonomy->get_product_categories($product->id);
    $product_tags      = $taxonomy->get_product_tags($product->id);

    $category_ids = array_map(function($cat) { return $cat->id; }, $product_categories);
    $tag_ids      = array_map(function($tag) { return $tag->id; }, $product_tags);

    $created_at = isset($product->created_at) ? strtotime($product->created_at) : 0;
    $name_sort  = strtolower($product->name);
    $price_sort = floatval($product->price);
    $stock_sort = intval($product->stock);

    $item_classes = ['product-grid-item', esc_attr($hover_class)];
    if ($is_single_column && $layout_style) {
        $item_classes[] = 'list-layout-' . esc_attr($layout_style);
        if ($layout_style === 'horizontal') {
            $item_classes[] = 'image-' . esc_attr($image_position);
        }
    } else {
        $item_classes[] = 'grid-style-' . esc_attr($grid_style);
    }

    echo '<div class="' . esc_attr(implode(' ', $item_classes)) . '" ';
    echo 'data-product-id="' . esc_attr($product->id) . '" ';
    echo 'data-category-ids="' . esc_attr(implode(',', $category_ids)) . '" ';
    echo 'data-tag-ids="' . esc_attr(implode(',', $tag_ids)) . '" ';
    echo 'data-price="' . esc_attr($product->price) . '" ';
    echo 'data-created-at="' . esc_attr($created_at) . '" ';
    echo 'data-name="' . esc_attr($name_sort) . '" ';
    echo 'data-price-sort="' . esc_attr($price_sort) . '" ';
    echo 'data-stock="' . esc_attr($stock_sort) . '"';
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $initial_hide is a safe style attribute or empty string
    echo $initial_hide . '>';

    ggmc_product_grid_render_ordered_components( $product, $product_url, $settings );

    echo '</div>';
}

// -----------------------------------------------------------------------
// Component rendering
// -----------------------------------------------------------------------

/**
 * Render components in the order specified by settings.
 *
 * @param object $product     The product object.
 * @param string $product_url The product URL.
 * @param array  $settings    Widget settings.
 */
function ggmc_product_grid_render_ordered_components( $product, $product_url, $settings ) {
    $component_order = $settings['component_order'] ?? ggmc_product_grid_get_default_component_order();
    $component_order = ggmc_product_grid_ensure_all_components_present( $component_order );

    $valid_components = ['image', 'badges', 'categories', 'title', 'rating', 'price', 'description', 'stock', 'details_link', 'actions'];

    // Render image first, outside the content wrapper
    if (ggmc_product_grid_is_component_visible( 'image', $settings ) && $product->image_id) {
        ggmc_product_grid_render_product_image_with_order( $product, GGM_Commerce_Helpers::get_product_details_url($product->id), $settings, 0 );
    }

    // Open content wrapper for all non-image components
    echo '<div class="product-content">';

    foreach ($component_order as $index => $component) {
        $component_id = $component['component_id'] ?? '';
        if (empty($component_id) || !in_array($component_id, $valid_components, true)) {
            continue;
        }

        // Skip image — already rendered above
        if ($component_id === 'image') {
            continue;
        }

        if (!ggmc_product_grid_is_component_visible( $component_id, $settings )) {
            continue;
        }

        if ($component_id === 'description' && empty($product->short_description)) {
            continue;
        }

        switch ($component_id) {
            case 'badges':
                ggmc_product_grid_render_product_badges_with_order( $product, $settings, $index );
                break;
            case 'categories':
                ggmc_product_grid_render_product_categories_with_order( $product, $settings, $index );
                break;
            case 'title':
                ggmc_product_grid_render_product_title_with_order( $product, GGM_Commerce_Helpers::get_product_details_url($product->id), $settings, $index );
                break;
            case 'rating':
                ggmc_product_grid_render_product_rating_with_order( $product, $settings, $index );
                break;
            case 'price':
                ggmc_product_grid_render_product_price_with_order( $product, $settings, $index );
                break;
            case 'description':
                ggmc_product_grid_render_product_description_with_order( $product, $settings, $index );
                break;
            case 'stock':
                ggmc_product_grid_render_product_stock_with_order( $product, $settings, $index );
                break;
            case 'details_link':
                ggmc_product_grid_render_details_link_with_order( $product, GGM_Commerce_Helpers::get_product_details_url($product->id), $settings, $index );
                break;
            case 'actions':
                ggmc_product_grid_render_add_to_cart_button_with_order( $product, $settings, $index );
                break;
        }
    }

    echo '</div>'; // .product-content
}

/**
 * Check if a component is visible based on the show_* settings.
 *
 * @param string $component_id The component identifier.
 * @param array  $settings     Widget settings.
 * @return bool Whether the component should be displayed.
 */
function ggmc_product_grid_is_component_visible( $component_id, $settings ) {
    $setting_map = [
        'image'       => 'show_image',
        'badges'      => 'show_badge',
        'categories'  => 'show_categories',
        'title'       => 'show_title',
        'rating'      => 'show_rating',
        'price'       => 'show_price',
        'description' => 'show_description',
        'stock'        => 'show_stock',
        'details_link' => 'show_details_link',
        'actions'      => 'show_add_to_cart',
    ];

    $setting_key = $setting_map[$component_id] ?? '';
    if (empty($setting_key)) {
        return true;
    }

    return ($settings[$setting_key] ?? 'yes') === 'yes';
}

/**
 * Get the default component order.
 *
 * @return array Default component order.
 */
function ggmc_product_grid_get_default_component_order() {
    return [
        ['component_id' => 'image',       'component_label' => __('Image', 'ggm-commerce')],
        ['component_id' => 'badges',      'component_label' => __('Badges', 'ggm-commerce')],
        ['component_id' => 'categories',  'component_label' => __('Categories', 'ggm-commerce')],
        ['component_id' => 'title',       'component_label' => __('Title', 'ggm-commerce')],
        ['component_id' => 'rating',      'component_label' => __('Rating', 'ggm-commerce')],
        ['component_id' => 'price',       'component_label' => __('Price', 'ggm-commerce')],
        ['component_id' => 'description', 'component_label' => __('Description', 'ggm-commerce')],
        ['component_id' => 'stock',        'component_label' => __('Stock Status', 'ggm-commerce')],
        ['component_id' => 'details_link', 'component_label' => __('Details Link', 'ggm-commerce')],
        ['component_id' => 'actions',      'component_label' => __('Actions (Cart, Wishlist, Compare)', 'ggm-commerce')],
    ];
}

/**
 * Ensure all components are present in the order array.
 * This handles widgets saved before new components were added.
 *
 * @param array $component_order The saved component order.
 * @return array Component order with any missing components added.
 */
function ggmc_product_grid_ensure_all_components_present( $component_order ) {
    $default_order = ggmc_product_grid_get_default_component_order();

    $existing_ids = [];
    foreach ($component_order as $component) {
        if (isset($component['component_id'])) {
            $existing_ids[] = $component['component_id'];
        }
    }

    $missing_components = [];
    foreach ($default_order as $default_component) {
        if (!in_array($default_component['component_id'], $existing_ids, true)) {
            $missing_components[] = $default_component;
        }
    }

    if (!empty($missing_components)) {
        $component_order = array_merge($missing_components, $component_order);
    }

    return $component_order;
}

// -----------------------------------------------------------------------
// Individual component renderers (with order/CSS-order)
// -----------------------------------------------------------------------

/**
 * Render product image with CSS order.
 *
 * @param object $product     Product object.
 * @param string $product_url Product URL.
 * @param array  $settings    Widget settings.
 * @param int    $order       CSS order value.
 */
function ggmc_product_grid_render_product_image_with_order( $product, $product_url, $settings, $order ) {
    $image_hover = $settings['image_hover_effect'] ?? 'none';

    echo '<div class="product-image hover-' . esc_attr($image_hover) . '" style="order: ' . esc_attr($order) . ';">';
    echo '<a href="' . esc_url($product_url) . '">';
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- ggmc_product_grid_get_product_image_html() returns pre-escaped HTML
    echo ggmc_product_grid_get_product_image_html( $product->image_id, 'medium', esc_attr($product->name) );
    echo '</a>';

    if ($settings['show_quick_view'] === 'yes' && class_exists('GGM_Commerce_Quick_View') && \GGM_Commerce_Quick_View::is_activated()) {
        echo '<div class="quick-view-overlay">';
        echo '<button class="quick-view-btn" data-product-id="' . esc_attr($product->id) . '">';
        echo '<i class="eicon-zoom-in"></i>';
        echo '</button>';
        echo '</div>';
    }

    echo '</div>';
}

/**
 * Render product badges with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_product_badges_with_order( $product, $settings, $order ) {
    echo '<div class="product-badges" style="order: ' . esc_attr($order) . ';">';

    if ($product->featured) {
        echo '<span class="badge badge-featured">Featured</span>';
    }

    if ($product->sale_price && $product->sale_price < $product->price) {
        $discount_percent = round((($product->price - $product->sale_price) / $product->price) * 100);
        echo '<span class="badge badge-sale">-' . esc_html($discount_percent) . '%</span>';
    }

    if (!empty($product->manage_stock) && $product->stock <= 0) {
        echo '<span class="badge badge-out-of-stock">Out of Stock</span>';
    }

    $created_date = new DateTime($product->created_at);
    $now          = new DateTime();
    $days_old     = $now->diff($created_date)->days;
    if ($days_old <= 30) {
        echo '<span class="badge badge-new">New</span>';
    }

    echo '</div>';
}

/**
 * Render product categories with CSS order.
 *
 * @param object $product Product object.
 * @param int    $order   CSS order value.
 */
function ggmc_product_grid_render_product_categories_with_order( $product, $settings, $order ) {
    $link = ! empty( $settings['link_categories'] ) && 'yes' === $settings['link_categories'];
    echo '<div class="product-categories" style="order: ' . esc_attr($order) . ';">';
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- ggmc_product_grid_get_product_categories() returns pre-escaped HTML
    echo ggmc_product_grid_get_product_categories( $product->id, $link );
    echo '</div>';
}

/**
 * Render product title with CSS order.
 *
 * @param object $product     Product object.
 * @param string $product_url Product URL.
 * @param array  $settings    Widget settings.
 * @param int    $order       CSS order value.
 */
function ggmc_product_grid_render_product_title_with_order( $product, $product_url, $settings, $order ) {
    $title_tag = $settings['title_html_tag'] ?? 'h3';
    echo '<' . esc_attr($title_tag) . ' class="product-title" style="order: ' . esc_attr($order) . ';">';
    echo '<a href="' . esc_url($product_url) . '">' . esc_html($product->name) . '</a>';
    echo '</' . esc_attr($title_tag) . '>';
}

/**
 * Render product rating with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_product_rating_with_order( $product, $settings, $order ) {
    $license = ggm_commerce_license();
    if (!$license->is_widget_activated('product-reviews')) {
        return;
    }

    $rating       = isset($product->average_rating) ? floatval($product->average_rating) : 0;
    $rating_count = isset($product->rating_count) ? intval($product->rating_count) : 0;

    $full_stars  = floor($rating);
    $half_star   = ($rating - $full_stars) >= 0.5;
    $empty_stars = 5 - $full_stars - ($half_star ? 1 : 0);

    $before_text = isset($settings['rating_before_text']) ? $settings['rating_before_text'] : '(';
    $after_text  = isset($settings['rating_after_text'])  ? $settings['rating_after_text']  : ')';

    echo '<div class="product-rating" style="order: ' . esc_attr($order) . ';">';

    for ($i = 0; $i < $full_stars; $i++) {
        echo '<i class="eicon-star star"></i>';
    }

    if ($half_star) {
        echo '<i class="eicon-star-half star"></i>';
    }

    for ($i = 0; $i < $empty_stars; $i++) {
        echo '<i class="eicon-star-o star empty"></i>';
    }

    if ($rating > 0) {
        echo '<span class="rating-text">' . esc_html($before_text) . number_format($rating, 1);
        if ($rating_count > 0) {
            echo ' <span class="rating-count">' . esc_html($rating_count) . '</span>';
        }
        echo esc_html($after_text) . '</span>';
    } else {
        echo '<span class="rating-text no-rating">No reviews</span>';
    }

    echo '</div>';
}

/**
 * Render product price with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_product_price_with_order( $product, $settings, $order ) {
    $price_tag = $settings['price_html_tag'] ?? 'p';
    echo '<' . esc_attr($price_tag) . ' class="product-price" style="order: ' . esc_attr($order) . ';">';
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- format_price() returns pre-escaped HTML
    echo GGM_Commerce_Helpers::format_price($product);
    echo '</' . esc_attr($price_tag) . '>';
}

/**
 * Render product description with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_product_description_with_order( $product, $settings, $order ) {
    $length            = intval($settings['description_length'] ?? 15) ?: 15;
    $short_description = $product->short_description ?? '';
    $description       = wp_trim_words(wp_strip_all_tags($short_description), $length);

    echo '<div class="product-description" style="order: ' . esc_attr($order) . ';">';

    if (isset($settings['description_show_header']) && $settings['description_show_header'] === 'yes') {
        $header_text = isset($settings['description_header_text'])     ? $settings['description_header_text']     : 'Description';
        $header_tag  = isset($settings['description_header_html_tag']) ? $settings['description_header_html_tag'] : 'h3';
        echo '<' . esc_attr($header_tag) . ' class="product-description-header">';
        echo esc_html($header_text);
        echo '</' . esc_attr($header_tag) . '>';
    }

    echo esc_html($description);
    echo '</div>';
}

/**
 * Render product stock with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_product_stock_with_order( $product, $settings, $order ) {
    $stock_tag      = $settings['stock_html_tag'] ?? 'p';
    $stock_quantity = isset($product->stock) ? intval($product->stock) : 999;
    $threshold      = isset($product->low_stock_threshold) ? intval($product->low_stock_threshold) : 10;
    $stock_classes  = ['product-stock', GGM_Commerce_Helpers::get_product_stock_class($stock_quantity, $threshold)];

    // Check for restock text — replaces "Out of Stock" when available
    $display_text = '';
    if ( $stock_quantity <= 0 && ( $settings['show_restock_date'] ?? 'yes' ) === 'yes' ) {
        $restock_text = GGM_Commerce_Helpers::get_restock_display_text(
            $product->restock_date ?? null,
            $settings['restock_display_format'] ?? 'descriptive'
        );
        if ( $restock_text !== '' ) {
            $display_text = $restock_text;
            $stock_classes[] = 'restock-expected';
        }
    }

    if ( $display_text === '' ) {
        $display_text = ggmc_product_grid_get_stock_status( $product );
    }

    echo '<' . esc_attr($stock_tag) . ' class="' . esc_attr(implode(' ', $stock_classes)) . '" style="order: ' . esc_attr($order) . ';">';
    echo esc_html( $display_text );
    echo '</' . esc_attr($stock_tag) . '>';
}

/**
 * Render details link with CSS order.
 */
function ggmc_product_grid_render_details_link_with_order( $product, $product_url, $settings, $order ) {
    $link_text = $settings['details_link_text'] ?? __( 'MORE DETAILS', 'ggm-commerce' );
    $icon      = $settings['details_link_icon'] ?? [ 'value' => 'eicon-chevron-right', 'library' => 'eicons' ];

    echo '<div class="product-details-link" style="order: ' . esc_attr( $order ) . ';">';
    echo '<a href="' . esc_url( $product_url ) . '" class="details-link">';
    echo '<span class="details-link-text">' . esc_html( $link_text ) . '</span>';
    if ( ! empty( $icon['value'] ) ) {
        echo ' <span class="details-link-icon">';
        ggmc_render_icon( $icon, [ 'aria-hidden' => 'true' ] );
        echo '</span>';
    }
    echo '</a>';
    echo '</div>';
}

/**
 * Render add-to-cart / wishlist / compare actions with CSS order.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 * @param int    $order    CSS order value.
 */
function ggmc_product_grid_render_add_to_cart_button_with_order( $product, $settings, $order ) {
    $button_size          = $settings['button_size'] ?? 'sm';
    $button_text          = $settings['add_to_cart_text'] ?? 'Add to Cart';
    $variable_button_text = $settings['variable_product_text'] ?? 'Select Options';
    $variable_behavior    = $settings['variable_product_behavior'] ?? 'link_to_product';
    $button_style         = $settings['grid_button_style'] ?? 'normal';

    $has_variations = !empty($product->has_variations) && $product->has_variations;
    $display_text   = $has_variations ? $variable_button_text : $button_text;

    $product_url = '';
    if ($has_variations && $variable_behavior === 'link_to_product') {
        $product_url = GGM_Commerce_Helpers::get_product_details_url($product->id);
    }

    $icon_html = '';
    if (isset($settings['add_to_cart_icon']) && !empty($settings['add_to_cart_icon']['value'])) {
        ob_start();
        ggmc_render_icon($settings['add_to_cart_icon'], ['aria-hidden' => 'true']);
        $icon_html = ob_get_clean();
    } else {
        $icon_html = '<i class="fas fa-shopping-cart"></i>';
    }

    $data_attrs  = 'data-product-id="' . esc_attr($product->id) . '" ';
    $data_attrs .= 'data-has-variations="' . ($has_variations ? '1' : '0') . '" ';
    $data_attrs .= 'data-variable-behavior="' . esc_attr($variable_behavior) . '" ';
    $data_attrs .= 'data-add-text="' . esc_attr($button_text) . '" ';
    $data_attrs .= 'data-variable-text="' . esc_attr($variable_button_text) . '" ';
    if ($product_url) {
        $data_attrs .= 'data-product-url="' . esc_url($product_url) . '" ';
    }

    $variation_class = $has_variations ? ' variation-required' : '';

    $disabled_attr = '';
    if ($has_variations && $variable_behavior === 'disabled') {
        $disabled_attr = ' disabled="disabled"';
    }

    echo '<div class="product-actions" style="order: ' . esc_attr($order) . ';">';

    if ($has_variations) {
        echo '<input type="hidden" class="ggmc-selected-variation-id" name="variation_id" value="" />';
        echo '<input type="hidden" class="ggmc-selected-variation-data" name="variation_data" value="" />';
    }

    if ($button_style === 'icon') {
        echo '<button type="button" ';
        echo 'class="ggmc-add-to-cart-btn btn-icon btn-' . esc_attr($button_size) . esc_attr($variation_class) . '" ';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $data_attrs is built from esc_attr() calls
        echo $data_attrs;
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disabled_attr is a hardcoded safe string
        echo 'title="' . esc_attr($display_text) . '"' . $disabled_attr . '>';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $icon_html is rendered via Elementor Icons_Manager
        echo $icon_html;
        echo '</button>';
    } elseif ($button_style === 'icon_text') {
        $icon_position  = $settings['add_to_cart_icon_position'] ?? 'left';
        $position_class = $icon_position === 'right' ? ' icon-right' : ' icon-left';

        echo '<button type="button" ';
        echo 'class="ggmc-add-to-cart-btn btn-icon-text btn-' . esc_attr($button_size) . esc_attr($position_class) . esc_attr($variation_class) . '" ';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $data_attrs is built from esc_attr() calls, $disabled_attr is hardcoded
        echo $data_attrs . $disabled_attr . '>';

        if ($icon_position === 'right') {
            echo '<span>' . esc_html($display_text) . '</span>';
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $icon_html is rendered via Elementor Icons_Manager
            echo $icon_html;
        } else {
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $icon_html is rendered via Elementor Icons_Manager
            echo $icon_html;
            echo '<span>' . esc_html($display_text) . '</span>';
        }

        echo '</button>';
    } else {
        echo '<button type="button" ';
        echo 'class="ggmc-add-to-cart-btn btn-text btn-' . esc_attr($button_size) . esc_attr($variation_class) . '" ';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $data_attrs is built from esc_attr() calls, $disabled_attr is hardcoded
        echo $data_attrs . $disabled_attr . '>';
        echo esc_html($display_text);
        echo '</button>';
    }

    // Wishlist button
    $wishlist_active = \GGM_Commerce_License::instance()->is_widget_activated('wishlist');
    if ($settings['show_wishlist'] === 'yes' && $wishlist_active) {
        $wishlist_icon_html = '';
        if (isset($settings['wishlist_icon']) && !empty($settings['wishlist_icon']['value'])) {
            ob_start();
            ggmc_render_icon($settings['wishlist_icon'], ['aria-hidden' => 'true']);
            $wishlist_icon_html = ob_get_clean();
        } else {
            $wishlist_icon_html = '<i class="eicon-heart-o"></i>';
        }

        echo '<button type="button" class="wishlist-btn" data-product-id="' . esc_attr($product->id) . '">';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $wishlist_icon_html is rendered via Elementor Icons_Manager
        echo $wishlist_icon_html;
        echo '</button>';
    }

    // Compare button
    $compare_active = \GGM_Commerce_License::instance()->is_widget_activated('product-comparison');
    if ($settings['show_compare'] === 'yes' && $compare_active) {
        $compare_icon_html = '';
        if (isset($settings['compare_icon']) && !empty($settings['compare_icon']['value'])) {
            ob_start();
            ggmc_render_icon($settings['compare_icon'], ['aria-hidden' => 'true']);
            $compare_icon_html = ob_get_clean();
        } else {
            $compare_icon_html = '<i class="eicon-exchange"></i>';
        }

        echo '<button type="button" class="compare-btn" data-product-id="' . esc_attr($product->id) . '">';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $compare_icon_html is rendered via Elementor Icons_Manager
        echo $compare_icon_html;
        echo '</button>';
    }

    echo '</div>';
}

// -----------------------------------------------------------------------
// Pagination
// -----------------------------------------------------------------------

/**
 * Render pagination if enabled.
 * Closes both .ggmc-products-grid and .ggmc-product-grid-container.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_grid_render_pagination( $settings ) {
    echo '</div>'; // Close ggmc-products-grid

    if ($settings['show_pagination'] === 'yes') {
        $category_filter = $settings['filter_by_category'] ?? [];
        $tag_filter      = $settings['filter_by_tag'] ?? [];

        if (($settings['filter_by_current_taxonomy'] ?? 'no') === 'yes') {
            $current_category_id = GGM_Commerce_Helpers::get_current_category_id();
            if ($current_category_id) {
                $category_filter = [$current_category_id];
            }
            $current_tag_id = GGM_Commerce_Helpers::get_current_tag_id();
            if ($current_tag_id) {
                $tag_filter = [$current_tag_id];
            }
        }

        $args = [
            'posts_per_page'  => -1,
            'orderby'         => $settings['orderby'] ?? 'created_at',
            'order'           => $settings['order'] ?? 'DESC',
            'featured_only'   => ($settings['featured_only'] ?? 'no') === 'yes',
            'category_filter' => $category_filter,
            'tag_filter'      => $tag_filter,
            'stock_status'    => $settings['stock_filter'] ?? 'all',
            'price_min'       => $settings['price_min'] ?? '',
            'price_max'       => $settings['price_max'] ?? '',
            'search'          => $settings['search_term'] ?? '',
        ];

        $all_products  = GGM_Commerce_Product_Data_Handler::get_products($args);
        $total_products = count($all_products);
        $per_page       = intval($settings['products_per_page']) ?: 12;
        $total_pages    = ceil($total_products / $per_page);

        echo '<div class="ggmc-pagination" style="margin-top: var(--ggmc-spacing-lg, 24px);">';

        if ($total_pages > 1) {
            $current_page    = 1;
            $show_prev_next  = $settings['show_prev_next'] ?? 'yes';

            echo '<div class="pagination-wrapper">';

            if ($show_prev_next === 'yes' && $current_page > 1) {
                echo '<a href="#" class="pagination-prev">&laquo; Previous</a>';
            }

            $start_page = max(1, $current_page - 2);
            $end_page   = min($total_pages, $current_page + 2);

            for ($i = $start_page; $i <= $end_page; $i++) {
                if ($i === $current_page) {
                    echo '<span class="pagination-current">' . esc_html($i) . '</span>';
                } else {
                    echo '<a href="#" class="pagination-page">' . esc_html($i) . '</a>';
                }
            }

            if ($show_prev_next === 'yes' && $current_page < $total_pages) {
                echo '<a href="#" class="pagination-next">Next &raquo;</a>';
            }

            echo '</div>';
        }

        echo '</div>';
    }

    echo '</div>'; // Close ggmc-product-grid-container
}

// -----------------------------------------------------------------------
// Sorting controls
// -----------------------------------------------------------------------

/**
 * Render sorting controls (dropdown only).
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_grid_render_sorting_controls( $settings ) {
    $dropdown_options = [
        'date_desc'  => __('Newest First', 'ggm-commerce'),
        'date_asc'   => __('Oldest First', 'ggm-commerce'),
        'name_asc'   => __('Name (A-Z)', 'ggm-commerce'),
        'name_desc'  => __('Name (Z-A)', 'ggm-commerce'),
        'price_asc'  => __('Price (Low to High)', 'ggm-commerce'),
        'price_desc' => __('Price (High to Low)', 'ggm-commerce'),
        'stock_asc'  => __('Stock (Low to High)', 'ggm-commerce'),
        'stock_desc' => __('Stock (High to Low)', 'ggm-commerce'),
    ];

    $show_label = ($settings['sorting_show_label'] ?? 'yes') === 'yes';
    $label_text = $settings['sorting_label_text'] ?? __('Sort By:', 'ggm-commerce');

    echo '<div class="grid-sorting-controls">';
    echo '<div class="sorting-group">';

    if ($show_label) {
        echo '<label for="grid-sort-select">' . esc_html($label_text) . '</label>';
    }

    echo '<select id="grid-sort-select" class="grid-sort-select">';
    echo '<option value="" selected>' . esc_html__('Default', 'ggm-commerce') . '</option>';

    foreach ($dropdown_options as $option_value => $option_label) {
        echo '<option value="' . esc_attr($option_value) . '">' . esc_html($option_label) . '</option>';
    }

    echo '</select>';
    echo '</div>';
    echo '</div>';
}

// -----------------------------------------------------------------------
// Dynamic grid styles
// -----------------------------------------------------------------------

/**
 * Render grid styles — ONLY dynamic values that require PHP.
 *
 * Static/structural CSS is in widgets.css (always loaded).
 * Design CSS with variables is in professional-defaults.css (conditionally loaded).
 * This function outputs ONLY values that depend on widget settings.
 *
 * @param array                              $settings Widget settings.
 * @param \GGM_Commerce_Product_Grid_Widget  $widget   Widget instance (for get_id()).
 */
function ggmc_product_grid_render_grid_styles( $settings, $widget ) {
    $columns_desktop = intval($settings['columns'] ?? 3);
    $columns_tablet  = intval($settings['columns_tablet'] ?? 2);
    $columns_mobile  = intval($settings['columns_mobile'] ?? 1);
    $gap             = intval($settings['column_gap']['size'] ?? 20);

    $widget_id = $widget->get_id();
    ?>
    <style>
        /* Dynamic Grid Layout - values from widget settings */
        .elementor-element-<?php echo esc_attr($widget_id); ?> .ggmc-products-grid {
            grid-template-columns: repeat(<?php echo esc_html($columns_desktop); ?>, 1fr);
            gap: <?php echo esc_html($gap); ?>px;
        }

        /* Masonry Layout */
        .elementor-element-<?php echo esc_attr($widget_id); ?>.layout-masonry .ggmc-products-grid {
            column-count: <?php echo esc_html($columns_desktop); ?>;
            column-gap: <?php echo esc_html($gap); ?>px;
        }

        <?php
        $image_hover_trigger = $settings['image_hover_trigger'] ?? 'image';
        if ($image_hover_trigger === 'item') {
            ?>
        .elementor-element-<?php echo esc_attr($widget_id); ?> .product-grid-item:hover .product-image.hover-zoom img {
            transform: scale(1.1);
        }
        .elementor-element-<?php echo esc_attr($widget_id); ?> .product-grid-item:hover .product-image.hover-slide img {
            transform: translateX(-10px);
        }
        <?php } ?>

        <?php if ($columns_desktop === 1) :
            $layout_style    = $settings['layout_style'] ?? 'horizontal';
            $image_position  = $settings['image_position'] ?? 'left';
            $image_width     = $settings['image_width']['size'] ?? 120;
            $image_unit      = $settings['image_width']['unit'] ?? 'px';
        ?>
        /* Horizontal Layout - dynamic direction based on image position */
        .elementor-element-<?php echo esc_attr($widget_id); ?> .product-grid-item.list-layout-horizontal {
            flex-direction: <?php echo esc_html(($image_position === 'right') ? 'row-reverse' : 'row'); ?>;
        }

        /* Dynamic image width */
        .elementor-element-<?php echo esc_attr($widget_id); ?> .product-grid-item.list-layout-horizontal .product-image {
            width: <?php echo esc_html($image_width . $image_unit); ?>;
        }
        <?php endif; ?>

        /* Responsive Breakpoints - dynamic column values */
        @media (max-width: 1024px) {
            .elementor-element-<?php echo esc_attr($widget_id); ?> .ggmc-products-grid {
                grid-template-columns: repeat(<?php echo esc_html($columns_tablet); ?>, 1fr) !important;
            }
            .elementor-element-<?php echo esc_attr($widget_id); ?>.layout-masonry .ggmc-products-grid {
                column-count: <?php echo esc_html($columns_tablet); ?> !important;
            }
        }

        @media (max-width: 767px) {
            .elementor-element-<?php echo esc_attr($widget_id); ?> .ggmc-products-grid {
                grid-template-columns: repeat(<?php echo esc_html($columns_mobile); ?>, 1fr) !important;
            }
            .elementor-element-<?php echo esc_attr($widget_id); ?>.layout-masonry .ggmc-products-grid {
                column-count: <?php echo esc_html($columns_mobile); ?> !important;
            }
        }
    </style>
    <?php
}

// -----------------------------------------------------------------------
// Utility helpers
// -----------------------------------------------------------------------

/**
 * Generate product image HTML.
 *
 * @param int    $image_id Attachment ID.
 * @param string $size     Image size slug.
 * @param string $alt      Alt text (already escaped by caller).
 * @return string HTML string.
 */
function ggmc_product_grid_get_product_image_html( $image_id, $size = 'medium', $alt = '' ) {
    if (empty($image_id)) {
        return '<div class="placeholder-image">No Image</div>';
    }

    $image_url = wp_get_attachment_image_url($image_id, $size);
    if (!$image_url) {
        return '<div class="placeholder-image">No Image</div>';
    }

    return '<img src="' . esc_url($image_url) . '" alt="' . esc_attr($alt) . '" class="product-image-img" />';
}

/**
 * Get product categories HTML string.
 *
 * @param int $product_id Product ID.
 * @return string Pre-escaped HTML.
 */
function ggmc_product_grid_get_product_categories( $product_id, $link = false ) {
    $taxonomy   = GGM_Commerce_Taxonomy::get_instance();
    $categories = $taxonomy->get_product_categories($product_id);

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

    $category_badges = array_map(function($cat) use ($link) {
        $name = '<span class="product-category">' . esc_html($cat->name) . '</span>';
        if ($link) {
            $url = GGM_Commerce_Helpers::get_category_url($cat);
            return '<a href="' . esc_url($url) . '" class="product-category-link">' . $name . '</a>';
        }
        return $name;
    }, $categories);

    return implode(' ', $category_badges);
}

/**
 * Get product stock status text.
 *
 * @param object $product Product object.
 * @return string Translated, pre-escaped text.
 */
function ggmc_product_grid_get_stock_status( $product ) {
    if (!isset($product->stock)) {
        return __('In Stock', 'ggm-commerce');
    }

    $stock = intval($product->stock);
    if ($stock > 0) {
        /* translators: %d: stock quantity */
        return sprintf(__('%d in stock', 'ggm-commerce'), $stock);
    }

    return __('Out of Stock', 'ggm-commerce');
}
