<?php
/**
 * Product Table Widget — Standalone render functions
 * Replaces GGM_Commerce_Product_Table_Renderer class methods.
 * All functions are prefixed ggmc_product_table_.
 */

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

/**
 * Get available columns based on activated premium features.
 * Moved here from GGM_Commerce_Product_Table_Controls::get_available_columns().
 *
 * @return array List of available columns for the column order repeater.
 */
function ggmc_product_table_get_available_columns() {
    // Check premium feature activation
    $reviews_active = \GGM_Commerce_License::instance()->is_widget_activated('product-reviews');

    // Build the columns list
    $columns = [
        [
            'column_id' => 'image',
            'column_label' => __('Image', 'ggm-commerce'),
        ],
        [
            'column_id' => 'badges',
            'column_label' => __('Badges', 'ggm-commerce'),
        ],
        [
            'column_id' => 'name',
            'column_label' => __('Product Name', 'ggm-commerce'),
        ],
    ];

    // Rating column (premium feature)
    if ($reviews_active) {
        $columns[] = [
            'column_id' => 'rating',
            'column_label' => __('Rating', 'ggm-commerce'),
        ];
    }

    // Add remaining columns
    $columns[] = [
        'column_id' => 'sku',
        'column_label' => __('SKU', 'ggm-commerce'),
    ];
    $columns[] = [
        'column_id' => 'price',
        'column_label' => __('Price', 'ggm-commerce'),
    ];
    $columns[] = [
        'column_id' => 'stock',
        'column_label' => __('Stock', 'ggm-commerce'),
    ];
    $columns[] = [
        'column_id' => 'description',
        'column_label' => __('Description', 'ggm-commerce'),
    ];
    $columns[] = [
        'column_id' => 'actions',
        'column_label' => __('Actions', 'ggm-commerce'),
    ];

    return $columns;
}

/**
 * Build query arguments from widget settings.
 * Moved here from widget class build_query_args().
 *
 * @param array $settings Widget settings.
 * @return array WP_Query-compatible args for GGM_Commerce_Product_Data_Handler::get_products().
 */
function ggmc_product_table_build_query_args($settings) {
    // For client-side filtering to work, we need ALL products loaded
    // 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

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

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

        // Check for current tag
        $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, // Apply taxonomy filter if on archive page
        'tag_filter' => $tag_filter, // Apply taxonomy filter if on archive page
        'stock_status' => $settings['stock_filter'] ?? $settings['stock_status'] ?? 'all',
        'price_min' => $settings['price_min'] ?? '',
        'price_max' => $settings['price_max'] ?? '',
        'search' => $settings['search'] ?? '',
        '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
    ];
}

/**
 * Generate product image HTML.
 * Moved here from GGM_Commerce_Product_Table_Renderer::get_product_image_html().
 *
 * @param int|string $image_id WordPress attachment ID.
 * @param string     $size     Image size slug.
 * @param string     $alt      Alt text.
 * @param string     $style    Inline CSS style string.
 * @return string HTML string.
 */
function ggmc_product_table_get_product_image_html($image_id, $size = 'medium', $alt = '', $style = '') {
    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>';
    }

    $style_attr = !empty($style) ? ' style="' . esc_attr($style) . '"' : '';
    return '<img src="' . esc_url($image_url) . '" alt="' . esc_attr($alt) . '" class="product-image-img"' . $style_attr . ' />';
}

// =============================================================================
// Main render entry point
// =============================================================================

/**
 * Render the complete product table widget.
 * Called from the widget's render() method as ggmc_product_table_render( $this ).
 *
 * @param GGM_Commerce_Widget_Product_Table_Refactored $widget The widget instance.
 */
function ggmc_product_table_render($widget) {
    $settings = $widget->get_settings_for_display();

    // Build query arguments from settings
    $query_args = ggmc_product_table_build_query_args($settings);

    // Get products using the data handler
    $products = GGM_Commerce_Product_Data_Handler::get_products($query_args);

    if (empty($products)) {
        ggmc_product_table_render_no_products_message($settings);
        return;
    }

    ggmc_product_table_render_table_wrapper_start($widget, $settings);
    ggmc_product_table_render_table_header($settings);
    ggmc_product_table_render_table_body($products, $settings);
    ggmc_product_table_render_table_wrapper_end();
    ggmc_product_table_render_pagination($settings);
    ggmc_product_table_render_pagination_styles($widget);
}

