<?php

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

class AI_eShop_Chat_Database_Manager {

    
    private static $instance = null;

    
    private $embeddings_table;

    
    private $chat_history_table;

    
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    
    private function __construct() {
        global $wpdb;
        $this->embeddings_table = $wpdb->prefix . 'aieo_chat_embeddings';
        $this->chat_history_table = $wpdb->prefix . 'aieo_chat_history';

        
        
        
        
        
        add_filter('aieo_chat_retention_days', array($this, 'filter_retention_days'));

        
        add_action('aieo_chat_cleanup_cron', array($this, 'cleanup_old_chats'));
        if (!wp_next_scheduled('aieo_chat_cleanup_cron')) {
            wp_schedule_event(time(), 'daily', 'aieo_chat_cleanup_cron');
        }
    }

    
    public static function ensure_chat_history_columns() {
        static $done = false;
        if ($done) { return; }
        $done = true;
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_chat_history';
        if ((string) $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            return; 
        }
        $wpdb->suppress_errors();
        $has = $wpdb->get_var($wpdb->prepare(
            "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = 'user_rating'",
            DB_NAME, $table
        ));
        if (empty($has)) {
            
            $wpdb->query("ALTER TABLE {$table} ADD COLUMN user_rating TINYINT UNSIGNED DEFAULT NULL COMMENT 'User feedback rating 0-100, only stored if changed from default 50'");
            $idx = $wpdb->get_var($wpdb->prepare(
                "SELECT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND INDEX_NAME = 'user_rating'",
                DB_NAME, $table
            ));
            if (empty($idx)) {
                
                $wpdb->query("ALTER TABLE {$table} ADD KEY user_rating (user_rating)");
            }
            if (defined('WP_DEBUG') && WP_DEBUG) { error_log('AIEO: added missing user_rating column to chat history table'); }
        }
        $wpdb->show_errors();
    }

    
    public function create_tables() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        
        $embeddings_exists = $wpdb->get_var("SHOW TABLES LIKE '{$this->embeddings_table}'") === $this->embeddings_table;

