<?php
/**
 * Embedding Manager
 * Handles embedding generation and storage for RAG
 *
 * @package AskAny
 */

namespace Askany;

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

class Embedding_Manager {
	private $database;
	private $api_manager;
	
	public function __construct( $database, $api_manager ) {
		$this->database = $database;
		$this->api_manager = $api_manager;
	}
	
	/**
	 * Generate embedding for text using available provider (respects active provider setting)
	 */
	public function generate_embedding( $text, $model = null ) {
		// Get the active provider from settings
		$active_provider = get_option('askany_api_provider', 'deepseek');
		
		// Get custom embedding model if set
		$custom_model = get_option('askany_embedding_model', '');
		
		// Check which embedding providers are available
		$openai_key = $this->api_manager->get_openai_api_key();
		$gemini_key = $this->api_manager->get_gemini_api_key();
		
		// Use active provider if it supports embeddings
		if ( $active_provider === 'gemini' && ! empty( $gemini_key ) ) {
			$default_model = 'gemini-embedding-001'; // Updated Feb 2026
			$selected_model = $model ?? ( $custom_model ?: $default_model );
			return $this->generate_gemini_embedding( $text, $selected_model );
		} elseif ( $active_provider === 'openai' && ! empty( $openai_key ) ) {
			$default_model = 'text-embedding-3-small';
			$selected_model = $model ?? ( $custom_model ?: $default_model );
			return $this->generate_openai_embedding( $text, $selected_model );
		}
		
		// Fallback: Use any available embedding provider
		if ( ! empty( $openai_key ) ) {
			$default_model = 'text-embedding-3-small';
			$selected_model = $model ?? ( $custom_model ?: $default_model );
			return $this->generate_openai_embedding( $text, $selected_model );
		} elseif ( ! empty( $gemini_key ) ) {
			$default_model = 'gemini-embedding-001'; // Updated Feb 2026
			$selected_model = $model ?? ( $custom_model ?: $default_model );
			return $this->generate_gemini_embedding( $text, $selected_model );
		}
		
		return new \WP_Error( 'no_api_key', 'No embedding provider API key configured (OpenAI or Gemini required)' );
	}
	
	/**
	 * Generate embedding using OpenAI
	 */
	private function generate_openai_embedding( $text, $model = 'text-embedding-3-small' ) {
		$api_key = $this->api_manager->get_openai_api_key();
		
		if ( empty( $api_key ) ) {
			return new \WP_Error( 'no_api_key', 'OpenAI API key not configured' );
		}
		
		// Clean and prepare text
		$text = $this->prepare_text_for_embedding( $text );
		
		if ( empty( $text ) ) {
			return new \WP_Error( 'empty_text', 'Text is empty after cleaning' );
		}
		
		// Call OpenAI Embeddings API
		$response = wp_remote_post( 'https://api.openai.com/v1/embeddings', array(
			'headers' => array(
				'Authorization' => 'Bearer ' . $api_key,
				'Content-Type' => 'application/json',
			),
			'body' => json_encode( array(
				'input' => $text,
				'model' => $model,
			) ),
			'timeout' => 60, // Increased timeout for large content
		) );
		
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		
		if ( isset( $body['error'] ) ) {
			return new \WP_Error( 'api_error', $body['error']['message'] );
		}
		
		if ( ! isset( $body['data'][0]['embedding'] ) ) {
			return new \WP_Error( 'invalid_response', 'Invalid API response' );
		}
		
		return array(
			'embedding' => $body['data'][0]['embedding'],
			'model' => $model,
			'provider' => 'openai',
			'dimensions' => count( $body['data'][0]['embedding'] ),
		);
	}
	