// =============================================================================
// Internal rendering helpers (formerly private methods on the renderer class)
// =============================================================================

/**
 * Render the "no products" message.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_table_render_no_products_message($settings) {
    echo '<div class="no-products-message">';
    echo '<p>' . esc_html($settings['no_products_message'] ?? 'No products found.') . '</p>';
    echo '</div>';
}

/**
 * Render opening wrapper elements and optional sorting select.
 *
 * @param GGM_Commerce_Widget_Product_Table_Refactored $widget   The widget instance.
 * @param array                                        $settings Widget settings.
 */
function ggmc_product_table_render_table_wrapper_start($widget, $settings) {
    $wrapper_classes = ['ggmc-product-table-wrapper'];

    if ($settings['enable_sorting'] === 'yes') {
        $wrapper_classes[] = 'sortable-table';
    }

    // Add container wrapper for JavaScript to find pagination
    echo '<div class="ggmc-product-table-container">';

    echo '<div class="' . esc_attr(implode(' ', $wrapper_classes)) . '">';

    // Render sorting select box if enabled
    if ($settings['enable_sorting'] === 'yes') {
        $sorting_type = $settings['sorting_type'] ?? 'both';
        if ($sorting_type === 'select' || $sorting_type === 'both') {
            ggmc_product_table_render_sorting_select($settings);
        }
    }

    $per_page = intval($settings['products_per_page']) ?: 20;
    $show_prev_next = isset($settings['show_prev_next']) && $settings['show_prev_next'] === 'yes' ? 'yes' : 'no';
    $sorting_enabled = ($settings['enable_sorting'] === 'yes') ? 'yes' : 'no';
    $sorting_type = $settings['sorting_type'] ?? 'both';

    echo '<div class="table-container">';
    echo '<table class="ggmc-product-table" ';
    echo 'data-table-id="' . esc_attr($widget->get_id()) . '" ';
    echo 'data-products-per-page="' . esc_attr($per_page) . '" ';
    echo 'data-show-prev-next="' . esc_attr($show_prev_next) . '" ';
    echo 'data-sorting-enabled="' . esc_attr($sorting_enabled) . '" ';
    echo 'data-sorting-type="' . esc_attr($sorting_type) . '">';
}

/**
 * Render closing wrapper elements (table, table-container, wrapper).
 * Does NOT close ggmc-product-table-container — pagination sits inside it.
 */
function ggmc_product_table_render_table_wrapper_end() {
    echo '</table>';
    echo '</div>'; // table-container
    echo '</div>'; // wrapper
    // Don't close ggmc-product-table-container yet - pagination needs to be inside it
}

/**
 * Render pagination container (if enabled) and close the outer container div.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_table_render_pagination($settings) {
    if (isset($settings['show_pagination']) && $settings['show_pagination'] === 'yes') {
        // Render empty pagination container - JavaScript will populate it
        echo '<div class="ggmc-pagination" style="margin-top: 30px;">';
        // JavaScript will populate pagination here
        echo '</div>';
    }
    // Close the ggmc-product-table-container div that was opened in render_table_wrapper_start
    echo '</div>'; // ggmc-product-table-container
}

/**
 * Render the table <thead>.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_table_render_table_header($settings) {
    $sorting_enabled = ($settings['enable_sorting'] === 'yes');
    $sorting_type = $settings['sorting_type'] ?? 'both';
    $show_arrows = $sorting_enabled && ($sorting_type === 'headers' || $sorting_type === 'both');

    echo '<thead>';
    echo '<tr>';

    // Get column order from settings
    $column_order = $settings['column_order'] ?? [];

    // Render columns in the specified order
    foreach ($column_order as $column) {
        $column_id = $column['column_id'] ?? '';

        // Check if column is enabled via the show_*_column settings
        if (!ggmc_product_table_is_column_visible($column_id, $settings)) {
            continue;
        }

        ggmc_product_table_render_column_header($column_id, $settings, $show_arrows);
    }

    echo '</tr>';
    echo '</thead>';
}

/**
 * Render an individual column header <th>.
 *
 * @param string $column_id  Column identifier.
 * @param array  $settings   Widget settings.
 * @param bool   $show_arrows Whether sortable arrows should be shown.
 */