        if (!$embeddings_exists) {
            
            
            $embeddings_sql = "CREATE TABLE {$this->embeddings_table} (
                id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
                post_id bigint(20) UNSIGNED NOT NULL,
                post_type varchar(50) NOT NULL DEFAULT 'product',
                engine_type varchar(50) NOT NULL DEFAULT 'voyage-2',
                deduplicated tinyint(1) NOT NULL DEFAULT 0,
                embedding LONGTEXT NOT NULL,
                content_hash varchar(32) NOT NULL,
                failure_count int(11) DEFAULT 0,
                last_failure_reason varchar(500) DEFAULT NULL,
                last_failure_date datetime DEFAULT NULL,
                created_at datetime DEFAULT CURRENT_TIMESTAMP,
                updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                PRIMARY KEY (id),
                UNIQUE KEY post_id_type_engine_dedup (post_id, post_type, engine_type, deduplicated),
                KEY post_type (post_type),
                KEY engine_type (engine_type),
                KEY deduplicated (deduplicated),
                KEY created_at (created_at),
                KEY lookup_idx (post_id, engine_type, deduplicated),
                KEY stats_idx (engine_type, deduplicated, created_at),
                KEY failure_idx (failure_count, last_failure_date)
            ) $charset_collate;";

            
            $wpdb->query($embeddings_sql);
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO: Created embeddings table with full schema (fresh install)'); }
        } else {
            
            $this->add_performance_indexes();
            $this->add_failure_tracking_columns(true);
        }

        
        $history_exists = $wpdb->get_var("SHOW TABLES LIKE '{$this->chat_history_table}'") === $this->chat_history_table;

        if (!$history_exists) {
            
            $chat_history_sql = "CREATE TABLE {$this->chat_history_table} (
                id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
                conversation_id varchar(64) NOT NULL,
                session_id varchar(64) NOT NULL,
                user_id bigint(20) UNSIGNED DEFAULT NULL,
                user_message TEXT NOT NULL,
                assistant_message TEXT NOT NULL,
                model_used varchar(100) DEFAULT 'claude-3-5-sonnet-20241022',
                embedder_used varchar(50) DEFAULT NULL,
                products_found TEXT DEFAULT NULL,
                potential_suggestions_found TEXT DEFAULT NULL,
                user_rating TINYINT UNSIGNED DEFAULT NULL COMMENT 'User feedback rating 0-100, only stored if changed from default 50',
                created_at datetime DEFAULT CURRENT_TIMESTAMP,
                PRIMARY KEY (id),
                KEY conversation_id (conversation_id),
                KEY session_id (session_id),
                KEY user_id (user_id),
                KEY created_at (created_at),
                KEY user_rating (user_rating)
            ) $charset_collate;";

            
            $wpdb->query($chat_history_sql);
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO: Created chat history table (fresh install)'); }
        }
    }

    
    private function add_performance_indexes() {
        global $wpdb;

        
        static $indexes_checked = false;
        if ($indexes_checked) {
            return; 
        }

        
        
        
        
        $wpdb->suppress_errors();

        $lookup_exists = $wpdb->query(
            "SELECT 1 FROM {$this->embeddings_table} USE INDEX (lookup_idx) LIMIT 0"
        );

        if ($lookup_exists === false) {
            
            $wpdb->query("ALTER TABLE {$this->embeddings_table} ADD INDEX lookup_idx (post_id, engine_type, deduplicated)");
        }

        
        $stats_exists = $wpdb->query(
            "SELECT 1 FROM {$this->embeddings_table} USE INDEX (stats_idx) LIMIT 0"
        );

        if ($stats_exists === false) {
            
            $wpdb->query("ALTER TABLE {$this->embeddings_table} ADD INDEX stats_idx (engine_type, deduplicated, created_at)");
        }

        
        $wpdb->show_errors();

        
        $indexes_checked = true;
    }

    
    private function add_failure_tracking_columns($force_check = false) {
        global $wpdb;

        
        
        static $columns_checked = false;
        if ($columns_checked && !$force_check) {
            return;
        }

        
        $wpdb->suppress_errors();
        $column_exists = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
                WHERE TABLE_SCHEMA = %s
                AND TABLE_NAME = %s
                AND COLUMN_NAME = 'failure_count'",
                DB_NAME,
                $this->embeddings_table
            )
        );

        if (empty($column_exists)) {
            
            
            $wpdb->query("ALTER TABLE {$this->embeddings_table}
                ADD COLUMN failure_count int(11) DEFAULT 0");
            $wpdb->query("ALTER TABLE {$this->embeddings_table}
                ADD COLUMN last_failure_reason varchar(500) DEFAULT NULL");
            $wpdb->query("ALTER TABLE {$this->embeddings_table}
                ADD COLUMN last_failure_date datetime DEFAULT NULL");

            
            $index_exists = $wpdb->query(
                "SELECT 1 FROM {$this->embeddings_table} USE INDEX (failure_idx) LIMIT 0"
            );
            if ($index_exists === false) {
                $wpdb->query("ALTER TABLE {$this->embeddings_table}
                    ADD INDEX failure_idx (failure_count, last_failure_date)");
            }

            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO: Added failure tracking columns to embeddings table'); }
        }

        $wpdb->show_errors();
        $columns_checked = true;
    }

    
    public function store_embedding($post_id, $embedding, $content_hash, $post_type = 'product', $engine_type = null, $deduplicated = 0) {
        global $wpdb;

        
        $failure_reason = null;

        if (empty($embedding) || !is_array($embedding)) {
            $failure_reason = "Empty or invalid embedding data";
        } elseif (true) {
            
            $expected_dims = array(
                'text-embedding-ada-002' => 1536,
                'text-embedding-3-small' => 1536,
                'text-embedding-3-large' => 3072,
                'voyage-2' => 1024,
                'voyage-3' => 1024,
                'voyage-3.5' => 1024,
                'voyage-3.5-lite' => 1024,
                'voyage-large-2' => 1536,
            );

            $embedding_count = count($embedding);
            $expected = isset($expected_dims[$engine_type]) ? $expected_dims[$engine_type] : null;

            if ($expected !== null && $embedding_count !== $expected) {
                $failure_reason = "Wrong dimensions: expected $expected, got $embedding_count";
            } else {
                
                $non_zero_count = 0;
                foreach ($embedding as $value) {
                    if (abs($value) > 0.0001) {
                        $non_zero_count++;
                        if ($non_zero_count > 10) break; 
                    }
                }

                if ($non_zero_count < 10) {
                    $failure_reason = "Nearly all-zero embedding (API failure)";
                } else {
                    
                    if ($engine_type === null) {
                        $engine_type = get_option('aieo_chat_embedding_model', 'voyage-2');
                    }

                    
                    $compressed = $this->compress_embedding($embedding);

                    
                    if (strlen($compressed) < 100) {
                        $failure_reason = "Compressed size too small: " . strlen($compressed) . " bytes";
                    }
                }
            }
        }

        
        if ($failure_reason !== null) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log("AIEO: Rejected embedding for post_id=$post_id, type=$post_type, engine=$engine_type - Reason: $failure_reason"); }
            $this->log_embedding_failure($post_id, $post_type, $engine_type, $deduplicated, $failure_reason);
            return false;
        }

        
        if ($engine_type === null) {
            $engine_type = get_option('aieo_chat_embedding_model', 'voyage-2');
        }

        
        
        $cache_suspended = false;
        if (function_exists('wp_suspend_cache_addition')) {
            $cache_suspended = true;
            wp_suspend_cache_addition(true);
        }

        
        
        
        
        $result = $wpdb->query($wpdb->prepare(
            "INSERT INTO {$this->embeddings_table}
            (post_id, post_type, engine_type, deduplicated, embedding, content_hash, failure_count, last_failure_reason, last_failure_date, created_at, updated_at)
            VALUES (%d, %s, %s, %d, %s, %s, 0, NULL, NULL, %s, %s)
            ON DUPLICATE KEY UPDATE
                embedding = VALUES(embedding),
                content_hash = VALUES(content_hash),
                failure_count = 0,
                last_failure_reason = NULL,
                last_failure_date = NULL,
                updated_at = VALUES(updated_at)",
            $post_id,
            $post_type,
            $engine_type,
            $deduplicated,
            $compressed,
            $content_hash,
            current_time('mysql', true),
            current_time('mysql', true)
        ));

        
        if ($cache_suspended) {
            wp_suspend_cache_addition(false);
        }

        
        
        
        
        

        return $result !== false;
    }

    
    private function log_embedding_failure($post_id, $post_type, $engine_type, $deduplicated, $failure_reason) {
        global $wpdb;

        
        $wpdb->query($wpdb->prepare(
            "INSERT INTO {$this->embeddings_table}
            (post_id, post_type, engine_type, deduplicated, embedding, content_hash, failure_count, last_failure_reason, last_failure_date, created_at, updated_at)
            VALUES (%d, %s, %s, %d, '', '', 1, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                failure_count = failure_count + 1,
                last_failure_reason = VALUES(last_failure_reason),
                last_failure_date = VALUES(last_failure_date),
                updated_at = VALUES(updated_at)",
            $post_id,
            $post_type,
            $engine_type,
            $deduplicated,
            $failure_reason,
            current_time('mysql', true),
            current_time('mysql', true),
            current_time('mysql', true)
        ));
    }

    
    public function get_failed_embeddings($min_failures = 1) {
        global $wpdb;

        return $wpdb->get_results($wpdb->prepare(
            "SELECT post_id, post_type, engine_type, deduplicated, failure_count, last_failure_reason, last_failure_date
            FROM {$this->embeddings_table}
            WHERE failure_count >= %d
            ORDER BY failure_count DESC, last_failure_date DESC
            LIMIT 1000",
            $min_failures
        ));
    }

    
    public function get_failure_statistics($post_type, $engine_type, $deduplicated = 0) {
        global $wpdb;

        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT
                COUNT(*) as total_failed,
                SUM(failure_count) as total_attempts,
                MAX(failure_count) as max_failures,
                MIN(last_failure_date) as first_failure,
                MAX(last_failure_date) as latest_failure
            FROM {$this->embeddings_table}
            WHERE post_type = %s
            AND engine_type = %s
            AND deduplicated = %d
            AND failure_count > 0",
            $post_type,
            $engine_type,
            $deduplicated
        ), ARRAY_A);

        
        $reasons = $wpdb->get_results($wpdb->prepare(
            "SELECT last_failure_reason, COUNT(*) as count
            FROM {$this->embeddings_table}
            WHERE post_type = %s
            AND engine_type = %s
            AND deduplicated = %d
            AND failure_count > 0
            GROUP BY last_failure_reason
            ORDER BY count DESC
            LIMIT 10",
            $post_type,
            $engine_type,
            $deduplicated
        ), ARRAY_A);

        return array(
            'total_failed' => (int) $stats['total_failed'],
            'total_attempts' => (int) $stats['total_attempts'],
            'max_failures' => (int) $stats['max_failures'],
            'first_failure' => $stats['first_failure'],
            'latest_failure' => $stats['latest_failure'],
            'failure_reasons' => $reasons
        );
    }

    
    private function compress_embedding($embedding) {
        
        $packed = '';
        foreach ($embedding as $value) {
            
            $packed .= pack('f', $value);
        }

        
        $compressed = gzcompress($packed, 9);

        
        return base64_encode($compressed);
    }

    
    public function decompress_embedding($compressed) {
        
        $decoded = base64_decode($compressed);

        
        $decompressed = gzuncompress($decoded);

        
        $unpacked = unpack('f*', $decompressed);

        return array_values($unpacked);
    }

    
    public function get_embedding($post_id) {
        global $wpdb;

        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT embedding, content_hash FROM {$this->embeddings_table} WHERE post_id = %d",
            $post_id
        ));

        if (!$row) {
            return null;
        }

        return array(
            'embedding' => $this->decompress_embedding($row->embedding),
            'content_hash' => $row->content_hash
        );
    }

    
    public function get_embeddings_for_search($post_types = array('product'), $post_ids = null, $engine_type = null) {
        global $wpdb;

        
        if (empty($post_types) || !is_array($post_types)) {
            $post_types = array('product');
        }

        
        if ($engine_type === null) {
            $engine_type = get_option('aieo_chat_embedding_model', 'voyage-2');
        }

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

        if ($post_ids && is_array($post_ids)) {
            $id_placeholders = implode(',', array_fill(0, count($post_ids), '%d'));
            $sql = $wpdb->prepare(
                "SELECT post_id, embedding FROM {$this->embeddings_table}
                WHERE post_type IN ($placeholders)
                AND post_id IN ($id_placeholders)
                AND (engine_type = %s OR engine_type IS NULL)",
                array_merge($post_types, $post_ids, array($engine_type))
            );
        } else {
            $sql = $wpdb->prepare(
                "SELECT post_id, embedding FROM {$this->embeddings_table}
                WHERE post_type IN ($placeholders)
                AND (engine_type = %s OR engine_type IS NULL)",
                array_merge($post_types, array($engine_type))
            );
        }

        
        $rows = $wpdb->get_results($sql);

        $embeddings = array();
        foreach ($rows as $row) {
            $embeddings[$row->post_id] = $this->decompress_embedding($row->embedding);
        }

        return $embeddings;
    }

    
    public function delete_embedding($post_id) {
        global $wpdb;

        $result = $wpdb->delete(
            $this->embeddings_table,
            array('post_id' => $post_id),
            array('%d')
        );

        return $result !== false;
    }

    
    public function delete_embeddings_by_type($post_type) {
        global $wpdb;

        return $wpdb->delete(
            $this->embeddings_table,
            array('post_type' => $post_type),
            array('%s')
        );
    }

    
    public function clear_all_embeddings() {
        global $wpdb;
        return $wpdb->query("TRUNCATE TABLE {$this->embeddings_table}") !== false;
    }

    
    public function get_stats() {
        global $wpdb;

        $stats = array(
            'total' => 0,
            'by_type' => array(),
            'recent_24h' => 0
        );

        
        $stats['total'] = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$this->embeddings_table}");

        
        
        $type_counts = $wpdb->get_results(
            "SELECT post_type, COUNT(*) as count FROM {$this->embeddings_table} GROUP BY post_type"
        );
        foreach ($type_counts as $row) {
            $stats['by_type'][$row->post_type] = (int) $row->count;
        }

        
        $stats['recent_24h'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->embeddings_table} WHERE created_at > %s",
            gmdate('Y-m-d H:i:s', strtotime('-24 hours'))
        ));

        return $stats;
    }

    
    public function save_chat_message($conversation_id, $user_message, $assistant_message, $model = 'claude-3-5-sonnet-20241022', $products = null, $embedder = null, $potential_suggestions = null) {
        global $wpdb;

        
        
        if (get_option('aieo_chat_history_enabled', '1') !== '1') {
            return false;
        }

        
        
        self::ensure_chat_history_columns();

        $session_id = $this->get_session_id();
        $user_id = get_current_user_id();

        
        $data = array(
            'conversation_id' => $conversation_id,
            'session_id' => $session_id,
            'user_message' => $user_message,
            'assistant_message' => $assistant_message,
            'model_used' => $model,
            'embedder_used' => $embedder,
            'products_found' => $products ? json_encode($products, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
            'potential_suggestions_found' => $potential_suggestions ? json_encode($potential_suggestions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
            'created_at' => current_time('mysql', true)
        );

        $format = array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');

        
        if ($user_id > 0) {
            $data['user_id'] = $user_id;
            $format[] = '%d';
        }

        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Saving chat history to: ' . $this->chat_history_table); }
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO History enabled option: ' . get_option('aieo_chat_history_enabled', '1')); }

        
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$this->chat_history_table}'") === $this->chat_history_table;
        if (!$table_exists) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Chat history table does NOT exist! Creating it now...'); }
            $this->create_tables();
        }

        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Data being saved: ' . print_r($data, true)); }

        $result = $wpdb->insert(
            $this->chat_history_table,
            $data,
            $format
        );

        if ($result === false) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Chat history save FAILED. Error: ' . $wpdb->last_error); }
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Last query: ' . $wpdb->last_query); }
            return false;
        } else {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log('AIEO Chat history saved successfully. Insert ID: ' . $wpdb->insert_id); }
            return $wpdb->insert_id;
        }
    }

    
    private function get_session_id() {
        
        $session_tracking_enabled = get_option('aieo_session_tracking_enabled', '1') === '1';

        
        if ($session_tracking_enabled && isset($_COOKIE['aieo_session_id'])) {
            return sanitize_text_field(wp_unslash( $_COOKIE['aieo_session_id'] ));
        }

        
        if (session_id() === '') {
            session_start();
        }
        if (!isset($_SESSION['aieo_chat_session_id'])) {
            $_SESSION['aieo_chat_session_id'] = wp_generate_uuid4();
        }
        return sanitize_text_field($_SESSION['aieo_chat_session_id']);
    }

    
    public function get_chat_stats() {
        global $wpdb;

        $stats = array(
            'total_conversations' => 0,
            'total_messages' => 0,
            'avg_per_conversation' => 0,
            'last_30_days' => array(
                'conversations' => 0,
                'messages' => 0
            )
        );

        
        $stats['total_conversations'] = (int) $wpdb->get_var(
            "SELECT COUNT(DISTINCT conversation_id) FROM {$this->chat_history_table}"
        );

        
        $stats['total_messages'] = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->chat_history_table}"
        );

        
        if ($stats['total_conversations'] > 0) {
            $stats['avg_per_conversation'] = round($stats['total_messages'] / $stats['total_conversations'], 1);
        }

        
        $date_30_days_ago = gmdate('Y-m-d H:i:s', strtotime('-30 days'));

        $stats['last_30_days']['conversations'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT conversation_id) FROM {$this->chat_history_table} WHERE created_at > %s",
            $date_30_days_ago
        ));

        $stats['last_30_days']['messages'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->chat_history_table} WHERE created_at > %s",
            $date_30_days_ago
        ));

        return $stats;
    }

    
    public function get_recent_conversations($limit = 10) {
        global $wpdb;

        
        $conversations = $wpdb->get_results($wpdb->prepare(
            "SELECT
                conversation_id,
                user_id,
                COUNT(*) as message_count,
                MIN(created_at) as started_at,
                MAX(created_at) as last_message_at
            FROM {$this->chat_history_table}
            GROUP BY conversation_id, user_id
            ORDER BY last_message_at DESC
            LIMIT %d",
            $limit
        ));

        return $conversations;
    }

    
    
    public function filter_retention_days($days) {
        $opt = (int) get_option('aieo_chat_retention_days', 0);
        return $opt > 0 ? max(1, $opt) : $days;
    }

    public function cleanup_old_chats() {
        global $wpdb;

        $retention_days = apply_filters('aieo_chat_retention_days', 30);
        $cutoff_date = gmdate('Y-m-d H:i:s', strtotime("-{$retention_days} days"));

        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->chat_history_table} WHERE created_at < %s",
            $cutoff_date
        ));
    }

    
    public function get_posts_without_embeddings($post_type = 'product', $limit = 100, $engine_type = null, $deduplicated = 0) {
        global $wpdb;

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if ($post_type === 'product') {
            $vitals_helper = AI_eShop_Chat_Product_Vitals_Helper::get_instance();

            if ($vitals_helper->is_vitals_table_available()) {
                $prioritize_sales = get_option('aieo_chat_prioritize_by_sales', '0') === '1';

                $all_product_ids = $vitals_helper->get_sellable_unit_ids(array(
                    'prioritize_sales' => $prioritize_sales,
                    'in_stock_only'    => true,
                ));

                
                $total_units_in_vitals = $vitals_helper->count_sellable_units(array(
                    'in_stock_only' => true,
                ));
                $vitals_has_most_products = !empty($all_product_ids) && $total_units_in_vitals > 0;

                if ($vitals_has_most_products) {
                    
                    $post_ids_without_embeddings = array();

                    foreach ($all_product_ids as $product_id) {
                        
                        if ($engine_type !== null) {
                            $has_embedding = $wpdb->get_var($wpdb->prepare(
                                "SELECT COUNT(*) FROM {$this->embeddings_table}
                                 WHERE post_id = %d AND engine_type = %s AND deduplicated = %d",
                                $product_id,
                                $engine_type,
                                $deduplicated
                            ));
                        } else {
                            
                            $has_embedding = $wpdb->get_var($wpdb->prepare(
                                "SELECT COUNT(*) FROM {$this->embeddings_table} WHERE post_id = %d",
                                $product_id
                            ));
                        }

                        if (!$has_embedding) {
                            $post_ids_without_embeddings[] = $product_id;

                            if (count($post_ids_without_embeddings) >= $limit) {
                                break;
                            }
                        }
                    }

                    return $post_ids_without_embeddings;
                }
            }
        }

        
        
        
        
        
        
        $sellable_predicate = '';
        $type_clause = 'p.post_type = %s';
        $type_args = array($post_type);

        if ($post_type === 'product') {
            $type_clause = "p.post_type IN ('product', 'product_variation')";
            $type_args   = array();
            
            $sellable_predicate = "AND (
                p.post_type = 'product_variation'
                OR (p.post_type = 'product' AND NOT EXISTS (
                    SELECT 1 FROM {$wpdb->posts} c
                    WHERE c.post_parent = p.ID
                      AND c.post_type = 'product_variation'
                      AND c.post_status = 'publish'
                ))
            )";
        }

        if ($engine_type !== null) {
            
            $sql = $wpdb->prepare(
                "SELECT DISTINCT p.ID
                 FROM {$wpdb->posts} p
                 LEFT JOIN {$this->embeddings_table} e
                     ON p.ID = e.post_id
                     AND e.engine_type = %s
                     AND e.deduplicated = %d
                 WHERE {$type_clause}
                 AND p.post_status = 'publish'
                 {$sellable_predicate}
                 AND e.id IS NULL
                 LIMIT %d",
                array_merge(array($engine_type, $deduplicated), $type_args, array($limit))
            );
        } else {
            
            $sql = $wpdb->prepare(
                "SELECT DISTINCT p.ID
                 FROM {$wpdb->posts} p
                 LEFT JOIN {$this->embeddings_table} e ON p.ID = e.post_id
                 WHERE {$type_clause}
                 AND p.post_status = 'publish'
                 {$sellable_predicate}
                 AND e.id IS NULL
                 LIMIT %d",
                array_merge($type_args, array($limit))
            );
        }

        
        $results = $wpdb->get_col($sql);

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

    
    public function count_posts_without_embeddings($post_type = 'product', $engine_type = null, $deduplicated = 0) {
        global $wpdb;

        
        
        
        if ($post_type === 'product') {
            $vitals_helper = AI_eShop_Chat_Product_Vitals_Helper::get_instance();

            if ($vitals_helper->is_vitals_table_available()) {
                $vitals_table = $wpdb->prefix . 'aieo_core_product_vitals';

                
                
                
                
                if ($engine_type !== null) {
                    
                    return (int) $wpdb->get_var($wpdb->prepare(
                        "SELECT COUNT(DISTINCT v.product_id)
                         FROM {$vitals_table} v
                         INNER JOIN {$wpdb->posts} p ON v.product_id = p.ID
                         LEFT JOIN (
                             SELECT DISTINCT parent_id
                             FROM {$vitals_table}
                             WHERE product_id <> parent_id
                         ) vp ON vp.parent_id = v.product_id
                         LEFT JOIN {$this->embeddings_table} e
                             ON e.post_id = v.product_id
                             AND e.engine_type = %s
                             AND e.deduplicated = %d
                         WHERE p.post_status = 'publish'
                           AND p.post_type IN ('product', 'product_variation')
                           AND v.stock > 0
                           AND (v.product_id <> v.parent_id OR vp.parent_id IS NULL)
                           AND e.id IS NULL",
                        $engine_type,
                        $deduplicated
                    ));
                }

                
                
                return (int) $wpdb->get_var(
                    "SELECT COUNT(DISTINCT v.product_id)
                     FROM {$vitals_table} v
                     INNER JOIN {$wpdb->posts} p ON v.product_id = p.ID
                     LEFT JOIN (
                         SELECT DISTINCT parent_id
                         FROM {$vitals_table}
                         WHERE product_id <> parent_id
                     ) vp ON vp.parent_id = v.product_id
                     LEFT JOIN {$this->embeddings_table} e ON e.post_id = v.product_id
                     WHERE p.post_status = 'publish'
                       AND p.post_type IN ('product', 'product_variation')
                       AND v.stock > 0
                       AND (v.product_id <> v.parent_id OR vp.parent_id IS NULL)
                       AND e.id IS NULL"
                );
            }
        }

        
        if ($engine_type !== null) {
            $sql = $wpdb->prepare(
                "SELECT COUNT(p.ID)
                 FROM {$wpdb->posts} p
                 LEFT JOIN {$this->embeddings_table} e
                     ON p.ID = e.post_id
                     AND e.post_type = %s
                     AND e.engine_type = %s
                     AND e.deduplicated = %d
                 WHERE p.post_type = %s
                 AND p.post_status = 'publish'
                 AND e.id IS NULL",
                $post_type,
                $engine_type,
                $deduplicated,
                $post_type
            );
        } else {
            $sql = $wpdb->prepare(
                "SELECT COUNT(p.ID)
                 FROM {$wpdb->posts} p
                 LEFT JOIN {$this->embeddings_table} e ON p.ID = e.post_id AND e.post_type = %s
                 WHERE p.post_type = %s
                 AND p.post_status = 'publish'
                 AND e.id IS NULL",
                $post_type,
                $post_type
            );
        }

        
        return (int) $wpdb->get_var($sql);
    }

    
    public function get_terms_without_embeddings($taxonomy, $limit = 100, $engine_type = null, $deduplicated = 0) {
        global $wpdb;

        $post_type_key = 'taxonomy:' . $taxonomy;

        if ($engine_type !== null) {
            $sql = $wpdb->prepare(
                "SELECT DISTINCT t.term_id
                FROM {$wpdb->terms} t
                INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
                LEFT JOIN {$this->embeddings_table} e
                    ON t.term_id = e.post_id
                    AND e.post_type = %s
                    AND e.engine_type = %s
                    AND e.deduplicated = %d
                WHERE tt.taxonomy = %s
                AND tt.count > 0
                AND e.id IS NULL
                LIMIT %d",
                $post_type_key,
                $engine_type,
                $deduplicated,
                $taxonomy,
                $limit
            );
        } else {
            $sql = $wpdb->prepare(
                "SELECT DISTINCT t.term_id
                FROM {$wpdb->terms} t
                INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
                LEFT JOIN {$this->embeddings_table} e ON t.term_id = e.post_id AND e.post_type = %s
                WHERE tt.taxonomy = %s
                AND tt.count > 0
                AND e.id IS NULL
                LIMIT %d",
                $post_type_key,
                $taxonomy,
                $limit
            );
        }

        
        $results = $wpdb->get_col($sql);

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