<?php
/**
 * Shoptimizer Grid Feature
 *
 * @package  Shoptimizer
 * @author   CommerceGurus
 * @since    1.0.0
 */

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

if ( ! class_exists( 'Shoptimizer_Grid_Feature' ) ) :

class Shoptimizer_Grid_Feature {

	private static $product_count          = -1;
	private static $features               = array();
	private static $feature_widths         = array();
	private static $grid_position          = 0;
	private static $admin_positions_loaded = false;
	private static $admin_positions        = array();
	private static $col_count              = null;

	/**
	 * Constructor
	 */
	public function __construct() {
		add_action( 'init', array( $this, 'create_post_type' ) );
		add_filter( 'should_load_remote_block_patterns', '__return_false' );
		add_filter( 'rest_dispatch_request', array( $this, 'restrict_block_editor_patterns' ), 12, 3 );
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
		add_action( 'admin_init', array( $this, 'admin_meta_box' ) );
		add_action( 'save_post', array( $this, 'admin_meta_save' ), 10, 2 );
		add_action( 'wp_trash_post', array( $this, 'invalidate_grid_features_cache' ) );
		add_action( 'delete_post', array( $this, 'invalidate_grid_features_cache' ) );
		add_action( 'wp_ajax_shoptimizer_get_grid_feature_terms', array( $this, 'get_grid_feature_terms' ) );
		add_filter( 'manage_cg_grid_feature_posts_columns', array( $this, 'add_admin_columns' ) );
		add_action( 'manage_cg_grid_feature_posts_custom_column', array( $this, 'render_admin_columns' ), 10, 2 );

		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_styles' ) );
		add_action( 'woocommerce_before_shop_loop', array( $this, 'setup_features' ), 5 );
		add_action( 'shoptimizer_before_shop_loop_item', array( $this, 'maybe_insert_banner' ), 99 );
		add_filter( 'shoptimizer_should_render_product', array( $this, 'should_render_product' ), 10 );
		add_filter( 'body_class', array( $this, 'body_class' ), 40, 1 );
		add_action( 'wp_footer', array( $this, 'dynamic_styles' ), 10 );
	}

	/**
	 * Get the number of product columns, cached for the request.
	 */
	private static function get_col_count() {
		if ( null === self::$col_count ) {
			self::$col_count = wc_get_default_products_per_row();
		}
		return self::$col_count;
	}

	/**
	 * Get pattern image URL with child theme support
	 *
	 * @param string $image_path Relative path to image from assets/patterns/grid-features/
	 * @return string Image URL
	 */
	public static function get_pattern_image_url( $image_path ) {
		// Check if image exists in child theme first
		if ( is_child_theme() ) {
			$child_path = get_stylesheet_directory() . '/assets/patterns/grid-features/' . $image_path;
			if ( file_exists( $child_path ) ) {
				return get_stylesheet_directory_uri() . '/assets/patterns/grid-features/' . $image_path;
			}
		}

		// Fall back to parent theme
		return get_template_directory_uri() . '/assets/patterns/grid-features/' . $image_path;
	}

	/**
	 * Enqueue frontend styles
	 * Only loads when Grid Features exist on the site
	 */
	public function enqueue_frontend_styles() {
		// Only load on shop, category, and tag pages
		if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
			return;
		}

		// Check if any Grid Features exist
		$has_features = wp_count_posts( 'cg_grid_feature' );
		if ( ! $has_features || ( isset( $has_features->publish ) && $has_features->publish < 1 ) ) {
			return;
		}

		$theme_version = wp_get_theme()->get( 'Version' );

		wp_enqueue_style(
			'shoptimizer-grid-features',
			get_template_directory_uri() . '/src/css/layout/plp-grid-features.css',
			array(),
			$theme_version
		);