function ggmc_product_table_render_column_header($column_id, $settings, $show_arrows) {
    switch ($column_id) {
        case 'image':
            echo '<th class="image-column">Image</th>';
            break;

        case 'badges':
            echo '<th class="badges-column">Badges</th>';
            break;

        case 'name':
            $sortable_class = $show_arrows ? ' sortable' : '';
            echo '<th class="name-column' . esc_attr($sortable_class) . '" data-sort="name">';
            echo 'Product';
            if ($show_arrows) {
                echo '<span class="sort-arrows"><i class="eicon-arrow-up"></i><i class="eicon-arrow-down"></i></span>';
            }
            echo '</th>';
            break;

        case 'rating':
            $license = ggm_commerce_license();
            if ($license->is_widget_activated('product-reviews')) {
                $header_rating_label = $settings['header_rating_label'] ?? 'Rating';
                echo '<th class="rating-column">' . esc_html($header_rating_label) . '</th>';
            }
            break;

        case 'sku':
            $sortable_class = $show_arrows ? ' sortable' : '';
            echo '<th class="sku-column' . esc_attr($sortable_class) . '" data-sort="sku">';
            echo 'SKU';
            if ($show_arrows) {
                echo '<span class="sort-arrows"><i class="eicon-arrow-up"></i><i class="eicon-arrow-down"></i></span>';
            }
            echo '</th>';
            break;

        case 'price':
            $sortable_class = $show_arrows ? ' sortable' : '';
            $tax = ggm_commerce_tax();
            $price_header = 'Price';
            // Add tax suffix to header (e.g., "Price (excl. VAT)")
            if ($tax->should_show_suffix()) {
                $price_header .= ' <span class="ggmc-tax-suffix">(' . esc_html($tax->get_suffix_text()) . ')</span>';
            }

            echo '<th class="price-column' . esc_attr($sortable_class) . '" data-sort="price">';
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $price_header contains pre-escaped HTML
            echo $price_header;
            if ($show_arrows) {
                echo '<span class="sort-arrows"><i class="eicon-arrow-up"></i><i class="eicon-arrow-down"></i></span>';
            }
            echo '</th>';
            break;

        case 'stock':
            $sortable_class = $show_arrows ? ' sortable' : '';
            echo '<th class="stock-column' . esc_attr($sortable_class) . '" data-sort="stock">';
            echo 'Stock';
            if ($show_arrows) {
                echo '<span class="sort-arrows"><i class="eicon-arrow-up"></i><i class="eicon-arrow-down"></i></span>';
            }
            echo '</th>';
            break;

        case 'description':
            echo '<th class="description-column">Description</th>';
            break;

        case 'actions':
            echo '<th class="actions-column">Actions</th>';
            break;
    }
}

/**
 * Render the table <tbody>.
 *
 * @param array $products Array of product objects.
 * @param array $settings Widget settings.
 */
function ggmc_product_table_render_table_body($products, $settings) {
    echo '<tbody>';

    foreach ($products as $product) {
        ggmc_product_table_render_product_row($product, $settings);
    }

    echo '</tbody>';
}

/**
 * Render a single product <tr>.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_product_row($product, $settings) {
    $product_url = GGM_Commerce_Helpers::get_product_details_url($product->id);

    // Get product categories and tags for filtering
    $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);

    // Prepare sortable data attributes
    $created_at = isset($product->created_at) ? strtotime($product->created_at) : 0;
    $name_sort = strtolower($product->name);
    $sku_sort = strtolower($product->sku ?? '');
    $price_sort = floatval($product->price);
    $stock_sort = intval($product->stock);

    echo '<tr class="product-table-row" ';
    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-sku="' . esc_attr($sku_sort) . '" ';
    echo 'data-price-sort="' . esc_attr($price_sort) . '" ';
    echo 'data-stock="' . esc_attr($stock_sort) . '">';

    // Get column order from settings
    $column_order = $settings['column_order'] ?? [];

    // Render columns in the specified order
    foreach ($column_order as $column) {
        $column_id = $column['column_id'] ?? '';

        // Check if column is enabled via the show_*_column settings
        if (!ggmc_product_table_is_column_visible($column_id, $settings)) {
            continue;
        }

        ggmc_product_table_render_column_cell($column_id, $product, $product_url, $settings);
    }

    echo '</tr>';
}

/**
 * Render an individual column <td> cell.
 *
 * @param string $column_id   Column identifier.
 * @param object $product     Product object.
 * @param string $product_url URL to the product detail page.
 * @param array  $settings    Widget settings.
 */