	/**
	 * Generate embedding using Gemini
	 */
	private function generate_gemini_embedding( $text, $model = 'gemini-embedding-001' ) {
		$api_key = $this->api_manager->get_gemini_api_key();
		
		if ( empty( $api_key ) ) {
			return new \WP_Error( 'no_api_key', 'Gemini API key not configured' );
		}
		
		// Clean and prepare text
		$text = $this->prepare_text_for_embedding( $text );
		
		if ( empty( $text ) ) {
			return new \WP_Error( 'empty_text', 'Text is empty after cleaning' );
		}
		
		// Use the model selected by user (defaults to gemini-embedding-001)
		// Note: embedding-001 and text-embedding-004 were deprecated in Feb 2026
		$actual_model = $model;
		
		// Use v1beta API endpoint (confirmed working as of Feb 2026)
		$url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $actual_model . ':embedContent?key=' . $api_key;
		
		$request_body = array(
			'model' => 'models/' . $actual_model,
			'content' => array(
				'parts' => array(
					array( 'text' => $text )
				)
			),
			'taskType' => 'RETRIEVAL_DOCUMENT', // For document embeddings
		);
		
		$response = wp_remote_post( $url, array(
			'headers' => array(
				'Content-Type' => 'application/json',
			),
			'body' => json_encode( $request_body ),
			'timeout' => 60, // Increased timeout for large content
		) );
		
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		
		if ( isset( $body['error'] ) ) {
			$error_message = isset( $body['error']['message'] ) ? $body['error']['message'] : 'Unknown Gemini API error';
			
			// Don't log quota errors since we show them in the UI
			$is_quota_error = stripos($error_message, 'quota') !== false || 
			                  stripos($error_message, 'rate limit') !== false;
			
			if (!$is_quota_error) {
				// ( 'AskAny RAG: Failed to generate Gemini embedding - ' . $error_message );
			}
			
			return new \WP_Error( 'api_error', $error_message );
		}
		
		if ( ! isset( $body['embedding']['values'] ) ) {
			// ( 'AskAny RAG: Invalid Gemini API response - ' . wp_json_encode( $body ) );
			return new \WP_Error( 'invalid_response', 'Invalid API response from Gemini' );
		}
		
		return array(
			'embedding' => $body['embedding']['values'],
			'model' => $actual_model, // Return the actual model name used
			'provider' => 'gemini',
			'dimensions' => count( $body['embedding']['values'] ),
		);
	}
	