		wp_enqueue_script(
			'shoptimizer-grid-feature-analytics',
			get_template_directory_uri() . '/assets/js/grid-feature-analytics.js',
			array(),
			$theme_version,
			true
		);
	}

	/**
	 * Create post type
	 */
	public function create_post_type() {
		$args       = array(
			'labels'            => array(
				'name'               => esc_html__( 'Grid Features', 'shoptimizer' ),
				'singular_name'      => esc_html__( 'Grid Feature', 'shoptimizer' ),
				'add_new'            => esc_html__( 'Add Feature', 'shoptimizer' ),
				'add_new_item'       => esc_html__( 'Add Grid Feature', 'shoptimizer' ),
				'edit_item'          => esc_html__( 'Edit Grid Feature', 'shoptimizer' ),
				'new_item'           => esc_html__( 'New Grid Feature', 'shoptimizer' ),
				'view_item'          => esc_html__( 'View Grid Feature', 'shoptimizer' ),
				'search_items'       => esc_html__( 'Search Grid Features', 'shoptimizer' ),
				'not_found'          => esc_html__( 'No grid features found', 'shoptimizer' ),
				'not_found_in_trash' => esc_html__( 'No grid features found in trash', 'shoptimizer' ),
			),
			'public'            => true,
			'has_archive'       => true,
			'show_in_nav_menus' => true,
			'show_in_menu'      => true,
			'menu_icon'         => 'dashicons-grid-view',
			'show_in_rest'      => true,
			'supports'          => array( 'title', 'editor' ),
			'rewrite'           => false,
			'query_var'         => false,
		);

		$args['exclude_from_search'] = true;
		$args['publicly_queryable']  = false;
		register_post_type( 'cg_grid_feature', $args );

		// Register pattern category
		register_block_pattern_category(
			'shoptimizer-grid-promotions',
			array( 'label' => esc_html__( 'Grid Features', 'shoptimizer' ) )
		);

		// Patterns are loaded automatically from /patterns/ directory
	}

	/**
	 * Restrict block editor patterns to only show Grid Features
	 */
	public function restrict_block_editor_patterns( $dispatch_result, $request, $route ) {
		// Check if pattern filtering is enabled in customizer
		$hide_default_patterns = get_theme_mod( 'shoptimizer_general_accessibility_hide_default_patterns', true );

		if ( ! $hide_default_patterns ) {
			return $dispatch_result;
		}

		// Only filter patterns when editing a Grid Feature post
		$referer = wp_get_referer();
		if ( $referer ) {
			$post_id = url_to_postid( $referer );
			if ( ! $post_id ) {
				// Try to extract post ID from query string (block editor uses ?post=ID)
				$query_str = wp_parse_url( $referer, PHP_URL_QUERY );
				if ( $query_str ) {
					parse_str( $query_str, $params );
					$post_id = isset( $params['post'] ) ? intval( $params['post'] ) : 0;
				}
			}
			if ( $post_id && 'cg_grid_feature' !== get_post_type( $post_id ) ) {
				return $dispatch_result;
			}
		}

		if ( strpos( $route, '/wp/v2/block-patterns/patterns' ) === 0 ) {
			$patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered();

			if ( ! empty( $patterns ) ) {
				// Remove all patterns except our Grid Features patterns
				foreach ( $patterns as $pattern ) {
					// Keep only our custom Grid Features patterns
					if ( ! isset( $pattern['categories'] ) || ! in_array( 'shoptimizer-grid-promotions', $pattern['categories'], true ) ) {
						unregister_block_pattern( $pattern['name'] );
					}
				}

				// Remove core patterns from the Dotorg pattern directory
				remove_theme_support( 'core-block-patterns' );
			}
		}

		return $dispatch_result;
	}

	/**
	 * Admin scripts
	 */
	public function admin_scripts() {
		$screen = get_current_screen();
		if ( 'cg_grid_feature' === $screen->post_type && 'post' === $screen->base ) {
			wp_enqueue_style( 'woocommerce-select2-styles', WC()->plugin_url() . '/assets/css/select2.css', array(), WC()->version );
			$wc_version = defined( 'WC_VERSION' ) ? WC_VERSION : null;
			if ( $wc_version && version_compare( $wc_version, '10.3.0', '>=' ) ) {
				wp_enqueue_script( 'wc-select2' );
			} else {
				wp_enqueue_script( 'select2' );
			}
			$theme   = wp_get_theme();
			$version = $theme->get( 'Version' );

			wp_enqueue_script(
				'shoptimizer-grid-feature',
				get_template_directory_uri() . '/assets/js/admin-grid-feature.js',
				array(),
				$version,
				true
			);
		}
	}

	/**
	 * Admin meta box
	 */
	public function admin_meta_box() {
		add_meta_box( 'shoptimizer-grid-feature-meta-box', esc_html__( 'Placement', 'shoptimizer' ), array( $this, 'admin_placement_display' ), 'cg_grid_feature', 'side', 'low' );
	}

	/**
	 * Admin placement meta box display.
	 *
	 * @param string $post post object.
	 */
	public function admin_placement_display( $post ) {
		$term_options = '';
		$term_type    = 'category';
		$feature      = array();
		$shop_id      = get_option( 'woocommerce_shop_page_id' );
		if ( isset( $post->ID ) && $post->ID ) {
			$feature   = get_post_meta( $post->ID, 'shoptimizer_grid_feature', true );
			$term_data = explode( ':', (string) get_post_meta( $post->ID, 'shoptimizer_grid_feature_term', true ) );
			$page_type = isset( $term_data[0] ) && ! empty( $term_data[0] ) ? $term_data[0] : 'shop';
			$page_id   = isset( $term_data[1] ) && ! empty( $term_data[1] ) ? intval( $term_data[1] ) : 0;
			
			if ( 'category' === $page_type && $page_id ) {
				$nterm = get_term_by( 'id', $page_id, 'product_cat' );
				if ( $nterm ) {
					$nterm->name   = '#' . $nterm->term_id . ' - ' . $nterm->name;
					$term_options .= '<option value="' . esc_attr( $page_type . ':' . $nterm->term_id ) . '" selected="selected">' . esc_html( $nterm->name ) . '</option>';
				}
				$term_type = 'category';
			} elseif ( 'tag' === $page_type && $page_id ) {
				$nterm = get_term_by( 'id', $page_id, 'product_tag' );
				if ( $nterm ) {
					$nterm->name   = '#' . $nterm->term_id . ' - ' . $nterm->name;
					$term_options .= '<option value="' . esc_attr( $page_type . ':' . $nterm->term_id ) . '" selected="selected">' . esc_html( $nterm->name ) . '</option>';
				}
				$term_type = 'tag';
			} else {
				$term_options .= '<option value="' . esc_attr( 'shop:' . $shop_id ) . '" selected="selected">' . esc_html__( 'Shop', 'shoptimizer' ) . '</option>';
			}
		}

		$target_array = array(
			'shop'     => esc_html__( 'Shop', 'shoptimizer' ),
			'category' => esc_html__( 'Product Category', 'shoptimizer' ),
			'tag'      => esc_html__( 'Product Tag', 'shoptimizer' ),
		);

		$col_count = self::get_col_count();
		$row_count = wc_get_default_product_rows_per_page();

		$target_options = '';
		$target_value   = isset( $feature['target'] ) && ! empty( $feature['target'] ) ? $feature['target'] : 'shop';
		foreach ( $target_array as $target_key => $target_text ) {
			$target_options .= '<option value="' . esc_attr( $target_key ) . '" data-label="' . esc_html( $target_text ) . '" ' . ( $target_key === $target_value ? 'selected="selected"' : '' ) . ' >' . esc_html( $target_text ) . '</option>';
		}

		$row_options   = '';
		$row_value     = isset( $feature['row'] ) && ! empty( $feature['row'] ) ? intval( $feature['row'] ) : 1;
		$total_rows    = $row_count * 2;
		for ( $i = 1; $i <= $total_rows; $i++ ) {
			$row_options .= '<option value="' . esc_attr( $i ) . '" ' . ( $row_value === $i ? 'selected="selected"' : '' ) . '>' . esc_attr( $i ) . '</option>';
		}

		$position_options = '';
		$position_value   = isset( $feature['position'] ) && ! empty( $feature['position'] ) ? intval( $feature['position'] ) : 1;
		for ( $i = 1; $i <= $col_count; $i++ ) {
			$position_options .= '<option value="' . esc_attr( $i ) . '" ' . ( $position_value === $i ? 'selected="selected"' : '' ) . ' >' . esc_attr( $i ) . '</option>';
		}

		$max_width     = $col_count - ( $position_value - 1 );
		$width_options = '';
		$width_value   = isset( $feature['width'] ) && ! empty( $feature['width'] ) ? $feature['width'] : 1;
		for ( $i = 1; $i <= $max_width; $i++ ) {
			$width_title    = $i . ( 1 === $i ? ' column' : ' columns' );
			$width_options .= '<option value="' . esc_attr( $i ) . '" ' . ( (int) $width_value === $i ? 'selected="selected"' : '' ) . ' >' . esc_attr( $width_title ) . '</option>';
		}
		if ( 0 === $max_width ) {
			$width_options .= '<option value="full" ' . ( 'full' === (string) $width_value ? 'selected="selected"' : '' ) . ' >' . esc_html__( 'Full width', 'shoptimizer' ) . '</option>';
		}

		$row_suffix = $this->get_ordinal_suffix( $row_value );
		$help_text  = '';
		if ( 'full' === $width_value ) {
			$help_text .= 'This will appear full-width after ' . $row_value . $row_suffix . ' row of products';
		} else {
			$pos_text = 'product ' . $position_value;
			if ( $width_value > 1 ) {
				$pos_text = 'products ' . $position_value . '-' . ( $position_value + $width_value - 1 ) ;
			}
			$help_text = 'This will replace ' . $pos_text . ' in row ' . $row_value;
		}

		// Check for duplicate position
		$duplicate_warning = null;
		if ( isset( $post->ID ) && $post->ID ) {
			$current_term = get_post_meta( $post->ID, 'shoptimizer_grid_feature_term', true );
			if ( $current_term && isset( $feature['row'] ) && isset( $feature['position'] ) ) {
				$duplicate_warning = $this->check_duplicate_position( $post->ID, $feature, $current_term );
			}
		}

		// Load template file
		include locate_template( 'templates/admin/grid-feature-meta-box.php' );

		wp_nonce_field( 'shoptimizer_nonce', 'shoptimizer_nonce' );
	}

	/**
	 * Admin meta box save.
	 *
	 * @param string $post_id post id.
	 * @param string $post post object.
	 */
	function admin_meta_save( $post_id, $post ) {
		if ( 'cg_grid_feature' === $post->post_type ) {
			if ( isset( $_POST['shoptimizer_grid_feature_tmp'] ) && 1 === (int) $_POST['shoptimizer_grid_feature_tmp'] ) { // phpcs:ignore
				if ( ! current_user_can( 'manage_options' ) ) {
					return;
				}
				$shoptimizer_nonce = isset( $_POST['shoptimizer_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['shoptimizer_nonce'] ) ) : '';
				if ( ! $shoptimizer_nonce || ! wp_verify_nonce( $shoptimizer_nonce, 'shoptimizer_nonce' ) ) {
					return;
				}

				$features = isset( $_POST['shoptimizer_grid_feature'] ) ? map_deep( wp_unslash( $_POST['shoptimizer_grid_feature'] ), 'sanitize_text_field' ) : array();
				$features = array_filter( $features );
				$shop_id  = get_option( 'woocommerce_shop_page_id' );

				$feature_term = isset( $_POST['shoptimizer_grid_feature_term'] ) ? sanitize_text_field( wp_unslash( $_POST['shoptimizer_grid_feature_term'] ) ) : 'shop:' . $shop_id;

				update_post_meta( $post->ID, 'shoptimizer_grid_feature', $features );
				update_post_meta( $post->ID, 'shoptimizer_grid_feature_term', $feature_term );

				// Invalidate cache when grid feature is saved
				$this->invalidate_grid_features_cache( $post->ID );
			}
		}
	}

	/**
	 * Check if another grid feature already exists at this position
	 *
	 * @param int   $current_post_id Current post ID being saved.
	 * @param array $features Feature settings.
	 * @param string $feature_term Target term.
	 * @return array|false Duplicate post info or false.
	 */
	private function check_duplicate_position( $current_post_id, $features, $feature_term ) {
		$args = array(
			'post_type'      => 'cg_grid_feature',
			'posts_per_page' => -1,
			'post_status'    => 'publish',
			'post__not_in'   => array( $current_post_id ),
			'orderby'        => 'ID',
			'order'          => 'ASC',
			'meta_query'     => array(
				array(
					'key'   => 'shoptimizer_grid_feature_term',
					'value' => $feature_term,
				),
			),
		);

		$query = new WP_Query( $args );

		if ( $query->have_posts() ) {
			while ( $query->have_posts() ) {
				$query->the_post();
				$existing_feature = get_post_meta( get_the_ID(), 'shoptimizer_grid_feature', true );

				// Check if row and position match
				if ( isset( $existing_feature['row'] ) && isset( $existing_feature['position'] ) &&
				     isset( $features['row'] ) && isset( $features['position'] ) &&
				     $existing_feature['row'] === $features['row'] &&
				     $existing_feature['position'] === $features['position'] ) {

					// Only show warning if the other post has a LOWER ID (will be displayed instead)
					// This matches the frontend rendering logic
					if ( get_the_ID() < $current_post_id ) {
						wp_reset_postdata();
						return array(
							'title'    => get_the_title(),
							'id'       => get_the_ID(),
							'row'      => $existing_feature['row'],
							'position' => $existing_feature['position'],
						);
					}
				}
			}
			wp_reset_postdata();
		}

		return false;
	}

	/**
	 * Invalidate grid features cache
	 *
	 * @param int $post_id Post ID.
	 */
	public function invalidate_grid_features_cache( $post_id ) {
		if ( get_post_type( $post_id ) !== 'cg_grid_feature' ) {
			return;
		}

		// Delete the specific transient for this feature's assigned page
		$feature_term = get_post_meta( $post_id, 'shoptimizer_grid_feature_term', true );
		if ( $feature_term ) {
			delete_transient( 'shoptimizer_grid_features_' . md5( $feature_term ) );
		}

		// Also clear shop page transient as a fallback
		$shop_id = get_option( 'woocommerce_shop_page_id' );
		delete_transient( 'shoptimizer_grid_features_' . md5( 'shop:' . $shop_id ) );
	}

	/**
	 * Define meta columns
	 */
	private function meta_columns() {
		return array(
			'target'   => esc_html__( 'Target', 'shoptimizer' ),
			'row'      => esc_html__( 'Row', 'shoptimizer' ),
			'position' => esc_html__( 'Position', 'shoptimizer' ),
			'width'    => esc_html__( 'Width', 'shoptimizer' ),
			'display'  => esc_html__( 'Display', 'shoptimizer' ),
		);
	}

	/**
	 * Add columns to admin list
	 */
	public function add_admin_columns( $columns ) {
		$new = array();
		foreach ( $columns as $key => $label ) {
			$new[ $key ] = $label;
			if ( $key === 'title' ) {
				foreach ( $this->meta_columns() as $meta_key => $meta_label ) {
					$new[ 'meta_' . $meta_key ] = $meta_label;
				}
			}
		}
		return $new;
	}
	
	/**
	* Render column values
	*/
	public function render_admin_columns( $column, $post_id ) {
		if ( strpos( $column, 'meta_' ) !== 0 ) {
			return;
		}
		$key = str_replace( 'meta_', '', $column );
	
		$meta = get_post_meta( $post_id, 'shoptimizer_grid_feature', true );
		if ( ! is_array( $meta ) ) {
			echo '—';
			return;
		}
		if ( 'target' === $key && isset( $meta[ $key ] ) ) {
			$term_data = explode( ':', (string) get_post_meta( $post_id, 'shoptimizer_grid_feature_term', true ) );
			$page_type = isset( $term_data[0] ) && ! empty( $term_data[0] ) ? $term_data[0] : 'shop';
			$page_id   = isset( $term_data[1] ) && ! empty( $term_data[1] ) ? intval( $term_data[1] ) : 0;
			$page_title = '';
			if ( 'category' === $page_type && $page_id ) {
				$nterm = get_term_by( 'id', $page_id, 'product_cat' );
				if ( $nterm ) {
					$page_title = esc_html__( 'Category: ', 'shoptimizer' ) . '#' . $nterm->term_id . ' - ' . $nterm->name;
				}
			} elseif ( 'tag' === $page_type && $page_id ) {
				$nterm = get_term_by( 'id', $page_id, 'product_tag' );
				if ( $nterm ) {
					$page_title = esc_html__( 'Tag: ', 'shoptimizer' ) . '#' . $nterm->term_id . ' - ' . $nterm->name;
				}
			} else {
				$page_title = esc_html__( 'Shop', 'shoptimizer' );
			}
			$meta[ $key ] = $page_title;
		}
		// Position is just a number now
		if ( 'width' === $key && isset( $meta[ $key ] ) ) {
			if ( 'full' === $meta[ $key ] ) {
				$meta[ $key ] = esc_html__( 'Full width', 'shoptimizer' );
			} else {
				$meta[ $key ] = $meta[ $key ] . ( '1' === (string) $meta[ $key ] ? ' column' : ' columns' );
			}
		}

		// Check if this feature will actually be displayed
		if ( 'display' === $key ) {
			$post_status = get_post_status( $post_id );

			// Only show display status for published posts
			if ( 'publish' !== $post_status ) {
				echo '—';
				return;
			}

			$feature_term = get_post_meta( $post_id, 'shoptimizer_grid_feature_term', true );

			if ( $feature_term && isset( $meta['row'] ) && isset( $meta['position'] ) ) {
				// Load all positions once to avoid N+1 queries
				$this->load_admin_positions();

				$position_key = $feature_term . ':' . $meta['row'] . ':' . $meta['position'];
				$lowest_id = isset( self::$admin_positions[ $position_key ] ) ? self::$admin_positions[ $position_key ] : $post_id;
				$will_display = ( $lowest_id >= $post_id );

				if ( $will_display ) {
					echo '<span style="color: #46b450; font-size: 18px;" title="' . esc_attr__( 'Will be displayed', 'shoptimizer' ) . '">✓</span>';
				} else {
					echo '<span style="color: #dc3232; font-size: 18px;" title="' . esc_attr__( 'Hidden - Another grid feature is already at this position', 'shoptimizer' ) . '">✗</span>';
				}
			} else {
				echo '—';
			}
			return;
		}

		echo isset( $meta[ $key ] ) ? esc_html( $meta[ $key ] ) : '—';
	}

	/**
	 * Pre-load all grid feature positions for admin column rendering.
	 * Maps each term:row:position key to the lowest post ID at that position.
	 */
	private function load_admin_positions() {
		if ( self::$admin_positions_loaded ) {
			return;
		}
		self::$admin_positions_loaded = true;

		$query = new WP_Query( array(
			'post_type'      => 'cg_grid_feature',
			'posts_per_page' => -1,
			'post_status'    => 'publish',
			'orderby'        => 'ID',
			'order'          => 'ASC',
			'fields'         => 'ids',
		) );

		if ( ! empty( $query->posts ) ) {
			foreach ( $query->posts as $pid ) {
				$f = get_post_meta( $pid, 'shoptimizer_grid_feature', true );
				$term = get_post_meta( $pid, 'shoptimizer_grid_feature_term', true );
				if ( ! is_array( $f ) || ! $term || ! isset( $f['row'] ) || ! isset( $f['position'] ) ) {
					continue;
				}
				$position_key = $term . ':' . $f['row'] . ':' . $f['position'];
				// First ID encountered is lowest (ordered ASC)
				if ( ! isset( self::$admin_positions[ $position_key ] ) ) {
					self::$admin_positions[ $position_key ] = $pid;
				}
			}
		}
	}

	/**
	 * Get products or categories IDs
	 */
	function get_grid_feature_terms() {
		$return = array();
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json( $return );
		}

		$shoptimizer_nonce = isset( $_GET['shoptimizer_nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['shoptimizer_nonce'] ) ) : '';
		if ( ! $shoptimizer_nonce || ! wp_verify_nonce( $shoptimizer_nonce, 'shoptimizer_nonce' ) ) {
			wp_send_json( $return );
		}

		$type = isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'product';
		if ( 'category' === $type || 'tag' === $type ) {
			$query = ! empty( $_GET['q'] ) ? sanitize_text_field( wp_unslash( $_GET['q'] ) ) : '';
			$args  = array(
				'taxonomy'   => 'product_cat',
				'name__like' => $query,
				'hide_empty' => true,
				'number'     => 20,
			);

			if ( 'tag' === $type ) {
				$args['taxonomy'] = 'product_tag';
			}
			if ( is_numeric( $query ) ) {
				$terms = array( get_term( $query, $args['taxonomy'] ) );
			} else {
				$terms = get_terms( $args );
			}
			if ( is_array( $terms ) && count( $terms ) > 0 ) {
				foreach ( $terms as $term ) {
					if ( isset( $term->name ) ) {
						$term->name = '#' . $term->term_id . ' - ' . $term->name;
						$return[]   = array( $type . ':' . $term->term_id, $term->name );
					}
				}
			}
		}

		wp_send_json( $return );
	}

	/**
	 * Get ordinal suffix
	 */
	public function get_ordinal_suffix( $number ) {
		if ( $number % 100 >= 11 && $number % 100 <= 13 ) {
			return 'th';
		}
		switch ( $number % 10 ) {
			case 1: return 'st';
			case 2: return 'nd';
			case 3: return 'rd';
			default: return 'th';
		}
	}

	/**
	 * Current page type
	 */
	private function get_current_page_type() {
		if ( function_exists( 'is_shop' ) && is_shop() ) {
			$shop_id = get_option( 'woocommerce_shop_page_id' );
			return 'shop:' . absint( $shop_id );
		}
		if ( function_exists( 'is_product_category' ) && is_product_category() ) {
			$term = get_queried_object();
			if ( $term && ! is_wp_error( $term ) ) {
				return 'category:' . absint( $term->term_id );
			}
		}
		if ( function_exists( 'is_product_tag' ) && is_product_tag() ) {
			$term = get_queried_object();
			if ( $term && ! is_wp_error( $term ) ) {
				return 'tag:' . absint( $term->term_id );
			}
		}
	
		return '';
	}

	public function setup_features() {
		self::$product_count   = -1;
		self::$features        = array();
		self::$feature_widths  = array();
		self::$grid_position   = 0;

		$page_type = $this->get_current_page_type();
		if ( ! $page_type ) {
			return;
		}

		// Check transient cache first
		$cache_key = 'shoptimizer_grid_features_' . md5( $page_type );
		$cached_data = get_transient( $cache_key );

		if ( false !== $cached_data && is_array( $cached_data ) ) {
			// Use cached data
			self::$features = $cached_data['features'];
			self::$feature_widths = $cached_data['widths'];
			return;
		}

		// Cache miss - run query
		$query_args = array(
			'post_type'      => 'cg_grid_feature',
			'post_status'    => 'publish',
			'posts_per_page' => -1,
			'orderby'        => 'ID',
			'order'          => 'ASC',
			'meta_query'     => array(
				array(
					'key'   => 'shoptimizer_grid_feature_term',
					'value' => $page_type,
				),
			),
		);
		$query_args = apply_filters( 'shoptimizer_grid_features_query_args', $query_args, $page_type );
		$query = new WP_Query( $query_args );

		if ( ! $query->have_posts() ) {
			return;
		}

		$col_count = self::get_col_count();
		$row_count = wc_get_default_product_rows_per_page();

		while ( $query->have_posts() ) {
			$query->the_post();

			$feature   = get_post_meta( get_the_ID(), 'shoptimizer_grid_feature', true );
			$row_value = isset( $feature['row'] ) && ! empty( $feature['row'] ) ? intval( $feature['row'] ) : 1;
			$col_value = isset( $feature['position'] ) && ! empty( $feature['position'] ) ? intval( $feature['position'] ) : 1;
			$col_value = min( $col_value, $col_count );

			// Normal position calculation for width
			$max_width     = $col_count - ( $col_value - 1 );
			$width_options = array();
			$width_value   = isset( $feature['width'] ) && ! empty( $feature['width'] ) ? (string) $feature['width'] : '1';
			for ( $i = 1; $i <= $max_width; $i++ ) {
				$width_options[] = (string) $i;
			}
			if ( ! in_array( $width_value, $width_options, true ) ) {
				$width_value = '1';
			}
			$var_cols = 1;
			if ( 'full' === $width_value ) {
				$var_cols = $col_count;
			} elseif ( is_numeric( $width_value ) && (int) $width_value ) {
				$var_cols = max( 1, (int) $width_value );
			}

			// Calculate position in the product grid
			$position = ( $col_count * ( $row_value - 1 ) ) + ( $col_value - 1 );

			$feature['style'] = '--span: ' . $var_cols . ';';
			if ( ! isset( self::$features[ $position ] ) ) {
				self::$features[ $position ]       = $this->render_feature( get_the_ID(), $feature, $position, $var_cols );
				self::$feature_widths[ $position ] = $var_cols;
			}
		}

		ksort( self::$features );

		wp_reset_postdata();

		// Cache the results (invalidated on save/trash/delete)
		set_transient(
			$cache_key,
			array(
				'features' => self::$features,
				'widths'   => self::$feature_widths,
			),
			DAY_IN_SECONDS
		);
	}

	/**
	 * Check if product should be rendered (not replaced by Grid Feature)
	 */
	public function should_render_product( $should_render = true ) {
		// Grid features take up grid positions, but products are not removed from the loop
		// All products continue to render after grid features
		// The grid's auto-flow handles positioning naturally
		return $should_render;
	}

	/**
	 * Maybe insert banner after item
	 */
	public function maybe_insert_banner() {

		if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
			return;
		}

		if ( ! wc_get_loop_prop( 'is_paginated' ) ) {
			return;
		}

		// Insert any features scheduled for the current grid slot BEFORE the product renders
		while ( isset( self::$features[ self::$grid_position ] ) ) {
			echo self::$features[ self::$grid_position ];
			self::$grid_position += self::$feature_widths[ self::$grid_position ];
		}

		// The product will render after this hook, so it consumes 1 grid slot
		self::$grid_position++;
	}

	/**
	 * Render feature
	 */
	private function render_feature( $post_id, $feature, $position, $width = 1 ) {
		// Get the CSS variable style that was set earlier (--span)
		$css_vars = isset( $feature['style'] ) ? $feature['style'] : '';
		$content  = apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) );
		$content  = apply_filters( 'shoptimizer_grid_feature_content', $content, $post_id );

		$html = '
<li class="product cg-grid-feature cg-grid-feature-' . esc_attr( $post_id ) . '" style="' . $css_vars . '" role="complementary" aria-label="' . esc_attr__( 'Promotional content', 'shoptimizer' ) . '" data-position="' . esc_attr( $position ) . '" data-post-id="' . esc_attr( $post_id ) . '">
	<div class="cg-grid-feature-inner">
		' . $content . '
	</div>
</li>';

		return apply_filters( 'shoptimizer_grid_feature_html', $html, $post_id, $feature, $position );
	}

	/**
	 * Body class
	 */
	public function body_class( $classes ) {

		if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
			return $classes;
		}

		$classes[] = 'shoptimizer-grid-feature';

		return $classes;
	}

	/**
	 * Enqueue frontend styles
	 */
	public function dynamic_styles() {
		// Only output dynamic styles on relevant pages
		if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
			return;
		}

		// Check if any Grid Features exist
		$has_features = wp_count_posts( 'cg_grid_feature' );
		if ( ! $has_features || ( isset( $has_features->publish ) && $has_features->publish < 1 ) ) {
			return;
		}

		$col_count = self::get_col_count();
		$thumbnail_size = wc_get_image_size( 'woocommerce_thumbnail' );
		$width = $thumbnail_size['width'];
		$height = $thumbnail_size['height'];
		?>
<style>
/* Dynamic CSS custom properties */
@media (min-width: 771px) {
	.shoptimizer-grid-feature .site-main ul.products {
		--grid-columns: <?php echo esc_attr( $col_count ); ?>;
	}
}

/* Dynamic aspect ratio for product grid boxes */
@media (min-width: 993px) {
	.shoptimizer-product-grid-ratio-box {
		aspect-ratio: <?php echo esc_attr( $width ); ?> / <?php echo esc_attr( $height ); ?>;
	}
}
</style>
		<?php
	}
}

$shoptimizer_layout_woocommerce_grid_feature = shoptimizer_get_option( 'shoptimizer_layout_woocommerce_grid_feature' );
if ( true === $shoptimizer_layout_woocommerce_grid_feature ) {
	new Shoptimizer_Grid_Feature();
}

endif;