function ggmc_product_table_render_column_cell($column_id, $product, $product_url, $settings) {
    switch ($column_id) {
        case 'image':
            ggmc_product_table_render_image_cell($product, $settings);
            break;

        case 'badges':
            ggmc_product_table_render_badges_cell($product, $settings);
            break;

        case 'name':
            ggmc_product_table_render_name_cell($product, $product_url, $settings);
            break;

        case 'rating':
            $license = ggm_commerce_license();
            if ($license->is_widget_activated('product-reviews')) {
                ggmc_product_table_render_rating_cell($product, $settings);
            }
            break;

        case 'sku':
            ggmc_product_table_render_sku_cell($product, $settings);
            break;

        case 'price':
            ggmc_product_table_render_price_cell($product, $settings);
            break;

        case 'stock':
            ggmc_product_table_render_stock_cell($product, $settings);
            break;

        case 'description':
            ggmc_product_table_render_description_cell($product, $settings);
            break;

        case 'actions':
            ggmc_product_table_render_actions_cell($product, $settings);
            break;
    }
}

/**
 * Check if a column is visible based on the show_*_column settings.
 *
 * @param string $column_id The column identifier.
 * @param array  $settings  Widget settings.
 * @return bool Whether the column should be displayed.
 */
function ggmc_product_table_is_column_visible($column_id, $settings) {
    $setting_key = 'show_' . $column_id . '_column';
    return ($settings[$setting_key] ?? 'yes') === 'yes';
}

/**
 * Render the image cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_image_cell($product, $settings) {
    echo '<td class="image-cell">';

    if ($product->image_id) {
        echo '<div class="product-image-wrapper">';
        $image_html = ggmc_product_table_get_product_image_html(
            $product->image_id,
            'thumbnail',
            esc_attr($product->name),
            'width: 60px; height: 60px; object-fit: cover; border-radius: 4px;'
        );
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- ggmc_product_table_get_product_image_html() returns pre-escaped HTML
        echo $image_html;

        // Temporary debug - remove this later
        if (empty($image_html)) {
            echo '<span style="color: red; font-size: 10px;">Image ID: ' . esc_html($product->image_id) . ' - No HTML generated</span>';
        }

        // Quick View overlay - only show if premium feature is activated
        if (isset($settings['show_quick_view']) && $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-bold"></i>';
            echo '</button>';
            echo '</div>';
        }

        echo '</div>';
    } else {
        echo '<div class="no-image">No Image</div>';
    }

    echo '</td>';
}

/**
 * Render the badges cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_badges_cell($product, $settings) {
    echo '<td class="badges-cell">';

    $badges = [];

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

    // Sale badge (if product has sale price)
    if (!empty($product->sale_price) && $product->sale_price < $product->price) {
        $badges[] = '<span class="badge badge-sale">Sale</span>';
    }

    // New badge (products created in last 30 days)
    if (isset($product->created_at)) {
        $created_time = strtotime($product->created_at);
        $thirty_days_ago = strtotime('-30 days');
        if ($created_time > $thirty_days_ago) {
            $badges[] = '<span class="badge badge-new">New</span>';
        }
    }

    // Display badges or dash if none
    if (!empty($badges)) {
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $badges contains pre-escaped HTML badge markup
        echo '<div class="badge-container">' . implode(' ', $badges) . '</div>';
    } else {
        echo '<span class="no-badges">—</span>';
    }

    echo '</td>';
}

/**
 * Render the name cell.
 *
 * @param object $product     Product object.
 * @param string $product_url URL to the product detail page.
 * @param array  $settings    Widget settings.
 */