	/**
	 * Store embedding in database
	 */
	public function store_embedding( $content_id, $content_type, $embedding_data, $metadata = array() ) {
		global $wpdb;
		$table = $wpdb->prefix . 'askany_embeddings';
		
		$vector_id = isset( $metadata['vector_id'] ) ? $metadata['vector_id'] : $this->generate_vector_id( $content_id, $content_type );
		
		$embedding_json = json_encode( $embedding_data['embedding'] );
		$embedding_model = $embedding_data['model'];
		$dimensions = $embedding_data['dimensions'];
		$chunk_index = isset( $metadata['chunk_index'] ) ? $metadata['chunk_index'] : 0;
		$chunk_hash = isset( $metadata['chunk_hash'] ) ? $metadata['chunk_hash'] : null;
		$total_chunks = isset( $metadata['total_chunks'] ) ? $metadata['total_chunks'] : 1;
		$source_url = isset( $metadata['source_url'] ) ? $metadata['source_url'] : '';
		$title = isset( $metadata['title'] ) ? $metadata['title'] : '';
		$excerpt = isset( $metadata['excerpt'] ) ? $metadata['excerpt'] : '';
		$created_at = current_time( 'mysql' );
		$updated_at = current_time( 'mysql' );
		
		// Use INSERT ... ON DUPLICATE KEY UPDATE to handle re-indexing gracefully
		// This preserves hash cache optimization while preventing duplicate errors
		$result = $wpdb->query(
			$wpdb->prepare(
				"INSERT INTO {$table} 
				(vector_id, content_id, content_type, embedding, embedding_model, dimensions, 
				 chunk_index, chunk_hash, total_chunks, source_url, title, excerpt, created_at, updated_at)
				VALUES (%s, %d, %s, %s, %s, %d, %d, %s, %d, %s, %s, %s, %s, %s)
				ON DUPLICATE KEY UPDATE
					embedding = VALUES(embedding),
					embedding_model = VALUES(embedding_model),
					dimensions = VALUES(dimensions),
					chunk_hash = VALUES(chunk_hash),
					excerpt = VALUES(excerpt),
					updated_at = VALUES(updated_at)",
				$vector_id,
				$content_id,
				$content_type,
				$embedding_json,
				$embedding_model,
				$dimensions,
				$chunk_index,
				$chunk_hash,
				$total_chunks,
				$source_url,
				$title,
				$excerpt,
				$created_at,
				$updated_at
			)
		);
		
		if ( $result === false ) {
			return new \WP_Error( 'db_error', $wpdb->last_error );
		}
		
		// Update content_index status
		$this->update_content_embedding_status( $content_id, 'completed' );
		
		// Get the ID (either inserted or existing)
		$embedding_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$table} WHERE vector_id = %s",
				$vector_id
			)
		);
		
		return $embedding_id ? $embedding_id : $wpdb->insert_id;
	}
	
	/**
	 * Generate embeddings for content with chunking
	 */
	public function generate_content_embeddings( $content_id, $content_type, $text, $metadata = array(), $retry_count = 0 ) {
		// Check if content needs chunking
		$chunker = new Chunker();
		
		if ( $chunker->should_chunk( $text ) ) {
			return $this->generate_chunked_embeddings( $content_id, $content_type, $text, $metadata, $retry_count );
		}
		
		// Generate single embedding with retry logic
		$embedding_data = $this->generate_embedding( $text );
		
		if ( is_wp_error( $embedding_data ) ) {
			$error_message = $embedding_data->get_error_message();
			
			// Retry on timeout errors (max 2 retries)
			if ( $retry_count < 2 && ( 
				strpos( $error_message, 'timeout' ) !== false || 
				strpos( $error_message, 'SSL' ) !== false ||
				strpos( $error_message, 'cURL error 28' ) !== false
			) ) {
				// Exponential backoff: wait 2^retry_count seconds
				sleep( pow( 2, $retry_count ) );
				// ( "AskAny RAG: Retrying embedding generation for content ID {$content_id} (attempt " . ($retry_count + 2) . "/3)" );
				return $this->generate_content_embeddings( $content_id, $content_type, $text, $metadata, $retry_count + 1 );
			}
			
			$this->update_content_embedding_status( $content_id, 'failed', $error_message );
			return $embedding_data;
		}
		
		return $this->store_embedding( $content_id, $content_type, $embedding_data, $metadata );
	}
	
	/**
	 * Generate embeddings for chunked content
	 */
	private function generate_chunked_embeddings( $content_id, $content_type, $text, $metadata = array(), $retry_count = 0 ) {
		$chunker = new Chunker();
		$chunks = $chunker->chunk_text( $text );
		$total_chunks = count( $chunks );
		$base_hash = md5( ( isset( $metadata['source_url'] ) ? $metadata['source_url'] : $content_id ) );
		
		$embedding_ids = array();
		
		foreach ( $chunks as $index => $chunk ) {
			$chunk_metadata = array_merge( $metadata, array(
				'vector_id' => "{$base_hash}_chunk_{$index}",
				'chunk_index' => $index,
				'total_chunks' => $total_chunks,
				'excerpt' => substr( $chunk, 0, 500 ),
			) );
			
			$embedding_data = $this->generate_embedding( $chunk );
			
			if ( is_wp_error( $embedding_data ) ) {
				$error_message = $embedding_data->get_error_message();
				
				// Retry on timeout errors (max 2 retries)
				if ( $retry_count < 2 && ( 
					strpos( $error_message, 'timeout' ) !== false || 
					strpos( $error_message, 'SSL' ) !== false ||
					strpos( $error_message, 'cURL error 28' ) !== false
				) ) {
					// Exponential backoff: wait 2^retry_count seconds
					sleep( pow( 2, $retry_count ) );
					// ( "AskAny RAG: Retrying chunk {$index} for content ID {$content_id} (attempt " . ($retry_count + 2) . "/3)" );
					return $this->generate_chunked_embeddings( $content_id, $content_type, $text, $metadata, $retry_count + 1 );
				}
				
				$this->update_content_embedding_status( $content_id, 'failed', $error_message );
				return $embedding_data;
			}
			
			$embedding_id = $this->store_embedding( $content_id, $content_type, $embedding_data, $chunk_metadata );
			
			if ( is_wp_error( $embedding_id ) ) {
				return $embedding_id;
			}
			
			$embedding_ids[] = $embedding_id;
			
			// Rate limiting: 3000 RPM for tier 1
			usleep( 20000 ); // 20ms delay = 50 requests/second max
		}
		
		return $embedding_ids;
	}
	
	/**
	 * Update content embedding status
	 */
	private function update_content_embedding_status( $content_id, $status, $error = '' ) {
		global $wpdb;
		$table = $wpdb->prefix . 'askany_content_index';
		
		$wpdb->update(
			$table,
			array(
				'has_embedding' => ( $status === 'completed' ) ? 1 : 0,
				'embedding_status' => $status,
				'embedding_error' => $error,
			),
			array( 'id' => $content_id )
		);
	}
	
	/**
	 * Generate unique vector ID
	 */
	private function generate_vector_id( $content_id, $content_type ) {
		return md5( $content_type . '_' . $content_id . '_' . time() );
	}
	
	/**
	 * Prepare text for embedding
	 */
	private function prepare_text_for_embedding( $text ) {
		// Remove HTML tags
		$text = wp_strip_all_tags( $text );
		
		// Remove extra whitespace
		$text = preg_replace( '/\s+/', ' ', $text );
		
		// Trim
		$text = trim( $text );
		
		// Limit to 8191 tokens (roughly 32K characters for safety)
		if ( strlen( $text ) > 32000 ) {
			$text = substr( $text, 0, 32000 );
		}
		
		return $text;
	}
	
	/**
	 * Delete embeddings for content
	 */
	public function delete_embeddings( $content_id ) {
		global $wpdb;
		$table = $wpdb->prefix . 'askany_embeddings';
		
		return $wpdb->delete( $table, array( 'content_id' => $content_id ) );
	}
	
	/**
	 * Get embedding statistics
	 */
	public function get_embedding_stats() {
		global $wpdb;
		$table = $wpdb->prefix . 'askany_embeddings';
		
		$stats = array(
			'total' => $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ),
			'by_type' => $wpdb->get_results( "
				SELECT content_type, COUNT(*) as count 
				FROM {$table} 
				GROUP BY content_type
			", ARRAY_A ),
			'chunked' => $wpdb->get_var( "SELECT COUNT(DISTINCT content_id) FROM {$table} WHERE total_chunks > 1" ),
		);
		
		return $stats;
	}
	
	/**
	 * Generate content embeddings with optimization (hash caching + batch API)
	 * Only used when rag_optimization_enabled is true
	 */
	public function generate_content_embeddings_optimized( $content_id, $content_type, $text, $metadata = array(), $retry_count = 0 ) {
		// Check if content needs chunking
		$chunker = new Chunker();
		
		if ( $chunker->should_chunk( $text ) ) {
			return $this->generate_chunked_embeddings_optimized( $content_id, $content_type, $text, $metadata, $retry_count );
		}
		
		// For single chunk, check hash cache
		$text_hash = md5( $text );
		$cached_embedding = $this->get_cached_embedding_by_hash( $text_hash );
		
		if ( $cached_embedding ) {
			// Reuse existing embedding
			return $this->store_embedding( $content_id, $content_type, $cached_embedding, array_merge( $metadata, array( 'chunk_hash' => $text_hash ) ) );
		}
		
		// Generate new embedding
		$embedding_data = $this->generate_embedding( $text );
		
		if ( is_wp_error( $embedding_data ) ) {
			$error_message = $embedding_data->get_error_message();
			
			// Retry on timeout errors (max 2 retries)
			if ( $retry_count < 2 && ( 
				strpos( $error_message, 'timeout' ) !== false || 
				strpos( $error_message, 'SSL' ) !== false ||
				strpos( $error_message, 'cURL error 28' ) !== false
			) ) {
				sleep( pow( 2, $retry_count ) );
				return $this->generate_content_embeddings_optimized( $content_id, $content_type, $text, $metadata, $retry_count + 1 );
			}
			
			$this->update_content_embedding_status( $content_id, 'failed', $error_message );
			return $embedding_data;
		}
		
		return $this->store_embedding( $content_id, $content_type, $embedding_data, array_merge( $metadata, array( 'chunk_hash' => $text_hash ) ) );
	}
	
	/**
	 * Generate chunked embeddings with optimization (hash caching + batch API)
	 */
	private function generate_chunked_embeddings_optimized( $content_id, $content_type, $text, $metadata = array(), $retry_count = 0 ) {
		$chunker = new Chunker();
		$chunks = $chunker->chunk_text( $text );
		$total_chunks = count( $chunks );
		$base_hash = md5( ( isset( $metadata['source_url'] ) ? $metadata['source_url'] : $content_id ) );
		
		// Step 1: Check which chunks need embedding (hash cache check)
		$chunks_to_process = array();
		$cached_embeddings = array();
		
		foreach ( $chunks as $index => $chunk ) {
			$chunk_hash = md5( $chunk );
			$cached_embedding = $this->get_cached_embedding_by_hash( $chunk_hash );
			
			if ( $cached_embedding ) {
				// Store cached embedding info for later
				$cached_embeddings[$index] = array(
					'embedding_data' => $cached_embedding,
					'chunk_hash' => $chunk_hash,
					'chunk' => $chunk,
				);
			} else {
				// Need to generate embedding for this chunk
				$chunks_to_process[$index] = array(
					'text' => $chunk,
					'hash' => $chunk_hash,
				);
			}
		}
		
		$embedding_ids = array();
		
		// Step 2: Process cached embeddings first (instant, no API calls)
		foreach ( $cached_embeddings as $index => $cached_data ) {
			$chunk_metadata = array_merge( $metadata, array(
				'vector_id' => "{$base_hash}_chunk_{$index}",
				'chunk_index' => $index,
				'total_chunks' => $total_chunks,
				'excerpt' => substr( $cached_data['chunk'], 0, 500 ),
				'chunk_hash' => $cached_data['chunk_hash'],
			) );
			
			$embedding_id = $this->store_embedding( $content_id, $content_type, $cached_data['embedding_data'], $chunk_metadata );
			
			if ( ! is_wp_error( $embedding_id ) ) {
				$embedding_ids[] = $embedding_id;
			}
		}
		
		// Step 3: Process new chunks using batch API
		if ( ! empty( $chunks_to_process ) ) {
			$batch_embeddings = $this->generate_batch_embeddings( array_column( $chunks_to_process, 'text' ), $retry_count );
			
			if ( is_wp_error( $batch_embeddings ) ) {
				$this->update_content_embedding_status( $content_id, 'failed', $batch_embeddings->get_error_message() );
				return $batch_embeddings;
			}
			
			// Store batch embeddings
			$batch_index = 0;
			foreach ( $chunks_to_process as $index => $chunk_data ) {
				if ( isset( $batch_embeddings[$batch_index] ) ) {
					$chunk_metadata = array_merge( $metadata, array(
						'vector_id' => "{$base_hash}_chunk_{$index}",
						'chunk_index' => $index,
						'total_chunks' => $total_chunks,
						'excerpt' => substr( $chunk_data['text'], 0, 500 ),
						'chunk_hash' => $chunk_data['hash'],
					) );
					
					$embedding_id = $this->store_embedding( $content_id, $content_type, $batch_embeddings[$batch_index], $chunk_metadata );
					
					if ( ! is_wp_error( $embedding_id ) ) {
						$embedding_ids[] = $embedding_id;
					}
					
					$batch_index++;
				}
			}
		}
		
		return $embedding_ids;
	}
	
	/**
	 * Get cached embedding by content hash
	 */
	private function get_cached_embedding_by_hash( $chunk_hash ) {
		global $wpdb;
		$tables = $this->database->get_tables();
		$embeddings_table = $tables['embeddings'];
		
		$cached = $wpdb->get_row( $wpdb->prepare(
			"SELECT embedding, embedding_model, dimensions FROM {$embeddings_table} WHERE chunk_hash = %s LIMIT 1",
			$chunk_hash
		), ARRAY_A );
		
		if ( $cached ) {
			return array(
				'embedding' => json_decode( $cached['embedding'], true ),
				'model' => $cached['embedding_model'],
				'provider' => strpos( $cached['embedding_model'], 'gemini' ) !== false ? 'gemini' : 'openai',
				'dimensions' => (int) $cached['dimensions'],
			);
		}
		
		return null;
	}
	
	/**
	 * Generate embeddings for multiple texts using batch API
	 * Supports both Gemini and OpenAI batch endpoints
	 */
	private function generate_batch_embeddings( $texts, $retry_count = 0 ) {
		// Get the active provider
		$active_provider = get_option('askany_api_provider', 'deepseek');
		$openai_key = $this->api_manager->get_openai_api_key();
		$gemini_key = $this->api_manager->get_gemini_api_key();
		
		// Determine which provider to use
		if ( $active_provider === 'gemini' && ! empty( $gemini_key ) ) {
			return $this->generate_gemini_batch_embeddings( $texts, $retry_count );
		} elseif ( $active_provider === 'openai' && ! empty( $openai_key ) ) {
			return $this->generate_openai_batch_embeddings( $texts, $retry_count );
		} elseif ( ! empty( $openai_key ) ) {
			return $this->generate_openai_batch_embeddings( $texts, $retry_count );
		} elseif ( ! empty( $gemini_key ) ) {
			return $this->generate_gemini_batch_embeddings( $texts, $retry_count );
		}
		
		return new \WP_Error( 'no_api_key', 'No embedding API key configured' );
	}
	
	/**
	 * Generate batch embeddings using Gemini API
	 */
	private function generate_gemini_batch_embeddings( $texts, $retry_count = 0 ) {
		$api_key = $this->api_manager->get_gemini_api_key();
		$model = get_option('askany_embedding_model', 'gemini-embedding-001');
		
		// Build batch request
		$requests = array();
		foreach ( $texts as $text ) {
			$text = $this->prepare_text_for_embedding( $text );
			if ( ! empty( $text ) ) {
				$requests[] = array(
					'model' => 'models/' . $model,
					'content' => array(
						'parts' => array(
							array( 'text' => $text )
						)
					),
					'taskType' => 'RETRIEVAL_DOCUMENT',
				);
			}
		}
		
		if ( empty( $requests ) ) {
			return new \WP_Error( 'empty_texts', 'All texts are empty after cleaning' );
		}
		
		// Call Gemini batch API
		$url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model . ':batchEmbedContents?key=' . $api_key;
		
		$response = wp_remote_post( $url, array(
			'headers' => array(
				'Content-Type' => 'application/json',
			),
			'body' => json_encode( array( 'requests' => $requests ) ),
			'timeout' => 120, // Longer timeout for batch
		) );
		
		if ( is_wp_error( $response ) ) {
			// Retry on timeout
			if ( $retry_count < 2 ) {
				sleep( pow( 2, $retry_count ) );
				return $this->generate_gemini_batch_embeddings( $texts, $retry_count + 1 );
			}
			return $response;
		}
		
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		
		if ( isset( $body['error'] ) ) {
			$error_message = isset( $body['error']['message'] ) ? $body['error']['message'] : 'Unknown Gemini API error';
			
			// Don't log quota errors
			$is_quota_error = stripos($error_message, 'quota') !== false || 
			                  stripos($error_message, 'rate limit') !== false;
			
			if (!$is_quota_error) {
				// ( 'AskAny RAG: Failed to generate Gemini batch embeddings - ' . $error_message );
			}
			
			return new \WP_Error( 'api_error', $error_message );
		}
		
		// Gemini batch API returns 'embeddings' array
		if ( ! isset( $body['embeddings'] ) || ! is_array( $body['embeddings'] ) ) {
			// Log the actual response for debugging
			// ( 'AskAny RAG: Invalid Gemini batch API response structure - ' . wp_json_encode( $body ) );
			
			// Fallback: Try to process as single embedding response
			if ( isset( $body['embedding']['values'] ) ) {
				return array(
					array(
						'embedding' => $body['embedding']['values'],
						'model' => $model,
						'provider' => 'gemini',
						'dimensions' => count( $body['embedding']['values'] ),
					)
				);
			}
			
			return new \WP_Error( 'invalid_response', 'Invalid batch API response from Gemini. Expected "embeddings" array.' );
		}
		
		// Extract embeddings from response
		$embeddings = array();
		foreach ( $body['embeddings'] as $emb ) {
			if ( isset( $emb['values'] ) ) {
				$embeddings[] = array(
					'embedding' => $emb['values'],
					'model' => $model,
					'provider' => 'gemini',
					'dimensions' => count( $emb['values'] ),
				);
			}
		}
		
		if ( empty( $embeddings ) ) {
			// ( 'AskAny RAG: No valid embeddings found in Gemini batch response' );
			return new \WP_Error( 'invalid_response', 'No valid embeddings in batch response' );
		}
		
		return $embeddings;
	}
	
	/**
	 * Generate batch embeddings using OpenAI API
	 * Note: OpenAI doesn't have a dedicated batch endpoint for embeddings,
	 * but we can send array of inputs in single request
	 */
	private function generate_openai_batch_embeddings( $texts, $retry_count = 0 ) {
		$api_key = $this->api_manager->get_openai_api_key();
		$model = get_option('askany_embedding_model', 'text-embedding-3-small');
		
		// Clean texts
		$cleaned_texts = array();
		foreach ( $texts as $text ) {
			$cleaned = $this->prepare_text_for_embedding( $text );
			if ( ! empty( $cleaned ) ) {
				$cleaned_texts[] = $cleaned;
			}
		}
		
		if ( empty( $cleaned_texts ) ) {
			return new \WP_Error( 'empty_texts', 'All texts are empty after cleaning' );
		}
		
		// OpenAI supports array of inputs in single request
		$response = wp_remote_post( 'https://api.openai.com/v1/embeddings', array(
			'headers' => array(
				'Authorization' => 'Bearer ' . $api_key,
				'Content-Type' => 'application/json',
			),
			'body' => json_encode( array(
				'input' => $cleaned_texts,
				'model' => $model,
			) ),
			'timeout' => 120,
		) );
		
		if ( is_wp_error( $response ) ) {
			if ( $retry_count < 2 ) {
				sleep( pow( 2, $retry_count ) );
				return $this->generate_openai_batch_embeddings( $texts, $retry_count + 1 );
			}
			return $response;
		}
		
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		
		if ( isset( $body['error'] ) ) {
			return new \WP_Error( 'api_error', $body['error']['message'] );
		}
		
		if ( ! isset( $body['data'] ) || ! is_array( $body['data'] ) ) {
			return new \WP_Error( 'invalid_response', 'Invalid batch API response from OpenAI' );
		}
		
		// Extract embeddings from response
		$embeddings = array();
		foreach ( $body['data'] as $item ) {
			if ( isset( $item['embedding'] ) ) {
				$embeddings[] = array(
					'embedding' => $item['embedding'],
					'model' => $model,
					'provider' => 'openai',
					'dimensions' => count( $item['embedding'] ),
				);
			}
		}
		
		return $embeddings;
	}
}
