<?php

namespace AIPT\Utils\ProductFilter;

/**
 * ProductFilterBuilder - WooCommerce ürünleri için filtreleme mantığı oluşturur
 *
 * Bu sınıf, WooCommerce ürünlerini filtrelemek için wc_get_products()
 * fonksiyonunu kullanarak sorguları oluşturur ve yönetir. Fluent interface
 * kullanarak filtreleme kriterlerinin kolay bir şekilde birleştirilmesini sağlar.
 *
 * Filter logic supports both AND (intersection) and OR (union) modes via filterLogic().
 *
 * @version 2.4.5
 * @since 2.4.1 Migrated from WP_Query to wc_get_products() for better WooCommerce integration
 * @since 2.4.2 Fixed stock_status and price ordering using WooCommerce native parameters
 * @since 2.4.3 Fixed filter intersection logic and price sorting with include parameter
 * @since 2.4.4 Added AND/OR filter logic support, fixed price sorting to work at DB level across all pages
 */
class ProductFilterBuilder {
    /**
     * @var array wc_get_products() için oluşturulan argümanlar
     */
    private $args = [];

    /**
     * @var array Taksonomi sorguları
     */
    private $tax_query = [];

    /**
     * @var array Filtrelenmiş ürün ID'leri - tüm filtreler bu array üzerinden kesişim alır
     */
    private $filtered_product_ids = null;

    /**
     * @var string|null Price sorting direction (ASC veya DESC)
     */
    private $price_sort_direction = null;

    /**
     * @var int|null Cursor for cursor-based pagination (last seen product ID)
     */
    private $cursor_after = null;

    /**
     * @var int|null Cursor limit for cursor-based pagination
     */
    private $cursor_limit = null;

    /**
     * @var string Filter logic mode: 'and' (intersection) or 'or' (union)
     */
    private $filter_logic = 'and';

    /**
     * ProductFilterBuilder constructor.
     */
    public function __construct() {
        $this->clear();
    }

    /**
     * Enable cursor-based pagination for large datasets.
     * More efficient than offset pagination for large tables.
     *
     * @param int|null $after_id Product ID to start after (cursor)
     * @param int      $limit    Number of products to fetch
     * @return ProductFilterBuilder
     */
    public function cursorPagination(?int $after_id, int $limit = 50): ProductFilterBuilder {
        $this->cursor_after = $after_id;
        $this->cursor_limit = max(1, min($limit, 500)); 

        $this->args['paginate'] = false;
        $this->args['limit'] = $this->cursor_limit;
        return $this;
    }

    /**
     * Sayfa numarası ve sayfa başına öğe sayısını ayarlar
     *
     * @param int $page Sayfa numarası
     * @param int $perPage Sayfa başına öğe sayısı
     * @return ProductFilterBuilder
     */
    public function pagination($page, $perPage = 10) {
        $this->args['page'] = max(1, absint($page));
        $this->args['limit'] = max(1, absint($perPage));
        return $this;
    }

    /**
     * Metin tabanlı arama filtresi ekler (başlık veya SKU'da)
     *
     * @param string $search Arama terimi
     * @return ProductFilterBuilder
     */
    public function search($search) {
        if (!empty($search)) {
            global $wpdb;
            $search_term = sanitize_text_field($search);

            $search_query = $wpdb->prepare(
                "SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                LEFT JOIN {$wpdb->postmeta} pm ON (p.ID = pm.post_id AND pm.meta_key = '_sku')
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND (
                    p.post_title LIKE %s
                    OR pm.meta_value LIKE %s
                )",
                '%' . $wpdb->esc_like($search_term) . '%',
                '%' . $wpdb->esc_like($search_term) . '%'
            );

            $product_ids = $wpdb->get_col($search_query);
            $this->restrictToProductIds($product_ids);
        }

        return $this;
    }

    /**
     * Ürün durumu filtresi ekler (publish, draft, private)
     *
     * @param string|array $status Ürün durumu
     * @return ProductFilterBuilder
     */
    public function status($status) {
        if (!empty($status)) {
            $this->args['status'] = is_array($status) ? array_map('sanitize_text_field', $status) : sanitize_text_field($status);
        }

        return $this;
    }