function ggmc_product_table_render_name_cell($product, $product_url, $settings) {
    $name_tag = $settings['name_html_tag'] ?? 'h3';

    echo '<td class="name-cell" data-sort-value="' . esc_attr(strtolower($product->name)) . '">';
    echo '<' . esc_attr($name_tag) . ' class="product-name">';
    echo '<a href="' . esc_url($product_url) . '">' . esc_html($product->name) . '</a>';
    echo '</' . esc_attr($name_tag) . '>';

    // Display categories under product name if enabled
    if (isset($settings['show_categories']) && $settings['show_categories'] === 'yes') {
        ggmc_product_table_render_product_categories($product);
    }

    echo '</td>';
}

/**
 * Render product categories under the product name.
 *
 * @param object $product Product object.
 */
function ggmc_product_table_render_product_categories($product) {
    $taxonomy = GGM_Commerce_Taxonomy::get_instance();
    $categories = $taxonomy->get_product_categories($product->id);

    if (!empty($categories)) {
        echo '<div class="product-categories">';
        foreach ($categories as $index => $category) {
            if ($index > 0) {
                echo ', ';
            }
            echo '<a href="#" class="product-category" data-category-id="' . esc_attr($category->id) . '">';
            echo esc_html($category->name);
            echo '</a>';
        }
        echo '</div>';
    }
}

/**
 * Render the SKU cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_sku_cell($product, $settings) {
    $sku = $product->sku ?? '';
    echo '<td class="sku-cell" data-sort-value="' . esc_attr(strtolower($sku)) . '">';
    echo '<span class="product-sku">' . esc_html($sku ?: '-') . '</span>';
    echo '</td>';
}

/**
 * Render the price cell.
 * Note: Tax suffix is NOT shown here — it's displayed in the column header instead.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_price_cell($product, $settings) {
    $price_tag = $settings['price_html_tag'] ?? 'h3';
    $tax = ggm_commerce_tax();

    echo '<td class="price-cell" data-sort-value="' . esc_attr($product->price) . '">';

    echo '<' . esc_attr($price_tag) . ' class="product-price">';
    if (!empty($product->sale_price) && $product->sale_price < $product->price) {
        echo '<span class="sale-price">' . esc_html($tax->format_price($product->sale_price)) . '</span> ';
        echo '<span class="original-price">' . esc_html($tax->format_price($product->price)) . '</span>';
    } else {
        echo '<span class="current-price">' . esc_html($tax->format_price($product->price)) . '</span>';
    }
    echo '</' . esc_attr($price_tag) . '>';

    echo '</td>';
}

/**
 * Render the stock cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_stock_cell($product, $settings) {
    $stock_tag = $settings['stock_html_tag'] ?? 'p';
    $stock_quantity = intval($product->stock);
    $threshold = isset($product->low_stock_threshold) ? intval($product->low_stock_threshold) : 10;
    $stock_status = GGM_Commerce_Helpers::get_product_stock_status($stock_quantity, $threshold);
    $stock_class = '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_class .= ' restock-expected';
        }
    }

    if ( $display_text === '' ) {
        $display_text = $stock_status . ' (' . $stock_quantity . ')';
    }

    echo '<td class="stock-cell" data-sort-value="' . esc_attr($stock_quantity) . '">';
    echo '<' . esc_attr($stock_tag) . ' class="' . esc_attr($stock_class) . '">';
    echo esc_html( $display_text );
    echo '</' . esc_attr($stock_tag) . '>';
    echo '</td>';
}

/**
 * Render the description cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_description_cell($product, $settings) {
    echo '<td class="description-cell">';

    $description = !empty($product->short_description) ? $product->short_description : ($product->description ?? '');
    $max_length = 100;

    if (strlen($description) > $max_length) {
        echo '<span class="description-text">' . esc_html(substr($description, 0, $max_length)) . '...</span>';
    } else {
        echo '<span class="description-text">' . esc_html($description) . '</span>';
    }

    echo '</td>';
}

/**
 * Render the actions cell (add-to-cart, wishlist, compare buttons).
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_actions_cell($product, $settings) {
    echo '<td class="actions-cell">';
    echo '<div class="product-actions">';

    // Get button text settings
    $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';

    // Check if product has variations
    $has_variations = !empty($product->has_variations) && $product->has_variations;

    // Determine display text
    $display_text = $has_variations ? $variable_button_text : $button_text;

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

    // Build data attributes
    $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) . '" ';
    }

    // Add variation-required class if has variations
    $variation_class = $has_variations ? ' variation-required' : '';

    // Determine if button should be disabled
    $disabled_attr = '';
    if ($has_variations && $variable_behavior === 'disabled') {
        $disabled_attr = ' disabled="disabled"';
    }

    // Hidden inputs for variation data
    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="" />';
    }

    // Add to cart button
    $show_icon = isset($settings['add_to_cart_show_icon']) && $settings['add_to_cart_show_icon'] === 'yes';
    $icon_position = $settings['add_to_cart_icon_position'] ?? 'before';
    $position_class = $icon_position === 'after' ? ' icon-after' : '';

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

    // Render icon before text if position is 'before'
    if ($show_icon && $icon_position === 'before') {
        if (isset($settings['add_to_cart_icon']) && !empty($settings['add_to_cart_icon']['value'])) {
            ggmc_render_icon($settings['add_to_cart_icon'], ['aria-hidden' => 'true']);
            echo ' ';
        } else {
            echo '<i class="fas fa-shopping-cart"></i> ';
        }
    }

    echo esc_html($display_text);

    // Render icon after text if position is 'after'
    if ($show_icon && $icon_position === 'after') {
        echo ' ';
        if (isset($settings['add_to_cart_icon']) && !empty($settings['add_to_cart_icon']['value'])) {
            ggmc_render_icon($settings['add_to_cart_icon'], ['aria-hidden' => 'true']);
        } else {
            echo '<i class="fas fa-shopping-cart"></i>';
        }
    }

    echo '</button>';

    // Wishlist button - only show if wishlist premium feature is activated
    $wishlist_active = \GGM_Commerce_License::instance()->is_widget_activated( 'wishlist' );
    if ( isset($settings['show_wishlist']) && $settings['show_wishlist'] === 'yes' && $wishlist_active ) {
        echo '<button class="wishlist-btn" data-product-id="' . esc_attr($product->id) . '">';
        // Use custom icon or default
        if (isset($settings['wishlist_icon']) && !empty($settings['wishlist_icon']['value'])) {
            ggmc_render_icon($settings['wishlist_icon'], ['aria-hidden' => 'true']);
        } else {
            echo '<i class="eicon-heart-o"></i>';
        }
        echo '</button>';
    }

    // Compare button - only show if product comparison premium feature is activated
    $compare_active = \GGM_Commerce_License::instance()->is_widget_activated('product-comparison');
    if (isset($settings['show_compare']) && $settings['show_compare'] === 'yes' && $compare_active) {
        echo '<button class="compare-btn" data-product-id="' . esc_attr($product->id) . '">';
        // Use custom icon or default
        if (isset($settings['compare_icon']) && !empty($settings['compare_icon']['value'])) {
            ggmc_render_icon($settings['compare_icon'], ['aria-hidden' => 'true']);
        } else {
            echo '<i class="eicon-exchange"></i>';
        }
        echo '</button>';
    }

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

/**
 * Render the rating cell.
 *
 * @param object $product  Product object.
 * @param array  $settings Widget settings.
 */