    /**
     * Stok durumu filtresi ekler
     * SQL ile filtered_product_ids'e ekler - diğer filtrelerle kesişim sağlar
     *
     * @param string $stockStatus Stok durumu (instock, outofstock, onbackorder)
     * @return ProductFilterBuilder
     */
    public function stockStatus($stockStatus) {
        if (empty($stockStatus)) {
            return $this;
        }

        $allowed = ['instock', 'outofstock', 'onbackorder'];
        $sanitized = sanitize_text_field($stockStatus);

        if (!in_array($sanitized, $allowed, true)) {
            return $this;
        }

        global $wpdb;

        $query = $wpdb->prepare(
            "SELECT DISTINCT p.ID
            FROM {$wpdb->posts} p
            INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
            WHERE p.post_type = 'product'
            AND p.post_status IN ('publish', 'draft', 'private')
            AND pm.meta_key = '_stock_status'
            AND pm.meta_value = %s",
            $sanitized
        );

        $product_ids = $wpdb->get_col($query);
        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Adds category filter.
     *
     * Resolves slugs to term IDs via get_terms() which respects
     * Polylang's terms_clauses filter for multilingual compatibility.
     * This also removes the wp_terms JOIN, improving query performance.
     *
     * @param string|array $category Category slug or array of slugs.
     * @return ProductFilterBuilder
     */
    public function category($category) {
        if (empty($category)) {
            return $this;
        }

        $slugs = is_array($category) ? $category : [$category];
        $slugs = array_map('sanitize_text_field', $slugs);

        if (empty($slugs)) {
            return $this;
        }

        $term_ids = $this->resolveTermSlugsToIds($slugs, 'product_cat');

        if (empty($term_ids)) {
            $this->restrictToProductIds([]);
            return $this;
        }

        $this->filterByTermIds($term_ids, 'product_cat');

        return $this;
    }

    /**
     * Adds tag filter (by specific tag slugs).
     *
     * Resolves slugs to term IDs via get_terms() which respects
     * Polylang's terms_clauses filter for multilingual compatibility.
     *
     * @param string|array $tag Tag slug or array of slugs.
     * @return ProductFilterBuilder
     */
    public function tag($tag) {
        if (empty($tag)) {
            return $this;
        }

        $slugs = is_array($tag) ? $tag : [$tag];
        $slugs = array_map('sanitize_text_field', $slugs);

        if (empty($slugs)) {
            return $this;
        }

        $term_ids = $this->resolveTermSlugsToIds($slugs, 'product_tag');

        if (empty($term_ids)) {
            $this->restrictToProductIds([]);
            return $this;
        }

        $this->filterByTermIds($term_ids, 'product_tag');

        return $this;
    }

    /**
     * Resolves taxonomy term slugs to term IDs via WordPress get_terms() API.
     *
     * Delegates Polylang-specific adjustments (language bypass, translation expansion)
     * to PolylangTermHandler for multilingual compatibility.
     *
     * @param array  $slugs    Array of term slugs to resolve.
     * @param string $taxonomy Taxonomy name (e.g., 'product_cat', 'product_tag').
     * @return int[] Array of resolved term IDs.
     */
    private function resolveTermSlugsToIds(array $slugs, string $taxonomy): array {
        $args = [
            'taxonomy'   => $taxonomy,
            'slug'       => $slugs,
            'fields'     => 'ids',
            'hide_empty' => false,
        ];

        PolylangTermHandler::adjustTermQueryArgs($args);

        $terms = get_terms($args);

        if (is_wp_error($terms) || empty($terms)) {
            return [];
        }

        $term_ids = array_map('intval', $terms);

        return PolylangTermHandler::expandTermIdsWithTranslations($term_ids);
    }

    /**
     * Filters products by taxonomy term IDs.
     *
     * Uses term_id matching instead of slug matching, eliminating the need
     * for a wp_terms table JOIN and improving query performance.
     *
     * @param int[]  $term_ids Array of term IDs to filter by.
     * @param string $taxonomy Taxonomy name (e.g., 'product_cat', 'product_tag').
     * @return void
     */
    private function filterByTermIds(array $term_ids, string $taxonomy): void {
        global $wpdb;

        $placeholders = implode(',', array_fill(0, count($term_ids), '%d'));

        $sql = "SELECT DISTINCT p.ID FROM {$wpdb->posts} p INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE p.post_type = 'product' AND p.post_status IN ('publish', 'draft', 'private') AND tt.taxonomy = %s AND tt.term_id IN ($placeholders)";

        $args = array_merge([$taxonomy], $term_ids);

        $query = $wpdb->prepare($sql, ...$args);

        $product_ids = $wpdb->get_col($query);
        $this->restrictToProductIds($product_ids);
    }

    /**
     * Açıklama durumuna göre filtreler
     *
     * @param bool|null $hasDescription Tam açıklama var mı
     * @param bool|null $hasShortDescription Kısa açıklama var mı
     * @return ProductFilterBuilder
     */
    public function descriptions($hasDescription = null, $hasShortDescription = null) {
        if ($hasDescription === null && $hasShortDescription === null) {
            return $this;
        }

        global $wpdb;

        $conditions = ["p.post_type = 'product'", "p.post_status IN ('publish', 'draft', 'private')"];

        if ($hasDescription === true) {
            $conditions[] = "p.post_content IS NOT NULL AND TRIM(p.post_content) != ''";
        } elseif ($hasDescription === false) {
            $conditions[] = "(p.post_content IS NULL OR TRIM(p.post_content) = '')";
        }

        if ($hasShortDescription === true) {
            $conditions[] = "p.post_excerpt IS NOT NULL AND TRIM(p.post_excerpt) != ''";
        } elseif ($hasShortDescription === false) {
            $conditions[] = "(p.post_excerpt IS NULL OR TRIM(p.post_excerpt) = '')";
        }

        $where_clause = implode(' AND ', $conditions);

        $product_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} p WHERE {$where_clause}");

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Etiket durumuna göre filtreler (var/yok)
     *
     * @param bool|null $hasTags Etiketleri var mı
     * @return ProductFilterBuilder
     */
    public function tags($hasTags = null) {
        if ($hasTags === null) {
            return $this;
        }

        global $wpdb;

        if ($hasTags) {

            $product_ids = $wpdb->get_col("
                SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
                INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                WHERE p.post_type = 'product'
                AND tt.taxonomy = 'product_tag'
                AND p.post_status IN ('publish', 'draft', 'private')
            ");
        } else {

            $product_ids = $wpdb->get_col("
                SELECT p.ID
                FROM {$wpdb->posts} p
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND p.ID NOT IN (
                    SELECT DISTINCT tr.object_id
                    FROM {$wpdb->term_relationships} tr
                    INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                    WHERE tt.taxonomy = 'product_tag'
                )
            ");
        }

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Öne çıkan görsel (featured image) durumuna göre filtreler
     *
     * @param bool $hasImage Öne çıkan görseli var mı
     * @return ProductFilterBuilder
     */
    public function image($hasImage) {
        if ($hasImage === null) {
            return $this;
        }

        global $wpdb;

        if ($hasImage) {

            $product_ids = $wpdb->get_col("
                SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND pm.meta_key = '_thumbnail_id'
                AND pm.meta_value IS NOT NULL
                AND pm.meta_value != ''
                AND pm.meta_value != '0'
            ");
        } else {

            $product_ids = $wpdb->get_col("
                SELECT p.ID
                FROM {$wpdb->posts} p
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND p.ID NOT IN (
                    SELECT DISTINCT pm.post_id
                    FROM {$wpdb->postmeta} pm
                    WHERE pm.meta_key = '_thumbnail_id'
                    AND pm.meta_value IS NOT NULL
                    AND pm.meta_value != ''
                    AND pm.meta_value != '0'
                )
            ");
        }

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Galeri görselleri durumuna göre filtreler
     *
     * @param bool $hasGallery Galeri görselleri var mı
     * @return ProductFilterBuilder
     */
    public function gallery($hasGallery) {
        if ($hasGallery === null) {
            return $this;
        }

        global $wpdb;

        if ($hasGallery) {

            $product_ids = $wpdb->get_col("
                SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND pm.meta_key = '_product_image_gallery'
                AND pm.meta_value IS NOT NULL
                AND TRIM(pm.meta_value) != ''
            ");
        } else {

            $product_ids = $wpdb->get_col("
                SELECT p.ID
                FROM {$wpdb->posts} p
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND p.ID NOT IN (
                    SELECT DISTINCT pm.post_id
                    FROM {$wpdb->postmeta} pm
                    WHERE pm.meta_key = '_product_image_gallery'
                    AND pm.meta_value IS NOT NULL
                    AND TRIM(pm.meta_value) != ''
                )
            ");
        }

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * SKU durumuna göre filtreler
     *
     * @param bool $hasSku SKU var mı
     * @return ProductFilterBuilder
     */
    public function sku($hasSku) {
        if ($hasSku === null) {
            return $this;
        }

        global $wpdb;

        if ($hasSku) {

            $product_ids = $wpdb->get_col("
                SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND pm.meta_key = '_sku'
                AND pm.meta_value IS NOT NULL
                AND TRIM(pm.meta_value) != ''
            ");
        } else {

            $product_ids = $wpdb->get_col("
                SELECT p.ID
                FROM {$wpdb->posts} p
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND p.ID NOT IN (
                    SELECT DISTINCT pm.post_id
                    FROM {$wpdb->postmeta} pm
                    WHERE pm.meta_key = '_sku'
                    AND pm.meta_value IS NOT NULL
                    AND TRIM(pm.meta_value) != ''
                )
            ");
        }

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Ürün tipine göre filtreler (simple, variable, grouped, external)
     *
     * @param string|array $types Ürün tipi veya tipleri
     * @return ProductFilterBuilder
     */
    public function productType($types) {
        if (empty($types)) {
            return $this;
        }

        global $wpdb;

        $types = is_array($types) ? $types : [$types];
        $types = array_map('sanitize_text_field', $types);

        $allowed_types = ['simple', 'variable', 'grouped', 'external'];
        $types = array_intersect($types, $allowed_types);

        if (empty($types)) {
            return $this;
        }

        $placeholders = implode(',', array_fill(0, count($types), '%s'));

        $sql = "SELECT DISTINCT p.ID 
                FROM {$wpdb->posts} p 
                INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id 
                INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id 
                INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id 
                WHERE p.post_type = 'product' 
                AND p.post_status IN ('publish', 'draft', 'private') 
                AND tt.taxonomy = 'product_type' 
                AND t.slug IN ($placeholders)";

        $query = $wpdb->prepare($sql, ...$types);

        $product_ids = $wpdb->get_col($query);
        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Öne çıkan (featured) durumuna göre filtreler
     *
     * @param bool $isFeatured Öne çıkan mı
     * @return ProductFilterBuilder
     */
    public function featured($isFeatured) {
        if ($isFeatured === null) {
            return $this;
        }

        global $wpdb;

        if ($isFeatured) {

            $product_ids = $wpdb->get_col("
                SELECT DISTINCT p.ID
                FROM {$wpdb->posts} p
                INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
                INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND tt.taxonomy = 'product_visibility'
                AND t.slug = 'featured'
            ");
        } else {

            $product_ids = $wpdb->get_col("
                SELECT p.ID
                FROM {$wpdb->posts} p
                WHERE p.post_type = 'product'
                AND p.post_status IN ('publish', 'draft', 'private')
                AND p.ID NOT IN (
                    SELECT DISTINCT tr.object_id
                    FROM {$wpdb->term_relationships} tr
                    INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                    INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
                    WHERE tt.taxonomy = 'product_visibility'
                    AND t.slug = 'featured'
                )
            ");
        }

        $this->restrictToProductIds($product_ids);

        return $this;
    }

    /**
     * Filters by on-sale status.
     *
     * @param bool $isOnSale Whether to filter for on-sale products.
     * @return ProductFilterBuilder
     */
	public function onSale($isOnSale) {
		if ($isOnSale === null) {
			return $this;
		}

		global $wpdb;

		if ($isOnSale) {
			$product_ids = \wc_get_product_ids_on_sale();
			$this->restrictToProductIds($product_ids);
		} else {

			$on_sale_ids = \wc_get_product_ids_on_sale();
			
			if (empty($on_sale_ids)) {

				return $this;
			}
			

			$placeholders = implode(',', array_fill(0, count($on_sale_ids), '%d'));

			$product_ids = $wpdb->get_col(
				$wpdb->prepare(
					"SELECT p.ID
					FROM {$wpdb->posts} p
					WHERE p.post_type = 'product'
					AND p.post_status IN ('publish','draft','private')
					AND p.ID NOT IN ($placeholders)",
					...$on_sale_ids
				)
			);

			$this->restrictToProductIds($product_ids ?: []);
		}

        return $this;
    }

    /**
     * Sıralama parametrelerini ayarlar
     *
     * @param string $orderby Sıralama alanı (date, title, price, id, menu_order)
     * @param string $order Sıralama yönü (ASC veya DESC)
     * @return ProductFilterBuilder
     */
    public function orderBy($orderby, $order = 'DESC') {
        $order = strtoupper(sanitize_text_field($order));
        $this->args['order'] = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';

        $orderby = sanitize_text_field($orderby);

        if ($orderby === 'price') {
            $this->price_sort_direction = $this->args['order'];
            $this->args['orderby'] = 'date'; 
        } else {
            $this->price_sort_direction = null;
            $allowed_orderby = ['date', 'title', 'id', 'name', 'modified', 'menu_order'];
            $this->args['orderby'] = in_array($orderby, $allowed_orderby, true) ? $orderby : 'date';
        }

        return $this;
    }

    /**
     * Sets the filter logic mode (AND or OR)
     *
     * @param string $logic 'and' for intersection (default), 'or' for union
     * @return ProductFilterBuilder
     */
    public function filterLogic($logic) {
        $logic = strtolower(sanitize_text_field($logic));
        $this->filter_logic = in_array($logic, ['and', 'or'], true) ? $logic : 'and';
        return $this;
    }

    /**
     * Tüm parametreleri temizler ve varsayılan duruma döner
     *
     * @return ProductFilterBuilder
     */
    public function clear() {
        $this->args = [
            'limit' => 10,
            'page' => 1,
            'status' => ['publish', 'draft', 'private'],
            'orderby' => 'date',
            'order' => 'DESC',
            'return' => 'ids',
            'paginate' => true
        ];

        $this->tax_query = [];
        $this->filtered_product_ids = null;
        $this->price_sort_direction = null;
        $this->cursor_after = null;
        $this->cursor_limit = null;
        $this->filter_logic = 'and';

        return $this;
    }

    /**
     * Oluşturulan wc_get_products() argümanlarını döndürür
     *
     * @return array wc_get_products() argümanları
     */
    public function getArgs() {
        $args = $this->args;

        $tax_queries = [];

        if (!empty($this->tax_query)) {
            foreach ($this->tax_query as $query) {
                $tax_queries[] = $query;
            }
        }

        if (function_exists('wc_get_product_visibility_term_ids')) {
            $product_visibility_terms = \wc_get_product_visibility_term_ids();

            if (!empty($product_visibility_terms['exclude-from-catalog'])) {
                $tax_queries[] = [
                    'taxonomy' => 'product_visibility',
                    'field'    => 'term_taxonomy_id',
                    'terms'    => [$product_visibility_terms['exclude-from-catalog']],
                    'operator' => 'NOT IN',
                ];
            }
        }

        if (!empty($tax_queries)) {
            $args['tax_query'] = array_merge(['relation' => 'AND'], $tax_queries);
        }

        if ($this->filtered_product_ids !== null) {
            $filtered_ids = $this->filtered_product_ids;

            if ($this->price_sort_direction !== null && !empty($filtered_ids)) {
                $filtered_ids = $this->sortProductIdsByPrice($filtered_ids, $this->price_sort_direction);

                $args['orderby'] = 'post__in';
                unset($args['order']); 
            }

            $args['include'] = !empty($filtered_ids) ? $filtered_ids : [0];
        } elseif ($this->price_sort_direction !== null) {

            $direction = strtoupper($this->price_sort_direction) === 'DESC' ? 'DESC' : 'ASC';
            

            $args['orderby']  = 'meta_value_num';
            $args['meta_key'] = '_price';
            $args['order']    = $direction;
            

            $price_meta_query = [
                [
                    'key'     => '_price',
                    'compare' => 'EXISTS',
                ],
            ];
            

            if ( isset( $args['meta_query'] ) && is_array( $args['meta_query'] ) ) {
                $args['meta_query'] = array_merge( $args['meta_query'], $price_meta_query );
            } else {
                $args['meta_query'] = $price_meta_query;
            }
        }

        if ($this->cursor_after !== null) {

            if ($this->filtered_product_ids !== null) {

                sort($this->filtered_product_ids, SORT_NUMERIC);
                

                $this->filtered_product_ids = array_filter(
                    $this->filtered_product_ids,
                    fn($id) => $id > $this->cursor_after
                );
                $args['include'] = !empty($this->filtered_product_ids) 
                    ? array_slice($this->filtered_product_ids, 0, $this->cursor_limit ?? 50) 
                    : [0];
            } else {

                global $wpdb;

                $product_ids = $wpdb->get_col($wpdb->prepare(
                    "SELECT ID FROM {$wpdb->posts} 
                     WHERE post_type = 'product' 
                     AND post_status IN ('publish', 'draft', 'private')
                     AND ID > %d 
                     ORDER BY ID ASC 
                     LIMIT %d",
                    $this->cursor_after,
                    $this->cursor_limit ?? 50
                ));
                $args['include'] = !empty($product_ids) ? array_map('intval', $product_ids) : [0];
            }

            $args['orderby'] = 'ID';
            $args['order'] = 'ASC';
        }

        return $args;
    }

    /**
     * Sorts product IDs by price at the database level
     *
     * This method is called when price sorting is active to ensure correct
     * sorting across all pages, not just the current page.
     *
     * @param array  $product_ids Product IDs to sort
     * @param string $direction   Sort direction (ASC or DESC)
     * @return array Sorted product IDs
     */
	private function sortProductIdsByPrice($product_ids, $direction = 'ASC') {
		if (empty($product_ids)) {
			return [];
		}

		global $wpdb;

		$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
		$product_ids = array_map( 'intval', $product_ids );
		$placeholders = implode( ',', array_fill( 0, count( $product_ids ), '%d' ) );

		$query = $wpdb->prepare("
			SELECT p.ID
			FROM {$wpdb->posts} p
			LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_price'
			WHERE p.ID IN ({$placeholders})
			ORDER BY 
				CASE WHEN pm.meta_value IS NULL OR pm.meta_value = '' THEN 1 ELSE 0 END,
				CAST(pm.meta_value AS DECIMAL(10,2)) {$direction}
		", ...$product_ids );

		$sorted_ids = $wpdb->get_col( $query );

        return array_map('intval', $sorted_ids);
    }

	/**
	 * Restrict to specific product IDs with filter logic support.
	 *
	 * @param array|null $product_ids New filtered product IDs to combine.
	 * @return self Fluent interface.
	 */
	private function restrictToProductIds( $product_ids ) {
		if ( $product_ids === null ) {
			return $this;
		}

		$product_ids = array_map( 'intval', $product_ids );

		if ( $this->filtered_product_ids === null ) {

			$this->filtered_product_ids = $product_ids;
		} elseif ( $this->filter_logic === 'or' ) {

			$this->filtered_product_ids = array_values( array_unique( array_merge( $this->filtered_product_ids, $product_ids ) ) );
		} else {

			$this->filtered_product_ids = array_values( array_intersect( $this->filtered_product_ids, $product_ids ) );
		}

		return $this;
	}

    /**
     * Filtreleri uygular ve ürünleri getirir
     *
     * @return array Ürünler, toplam ürün sayısı ve toplam sayfa sayısı
     */
    public function getProducts() {
        try {
            $args = $this->getArgs();

            $user_id = get_current_user_id();
            $cache_key = 'aipt_products_' . $user_id . '_' . md5(wp_json_encode($args) . ($this->price_sort_direction ?? ''));
            $cached_result = wp_cache_get($cache_key, 'aipt_products');

            if ($cached_result !== false) {
                return $cached_result;
            }

            if (isset($args['include']) && $args['include'] === [0]) {
                $empty_result = [
                    'products' => [],
                    'total' => 0,
                    'total_pages' => 0,
                    'message' => 'No products found matching the selected filters.'
                ];
                wp_cache_set($cache_key, $empty_result, 'aipt_products', 300);
                return $empty_result;
            }

            $results = \wc_get_products($args);
            $products = [];

            if ($results && isset($results->products)) {
                foreach ($results->products as $product_id) {
                    $product = \wc_get_product($product_id);

                    if (!$product) {
                        continue;
                    }

                    $categories = \wc_get_product_terms($product_id, 'product_cat', ['fields' => 'names']);
                    $categories = \is_wp_error($categories) ? [] : $categories;
                    $decoded_categories = array_map([self::class, 'decodeHtmlEntities'], $categories);

                    $tags = \wc_get_product_terms($product_id, 'product_tag', ['fields' => 'names']);
                    $tags = \is_wp_error($tags) ? [] : $tags;
                    $decoded_tags = array_map([self::class, 'decodeHtmlEntities'], $tags);

                    $has_description = !empty(trim($product->get_description()));
                    $has_short_description = !empty(trim($product->get_short_description()));

                    $image_id = $product->get_image_id();
                    $image_url = $image_id ? \wp_get_attachment_image_url($image_id, 'thumbnail') : \wc_placeholder_img_src('thumbnail');

                    $price = $product->get_price();
                    $numeric_price = $price !== '' ? (float) $price : 0;

                    $products[] = [
                        'id' => $product_id,
                        'name' => $product->get_name(),
                        'status' => $product->get_status(),
                        'stock_status' => $product->get_stock_status(),
                        'categories' => $decoded_categories,
                        'tags' => $decoded_tags,
                        'hasDescription' => $has_description,
                        'hasShortDescription' => $has_short_description,
                        'description' => $product->get_description(),
                        'shortDescription' => $product->get_short_description(),
                        'image' => $image_url,
                        'dateCreated' => $product->get_date_created() ? $product->get_date_created()->date('Y-m-d H:i:s') : '',
                        'dateModified' => $product->get_date_modified() ? $product->get_date_modified()->date('Y-m-d H:i:s') : '',
                        'price' => $price,
                        'numeric_price' => $numeric_price,
                        'sku' => $product->get_sku()
                    ];
                }
            }

            $total = isset($results->total) ? (int) $results->total : 0;
            $max_num_pages = isset($results->max_num_pages) ? (int) $results->max_num_pages : 0;

            $result = [
                'products' => $products,
                'total' => $total,
                'total_pages' => $max_num_pages
            ];

            wp_cache_set($cache_key, $result, 'aipt_products', 300);

            return $result;

        } catch (\Exception $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
            }

            return [
                'products' => [],
                'total' => 0,
                'total_pages' => 0,
                'error' => 'An error occurred while searching products. Please try again or contact support if the issue persists.'
            ];
        }
    }

    /**
     * HTML entities decode helper
     *
     * @param string $string
     * @return string
     */
    private static function decodeHtmlEntities($string) {
        return html_entity_decode($string, ENT_QUOTES, 'UTF-8');
    }
}