function ggmc_product_table_render_rating_cell($product, $settings) {
    echo '<td class="rating-cell">';

    // Get product rating from pre-calculated aggregate fields
    $rating = isset($product->average_rating) ? floatval($product->average_rating) : 0;
    $rating_count = isset($product->rating_count) ? intval($product->rating_count) : 0;

    echo '<div class="product-rating">';

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

    // Render stars
    echo '<div class="rating-stars">';
    for ($i = 1; $i <= 5; $i++) {
        if ($i <= floor($rating)) {
            // Full star
            echo '<span class="star filled"><i class="eicon-star"></i></span>';
        } elseif ($i - 0.5 <= $rating) {
            // Half star
            echo '<span class="star half"><i class="eicon-star-o"></i></span>';
        } else {
            // Empty star
            echo '<span class="star empty"><i class="eicon-star-o"></i></span>';
        }
    }
    echo '</div>';

    // Render rating text with before/after text
    if ($rating > 0) {
        echo '<div class="rating-text">';
        if (!empty($before_text)) {
            echo '<span class="before-text">' . esc_html($before_text) . '</span> ';
        }
        echo '<span class="rating-value">' . number_format($rating, 1) . '</span>';
        if (!empty($after_text)) {
            echo ' <span class="after-text">' . esc_html($after_text) . '</span>';
        }
        if ($rating_count > 0) {
            echo ' <span class="rating-count">(' . esc_html($rating_count) . ')</span>';
        }
        echo '</div>';
    } else {
        echo '<div class="rating-text no-rating">No rating</div>';
    }

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

/**
 * Render the sorting select box.
 *
 * @param array $settings Widget settings.
 */
function ggmc_product_table_render_sorting_select($settings) {
    // Get selected sorting fields (grouped: name, price, stock)
    $selected_fields = $settings['sorting_options'] ?? ['name', 'price', 'stock'];

    // If empty, use all fields
    if (empty($selected_fields)) {
        $selected_fields = ['name', 'price', 'stock'];
    }

    // Map of fields to their dropdown options (both directions)
    $dropdown_options = [
        'name' => [
            'name_asc' => 'Name (A-Z)',
            'name_desc' => 'Name (Z-A)',
        ],
        'price' => [
            'price_asc' => 'Price (Low to High)',
            'price_desc' => 'Price (High to Low)',
        ],
        'stock' => [
            'stock_asc' => 'Stock (Low to High)',
            'stock_desc' => 'Stock (High to Low)',
        ],
    ];

    $show_label = ($settings['sorting_show_label'] ?? 'yes') === 'yes';
    $sort_label = $settings['sorting_label_text'] ?? __('Sort By:', 'ggm-commerce');
    $label_tag = $settings['sorting_label_html_tag'] ?? 'label';
    $allowed_tags = ['label', 'span', 'p', 'div'];
    $label_tag = in_array($label_tag, $allowed_tags, true) ? $label_tag : 'label';

    echo '<div class="table-sorting-controls">';
    echo '<div class="sorting-group">';
    if ($show_label) {
        $for_attr = $label_tag === 'label' ? ' for="table-sort-select"' : '';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $label_tag is allow-listed above, $for_attr is a static string.
        echo '<' . $label_tag . ' class="sorting-label"' . $for_attr . '>' . esc_html($sort_label) . '</' . $label_tag . '>';
    }
    echo '<select id="table-sort-select" class="table-sort-select">';
    echo '<option value="" selected disabled>Choose sorting...</option>';

    // For each selected field, add both asc and desc options
    foreach ($selected_fields as $field) {
        if (isset($dropdown_options[$field])) {
            foreach ($dropdown_options[$field] as $option_value => $option_label) {
                echo '<option value="' . esc_attr($option_value) . '">' . esc_html($option_label) . '</option>';
            }
        }
    }

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

/**
 * Render pagination styles and image hover styles.
 * This outputs a <style> block with dynamic CSS based on widget settings.
 *
 * @param GGM_Commerce_Widget_Product_Table_Refactored $widget The widget instance.
 */
function ggmc_product_table_render_pagination_styles($widget) {
    $settings = $widget->get_settings_for_display();

    // Determine hover selector based on trigger setting
    $image_hover_trigger = $settings['image_hover_trigger'] ?? 'image';
    $image_hover_effect = $settings['image_hover_effect'] ?? 'zoom';
    $hover_selector = $image_hover_trigger === 'item' ? 'tr:hover .product-image-wrapper' : '.product-image-wrapper:hover';

    ?>
    <style>
        <?php if ($image_hover_effect === 'zoom'): ?>
        /* Zoom effect */
        <?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $hover_selector is a safe CSS selector built from sanitized settings ?>
        <?php echo $hover_selector; ?> img {
            transform: scale(1.1);
        }
        <?php elseif ($image_hover_effect === 'slide'): ?>
        /* Slide effect */
        <?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $hover_selector is a safe CSS selector built from sanitized settings ?>
        <?php echo $hover_selector; ?> img {
            transform: translateX(-10px);
        }
        <?php endif; ?>
    </style>
    <?php
}
