//! Concurrent Query Engine with backpressure control
//!
//! Provides a high-performance query engine that:
//! - Executes vector and text searches in parallel
//! - Applies backpressure to prevent system overload
//! - Collects detailed query performance metrics
//!
//! # Architecture
//!
//! ```text
//! Query Request
//!      │
//!      ▼
//! ┌─────────────────────┐
//! │ BackpressureControl │ ──► Reject if overloaded
//! └─────────────────────┘
//!      │
//!      ▼
//! ┌─────────────────────┐
//! │   Parallel Search   │
//! │  ┌───────┬────────┐ │
//! │  │Vector │  Text  │ │
//! │  │Search │ Search │ │
//! │  └───────┴────────┘ │
//! └─────────────────────┘
//!      │
//!      ▼
//! ┌─────────────────────┐
//! │   Result Fusion     │
//! └─────────────────────┘
//!      │
//!      ▼
//!   Results + Metrics
//! ```

use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Instant;

use ordered_float::OrderedFloat;
use tokio::sync::Semaphore;

use crate::storage::concurrent::{BackpressureConfig, BackpressureController, WriteMetrics};
use crate::storage::{DocumentStore, GraphStore};
use crate::vector::HNSWIndex;

use super::filter::Filter;
use super::hybrid::{HybridQuery, ScoreBreakdown, SearchResult};

// ============================================================================
// Query Metrics
// ============================================================================

/// Metrics for monitoring query performance
#[derive(Debug, Default)]
pub struct QueryMetrics {
    /// Total number of queries executed
    pub total_queries: AtomicU64,

    /// Number of successful queries
    pub successful_queries: AtomicU64,

    /// Number of failed queries
    pub failed_queries: AtomicU64,

    /// Number of queries rejected due to backpressure
    pub rejected_queries: AtomicU64,

    /// Number of currently active queries
    pub active_queries: AtomicUsize,

    /// Peak number of concurrent queries
    pub peak_concurrent: AtomicUsize,

    /// Total query time in microseconds
    pub total_query_time_us: AtomicU64,

    /// Total vector search time in microseconds
    pub total_vector_time_us: AtomicU64,

    /// Total text search time in microseconds
    pub total_text_time_us: AtomicU64,

    /// Total filter time in microseconds
    pub total_filter_time_us: AtomicU64,

    /// Total results returned
    pub total_results: AtomicU64,

    /// Number of vector-only queries
    pub vector_queries: AtomicU64,

    /// Number of text-only queries
    pub text_queries: AtomicU64,

    /// Number of hybrid queries (both vector and text)
    pub hybrid_queries: AtomicU64,
}

impl QueryMetrics {
    /// Create new query metrics
    pub fn new() -> Self {
        Self::default()
    }

    /// Record query start
    pub fn start_query(&self) -> QueryTimer<'_> {
        self.total_queries.fetch_add(1, Ordering::Relaxed);
        let active = self.active_queries.fetch_add(1, Ordering::Relaxed) + 1;

        // Update peak concurrent
        let mut peak = self.peak_concurrent.load(Ordering::Relaxed);
        while active > peak {
            match self.peak_concurrent.compare_exchange_weak(
                peak,
                active,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(current) => peak = current,
            }
        }

        QueryTimer {
            start: Instant::now(),
            metrics: self,
        }
    }

    /// Record query success
    pub fn record_success(&self, results_count: usize) {
        self.successful_queries.fetch_add(1, Ordering::Relaxed);
        self.total_results
            .fetch_add(results_count as u64, Ordering::Relaxed);
    }

    /// Record query failure
    pub fn record_failure(&self) {
        self.failed_queries.fetch_add(1, Ordering::Relaxed);
    }

    /// Record query rejection
    pub fn record_rejection(&self) {
        self.rejected_queries.fetch_add(1, Ordering::Relaxed);
    }

    /// Record vector search time
    pub fn record_vector_time(&self, us: u64) {
        self.total_vector_time_us.fetch_add(us, Ordering::Relaxed);
    }

    /// Record text search time
    pub fn record_text_time(&self, us: u64) {
        self.total_text_time_us.fetch_add(us, Ordering::Relaxed);
    }

    /// Record filter time
    pub fn record_filter_time(&self, us: u64) {
        self.total_filter_time_us.fetch_add(us, Ordering::Relaxed);
    }

    /// Record query type
    pub fn record_query_type(&self, has_vector: bool, has_text: bool) {
        match (has_vector, has_text) {
            (true, true) => {
                self.hybrid_queries.fetch_add(1, Ordering::Relaxed);
            }
            (true, false) => {
                self.vector_queries.fetch_add(1, Ordering::Relaxed);
            }
            (false, true) => {
                self.text_queries.fetch_add(1, Ordering::Relaxed);
            }
            (false, false) => {}
        }
    }

    /// Get current active query count
    pub fn active_count(&self) -> usize {
        self.active_queries.load(Ordering::Relaxed)
    }

    /// Get metrics snapshot
    pub fn snapshot(&self) -> QueryMetricsSnapshot {
        QueryMetricsSnapshot {
            total_queries: self.total_queries.load(Ordering::Relaxed),
            successful_queries: self.successful_queries.load(Ordering::Relaxed),
            failed_queries: self.failed_queries.load(Ordering::Relaxed),
            rejected_queries: self.rejected_queries.load(Ordering::Relaxed),
            active_queries: self.active_queries.load(Ordering::Relaxed),
            peak_concurrent: self.peak_concurrent.load(Ordering::Relaxed),
            avg_query_time_us: self.avg_query_time_us(),
            avg_vector_time_us: self.avg_vector_time_us(),
            avg_text_time_us: self.avg_text_time_us(),
            avg_filter_time_us: self.avg_filter_time_us(),
            total_results: self.total_results.load(Ordering::Relaxed),
            vector_queries: self.vector_queries.load(Ordering::Relaxed),
            text_queries: self.text_queries.load(Ordering::Relaxed),
            hybrid_queries: self.hybrid_queries.load(Ordering::Relaxed),
        }
    }

    fn avg_query_time_us(&self) -> f64 {
        let total = self.total_query_time_us.load(Ordering::Relaxed);
        let count = self.successful_queries.load(Ordering::Relaxed);
        if count == 0 {
            0.0
        } else {
            total as f64 / count as f64
        }
    }

    fn avg_vector_time_us(&self) -> f64 {
        let total = self.total_vector_time_us.load(Ordering::Relaxed);
        let count = self
            .vector_queries
            .load(Ordering::Relaxed)
            .saturating_add(self.hybrid_queries.load(Ordering::Relaxed));
        if count == 0 {
            0.0
        } else {
            total as f64 / count as f64
        }
    }

    fn avg_text_time_us(&self) -> f64 {
        let total = self.total_text_time_us.load(Ordering::Relaxed);
        let count = self
            .text_queries
            .load(Ordering::Relaxed)
            .saturating_add(self.hybrid_queries.load(Ordering::Relaxed));
        if count == 0 {
            0.0
        } else {
            total as f64 / count as f64
        }
    }

    fn avg_filter_time_us(&self) -> f64 {
        let total = self.total_filter_time_us.load(Ordering::Relaxed);
        let count = self.successful_queries.load(Ordering::Relaxed);
        if count == 0 {
            0.0
        } else {
            total as f64 / count as f64
        }
    }

    /// Reset all metrics
    pub fn reset(&self) {
        self.total_queries.store(0, Ordering::Relaxed);
        self.successful_queries.store(0, Ordering::Relaxed);
        self.failed_queries.store(0, Ordering::Relaxed);
        self.rejected_queries.store(0, Ordering::Relaxed);
        self.active_queries.store(0, Ordering::Relaxed);
        self.peak_concurrent.store(0, Ordering::Relaxed);
        self.total_query_time_us.store(0, Ordering::Relaxed);
        self.total_vector_time_us.store(0, Ordering::Relaxed);
        self.total_text_time_us.store(0, Ordering::Relaxed);
        self.total_filter_time_us.store(0, Ordering::Relaxed);
        self.total_results.store(0, Ordering::Relaxed);
        self.vector_queries.store(0, Ordering::Relaxed);
        self.text_queries.store(0, Ordering::Relaxed);
        self.hybrid_queries.store(0, Ordering::Relaxed);
    }
}

/// Snapshot of query metrics
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct QueryMetricsSnapshot {
    pub total_queries: u64,
    pub successful_queries: u64,
    pub failed_queries: u64,
    pub rejected_queries: u64,
    pub active_queries: usize,
    pub peak_concurrent: usize,
    pub avg_query_time_us: f64,
    pub avg_vector_time_us: f64,
    pub avg_text_time_us: f64,
    pub avg_filter_time_us: f64,
    pub total_results: u64,
    pub vector_queries: u64,
    pub text_queries: u64,
    pub hybrid_queries: u64,
}

impl QueryMetricsSnapshot {
    /// Calculate success rate as percentage
    pub fn success_rate(&self) -> f64 {
        if self.total_queries == 0 {
            100.0
        } else {
            (self.successful_queries as f64 / self.total_queries as f64) * 100.0
        }
    }

    /// Calculate rejection rate as percentage
    pub fn rejection_rate(&self) -> f64 {
        if self.total_queries == 0 {
            0.0
        } else {
            (self.rejected_queries as f64 / self.total_queries as f64) * 100.0
        }
    }

    /// Average results per query
    pub fn avg_results_per_query(&self) -> f64 {
        if self.successful_queries == 0 {
            0.0
        } else {
            self.total_results as f64 / self.successful_queries as f64
        }
    }

    /// Calculate query throughput
    pub fn throughput(&self, elapsed_secs: f64) -> f64 {
        if elapsed_secs <= 0.0 {
            0.0
        } else {
            self.successful_queries as f64 / elapsed_secs
        }
    }
}

/// Timer for tracking query duration
pub struct QueryTimer<'a> {
    start: Instant,
    metrics: &'a QueryMetrics,
}

impl<'a> QueryTimer<'a> {
    /// Get elapsed microseconds
    pub fn elapsed_us(&self) -> u64 {
        self.start.elapsed().as_micros() as u64
    }
}

impl<'a> Drop for QueryTimer<'a> {
    fn drop(&mut self) {
        let elapsed = self.start.elapsed().as_micros() as u64;
        self.metrics
            .total_query_time_us
            .fetch_add(elapsed, Ordering::Relaxed);
        self.metrics.active_queries.fetch_sub(1, Ordering::Relaxed);
    }
}

// ============================================================================
// Concurrent Query Engine Configuration
// ============================================================================

/// Configuration for the concurrent query engine
#[derive(Debug, Clone)]
pub struct ConcurrentQueryConfig {
    /// Maximum concurrent queries
    pub max_concurrent_queries: usize,

    /// Backpressure configuration
    pub backpressure: BackpressureConfig,

    /// Whether to enable parallel search execution
    pub parallel_search: bool,

    /// Over-fetch multiplier for filtering (e.g., 3 = fetch 3x limit)
    pub over_fetch_multiplier: usize,
}

impl Default for ConcurrentQueryConfig {
    fn default() -> Self {
        Self {
            max_concurrent_queries: 100,
            backpressure: BackpressureConfig {
                queue_low_watermark: 0.5,
                queue_high_watermark: 0.8,
                reject_threshold: 0.95,
                base_slowdown_ms: 1,
                max_slowdown_ms: 50,
                exponential_backoff: true,
            },
            parallel_search: true,
            over_fetch_multiplier: 3,
        }
    }
}

impl ConcurrentQueryConfig {
    /// Create config optimized for low latency
    pub fn low_latency() -> Self {
        Self {
            max_concurrent_queries: 50,
            backpressure: BackpressureConfig {
                queue_low_watermark: 0.3,
                queue_high_watermark: 0.6,
                reject_threshold: 0.8,
                base_slowdown_ms: 1,
                max_slowdown_ms: 20,
                exponential_backoff: false,
            },
            parallel_search: true,
            over_fetch_multiplier: 2,
        }
    }

    /// Create config optimized for high throughput
    pub fn high_throughput() -> Self {
        Self {
            max_concurrent_queries: 200,
            backpressure: BackpressureConfig {
                queue_low_watermark: 0.7,
                queue_high_watermark: 0.9,
                reject_threshold: 0.98,
                base_slowdown_ms: 1,
                max_slowdown_ms: 100,
                exponential_backoff: true,
            },
            parallel_search: true,
            over_fetch_multiplier: 4,
        }
    }
}

// ============================================================================
// Query Errors
// ============================================================================

/// Errors from query operations
#[derive(Debug, Clone, thiserror::Error)]
pub enum QueryError {
    #[error("Query engine overloaded, please retry later")]
    Overloaded,

    #[error("Query timeout exceeded")]
    Timeout,

    #[error("Invalid query: {0}")]
    InvalidQuery(String),

    #[error("Internal error: {0}")]
    Internal(String),
}

// ============================================================================
// Concurrent Query Engine
// ============================================================================

/// High-performance concurrent query engine
///
/// Features:
/// - Parallel vector and text search execution
/// - Backpressure control to prevent overload
/// - Comprehensive query metrics
/// - Semaphore-based concurrency limiting
pub struct ConcurrentQueryEngine {
    /// HNSW index for vector search
    hnsw_index: Arc<HNSWIndex>,

    /// Document store
    doc_store: Arc<DocumentStore>,

    /// Graph store (optional)
    graph_store: Option<Arc<GraphStore>>,

    /// Configuration
    config: ConcurrentQueryConfig,

    /// Query metrics
    metrics: Arc<QueryMetrics>,

    /// Backpressure controller (uses WriteMetrics internally)
    backpressure: BackpressureController,

    /// Concurrency limiter
    semaphore: Arc<Semaphore>,
}

impl ConcurrentQueryEngine {
    /// Create a new concurrent query engine
    pub fn new(
        hnsw_index: Arc<HNSWIndex>,
        doc_store: Arc<DocumentStore>,
        config: ConcurrentQueryConfig,
    ) -> Self {
        let metrics = Arc::new(QueryMetrics::new());
        let write_metrics = Arc::new(WriteMetrics::new());
        let backpressure = BackpressureController::new(config.backpressure.clone(), write_metrics);
        let semaphore = Arc::new(Semaphore::new(config.max_concurrent_queries));

        Self {
            hnsw_index,
            doc_store,
            graph_store: None,
            config,
            metrics,
            backpressure,
            semaphore,
        }
    }

    /// Set the graph store
    pub fn with_graph_store(mut self, graph_store: Arc<GraphStore>) -> Self {
        self.graph_store = Some(graph_store);
        self
    }

    /// Get metrics reference
    pub fn metrics(&self) -> &Arc<QueryMetrics> {
        &self.metrics
    }

    /// Get configuration
    pub fn config(&self) -> &ConcurrentQueryConfig {
        &self.config
    }

    /// Get current load ratio (0.0 - 1.0)
    pub fn load_ratio(&self) -> f64 {
        let active = self.metrics.active_count();
        active as f64 / self.config.max_concurrent_queries as f64
    }

    /// Execute a hybrid query with backpressure control
    pub async fn query(&self, query: &HybridQuery) -> Result<Vec<SearchResult>, QueryError> {
        // Check backpressure
        let load = self.load_ratio();
        self.backpressure
            .apply(load)
            .await
            .map_err(|_| QueryError::Overloaded)?;

        // Acquire semaphore permit
        let _permit = self
            .semaphore
            .acquire()
            .await
            .map_err(|_| QueryError::Internal("Semaphore closed".to_string()))?;

        // Start metrics timer
        let _timer = self.metrics.start_query();
        self.metrics
            .record_query_type(query.vector.is_some(), query.text.is_some());

        // Execute query
        let result = if self.config.parallel_search && query.vector.is_some() && query.text.is_some()
        {
            self.parallel_search(query).await
        } else {
            self.sequential_search(query)
        };

        match &result {
            Ok(results) => self.metrics.record_success(results.len()),
            Err(_) => self.metrics.record_failure(),
        }

        result
    }

    /// Execute query without backpressure (for internal use)
    pub fn query_sync(&self, query: &HybridQuery) -> Vec<SearchResult> {
        let _timer = self.metrics.start_query();
        self.metrics
            .record_query_type(query.vector.is_some(), query.text.is_some());

        let result = self.sequential_search(query);
        match &result {
            Ok(results) => self.metrics.record_success(results.len()),
            Err(_) => self.metrics.record_failure(),
        }

        result.unwrap_or_default()
    }

    /// Parallel search execution (async)
    async fn parallel_search(&self, query: &HybridQuery) -> Result<Vec<SearchResult>, QueryError> {
        let k = (query.limit + query.offset) * self.config.over_fetch_multiplier;

        // Clone necessary data for parallel execution
        let hnsw = self.hnsw_index.clone();
        let doc_store = self.doc_store.clone();
        let vector = query.vector.clone();
        let text = query.text.clone();
        let vector_weight = query.vector_weight;
        let text_weight = query.text_weight;
        let metrics = self.metrics.clone();

        // Execute vector and text search in parallel
        let vector_future = {
            let metrics = metrics.clone();
            async move {
                if let Some(ref vec) = vector {
                    let start = Instant::now();
                    let results = hnsw.search_with_ids(vec, k).unwrap_or_default();
                    metrics.record_vector_time(start.elapsed().as_micros() as u64);
                    Some((results, vector_weight))
                } else {
                    None
                }
            }
        };

        let text_future = {
            let metrics = metrics.clone();
            async move {
                if let Some(ref txt) = text {
                    let start = Instant::now();
                    let results = Self::text_search_static(&doc_store, txt);
                    metrics.record_text_time(start.elapsed().as_micros() as u64);
                    Some((results, text_weight))
                } else {
                    None
                }
            }
        };

        let (vector_result, text_result) = tokio::join!(vector_future, text_future);

        // Merge results
        let mut candidates: HashMap<String, (f64, ScoreBreakdown)> = HashMap::new();

        // Process vector results
        if let Some((results, weight)) = vector_result {
            let max_dist = results
                .iter()
                .map(|(_, d)| *d)
                .fold(f32::MIN, f32::max);

            for (id, distance) in results {
                let similarity = if max_dist > 0.0 {
                    1.0 - (distance / max_dist) as f64
                } else {
                    1.0
                };
                let weighted_score = similarity * weight;

                let entry = candidates
                    .entry(id.clone())
                    .or_insert_with(|| (0.0, ScoreBreakdown::default()));
                entry.0 += weighted_score;
                entry.1.vector_score = Some(similarity);
            }
        }

        // Process text results
        if let Some((results, weight)) = text_result {
            for (id, score) in results {
                let weighted_score = score * weight;
                let entry = candidates
                    .entry(id)
                    .or_insert_with(|| (0.0, ScoreBreakdown::default()));
                entry.0 += weighted_score;
                entry.1.text_score = Some(score);
            }
        }

        // Apply filters and build final results
        self.apply_filters_and_build(query, candidates)
    }

    /// Sequential search execution (sync)
    fn sequential_search(&self, query: &HybridQuery) -> Result<Vec<SearchResult>, QueryError> {
        let k = (query.limit + query.offset) * self.config.over_fetch_multiplier;
        let mut candidates: HashMap<String, (f64, ScoreBreakdown)> = HashMap::new();

        // Vector search
        if let Some(ref vector) = query.vector {
            let start = Instant::now();
            if let Ok(results) = self.hnsw_index.search_with_ids(vector, k) {
                let max_dist = results
                    .iter()
                    .map(|(_, d)| *d)
                    .fold(f32::MIN, f32::max);

                for (id, distance) in results {
                    let similarity = if max_dist > 0.0 {
                        1.0 - (distance / max_dist) as f64
                    } else {
                        1.0
                    };
                    let weighted_score = similarity * query.vector_weight;

                    let entry = candidates
                        .entry(id.clone())
                        .or_insert_with(|| (0.0, ScoreBreakdown::default()));
                    entry.0 += weighted_score;
                    entry.1.vector_score = Some(similarity);
                }
            }
            self.metrics
                .record_vector_time(start.elapsed().as_micros() as u64);
        }

        // Text search
        if let Some(ref text) = query.text {
            let start = Instant::now();
            let results = Self::text_search_static(&self.doc_store, text);
            for (id, score) in results {
                let weighted_score = score * query.text_weight;
                let entry = candidates
                    .entry(id)
                    .or_insert_with(|| (0.0, ScoreBreakdown::default()));
                entry.0 += weighted_score;
                entry.1.text_score = Some(score);
            }
            self.metrics
                .record_text_time(start.elapsed().as_micros() as u64);
        }

        // If no vector or text search, get all documents
        if query.vector.is_none() && query.text.is_none() {
            for doc in self.doc_store.all() {
                candidates
                    .entry(doc.id.clone())
                    .or_insert_with(|| (0.0, ScoreBreakdown::default()));
            }
        }

        self.apply_filters_and_build(query, candidates)
    }

    /// Static text search function for async compatibility
    fn text_search_static(doc_store: &DocumentStore, text: &str) -> Vec<(String, f64)> {
        let text_lower = text.to_lowercase();
        let keywords: Vec<&str> = text_lower.split_whitespace().collect();
        let mut results = Vec::new();

        for doc in doc_store.all() {
            let content_lower = doc.content.to_lowercase();
            let matches: usize = keywords
                .iter()
                .filter(|kw| content_lower.contains(*kw))
                .count();

            if matches > 0 {
                let score = matches as f64 / keywords.len() as f64;
                results.push((doc.id.clone(), score));
            }
        }

        results
    }

    /// Apply filters and build final results
    fn apply_filters_and_build(
        &self,
        query: &HybridQuery,
        candidates: HashMap<String, (f64, ScoreBreakdown)>,
    ) -> Result<Vec<SearchResult>, QueryError> {
        let start = Instant::now();
        let mut results: Vec<SearchResult> = Vec::new();

        let filter = Filter {
            and: query.filters.clone(),
            or: Vec::new(),
        };

        for (id, (score, mut breakdown)) in candidates {
            // Get the document
            let node = match self.doc_store.get(&id) {
                Some(n) => n,
                None => continue,
            };

            // Apply metadata filter
            if !filter.is_empty() {
                let metadata_json = serde_json::to_value(&node.metadata).unwrap_or_default();
                if !filter.evaluate(&metadata_json) {
                    continue;
                }
            }

            // Apply time range filter
            if let Some(ref range) = query.time_range {
                if !range.contains(node.created_at) {
                    continue;
                }
            }

            // Apply importance filter
            let importance = node.computed_importance();
            breakdown.importance_score = importance;

            if let Some(min) = query.min_importance {
                if importance < min {
                    continue;
                }
            }

            // Calculate recency score
            let now = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_millis() as i64;
            let age_hours = (now - node.last_accessed_at) as f64 / (1000.0 * 60.0 * 60.0);
            let recency_score = (-age_hours / 168.0).exp();
            breakdown.recency_score = recency_score;

            // Combine scores
            let final_score = score + (importance * 0.1) + (recency_score * 0.05);

            results.push(SearchResult {
                node,
                score: final_score,
                score_breakdown: breakdown,
            });
        }

        // Sort by score descending
        results.sort_by(|a, b| OrderedFloat(b.score).cmp(&OrderedFloat(a.score)));

        self.metrics
            .record_filter_time(start.elapsed().as_micros() as u64);

        // Apply pagination
        Ok(results
            .into_iter()
            .skip(query.offset)
            .take(query.limit)
            .collect())
    }

    /// Search with RRF (Reciprocal Rank Fusion) - parallel version
    pub async fn query_rrf(
        &self,
        query: &HybridQuery,
        k: f64,
    ) -> Result<Vec<SearchResult>, QueryError> {
        // Check backpressure
        let load = self.load_ratio();
        self.backpressure
            .apply(load)
            .await
            .map_err(|_| QueryError::Overloaded)?;

        let _permit = self
            .semaphore
            .acquire()
            .await
            .map_err(|_| QueryError::Internal("Semaphore closed".to_string()))?;

        let _timer = self.metrics.start_query();
        self.metrics
            .record_query_type(query.vector.is_some(), query.text.is_some());

        let result = self.rrf_search_internal(query, k);

        match &result {
            Ok(results) => self.metrics.record_success(results.len()),
            Err(_) => self.metrics.record_failure(),
        }

        result
    }

    /// Internal RRF search implementation
    fn rrf_search_internal(
        &self,
        query: &HybridQuery,
        k: f64,
    ) -> Result<Vec<SearchResult>, QueryError> {
        let mut rrf_scores: HashMap<String, f64> = HashMap::new();
        let num_results = (query.limit + query.offset) * self.config.over_fetch_multiplier;

        // Vector search ranking
        if let Some(ref vector) = query.vector {
            let start = Instant::now();
            if let Ok(results) = self.hnsw_index.search_with_ids(vector, num_results) {
                for (rank, (id, _)) in results.iter().enumerate() {
                    let rrf_score = 1.0 / (k + rank as f64 + 1.0);
                    *rrf_scores.entry(id.clone()).or_default() += rrf_score * query.vector_weight;
                }
            }
            self.metrics
                .record_vector_time(start.elapsed().as_micros() as u64);
        }

        // Text search ranking
        if let Some(ref text) = query.text {
            let start = Instant::now();
            let mut text_results = Self::text_search_static(&self.doc_store, text);
            text_results.sort_by(|a, b| OrderedFloat(b.1).cmp(&OrderedFloat(a.1)));

            for (rank, (id, _)) in text_results.iter().enumerate() {
                let rrf_score = 1.0 / (k + rank as f64 + 1.0);
                *rrf_scores.entry(id.clone()).or_default() += rrf_score * query.text_weight;
            }
            self.metrics
                .record_text_time(start.elapsed().as_micros() as u64);
        }

        // Build results
        let filter = Filter {
            and: query.filters.clone(),
            or: Vec::new(),
        };

        let mut results: Vec<SearchResult> = Vec::new();

        for (id, rrf_score) in rrf_scores {
            let node = match self.doc_store.get(&id) {
                Some(n) => n,
                None => continue,
            };

            if !filter.is_empty() {
                let metadata_json = serde_json::to_value(&node.metadata).unwrap_or_default();
                if !filter.evaluate(&metadata_json) {
                    continue;
                }
            }

            if let Some(ref range) = query.time_range {
                if !range.contains(node.created_at) {
                    continue;
                }
            }

            let importance = node.computed_importance();
            if let Some(min) = query.min_importance {
                if importance < min {
                    continue;
                }
            }

            results.push(SearchResult {
                node,
                score: rrf_score,
                score_breakdown: ScoreBreakdown::default(),
            });
        }

        results.sort_by(|a, b| OrderedFloat(b.score).cmp(&OrderedFloat(a.score)));

        Ok(results
            .into_iter()
            .skip(query.offset)
            .take(query.limit)
            .collect())
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::{HNSWConfig, MemoryNode};
    use super::super::filter::FilterCondition;
    use tempfile::tempdir;

    fn setup_test_engine() -> (ConcurrentQueryEngine, Arc<HNSWIndex>, Arc<DocumentStore>) {
        let dir = tempdir().unwrap();

        let hnsw_config = HNSWConfig::new(4)
            .with_m(4)
            .with_ef_construction(16)
            .with_ef_search(16)
            .with_max_elements(100);

        let hnsw = Arc::new(HNSWIndex::new(hnsw_config));
        let doc_store =
            Arc::new(DocumentStore::open_or_create(dir.path().join("docs.jsonl")).unwrap());

        let config = ConcurrentQueryConfig::default();
        let engine = ConcurrentQueryEngine::new(hnsw.clone(), doc_store.clone(), config);

        (engine, hnsw, doc_store)
    }

    #[test]
    fn test_query_metrics() {
        let metrics = QueryMetrics::new();

        // Start some queries
        {
            let _t1 = metrics.start_query();
            let _t2 = metrics.start_query();
            assert_eq!(metrics.active_count(), 2);
        }

        // Timers dropped, active should be 0
        assert_eq!(metrics.active_count(), 0);
        assert_eq!(metrics.snapshot().total_queries, 2);
    }

    #[test]
    fn test_query_metrics_snapshot() {
        let metrics = QueryMetrics::new();

        metrics.record_query_type(true, false);
        metrics.record_query_type(false, true);
        metrics.record_query_type(true, true);
        metrics.record_success(5);
        metrics.record_success(10);
        metrics.record_failure();
        metrics.record_rejection();

        let snapshot = metrics.snapshot();
        assert_eq!(snapshot.vector_queries, 1);
        assert_eq!(snapshot.text_queries, 1);
        assert_eq!(snapshot.hybrid_queries, 1);
        assert_eq!(snapshot.successful_queries, 2);
        assert_eq!(snapshot.failed_queries, 1);
        assert_eq!(snapshot.rejected_queries, 1);
        assert_eq!(snapshot.total_results, 15);
    }

    #[test]
    fn test_concurrent_engine_text_search() {
        let (engine, _, doc_store) = setup_test_engine();

        // Add documents
        doc_store
            .put(MemoryNode::new("The quick brown fox jumps over the lazy dog"))
            .unwrap();
        doc_store
            .put(MemoryNode::new("A quick test document"))
            .unwrap();
        doc_store
            .put(MemoryNode::new("Something completely different"))
            .unwrap();

        let query = HybridQuery::text("quick");
        let results = engine.query_sync(&query);

        assert_eq!(results.len(), 2);
        assert!(results[0].node.content.contains("quick"));

        // Check metrics
        let snapshot = engine.metrics().snapshot();
        assert_eq!(snapshot.total_queries, 1);
        assert_eq!(snapshot.text_queries, 1);
    }

    #[test]
    fn test_concurrent_engine_with_filter() {
        let (engine, _, doc_store) = setup_test_engine();

        let mut node1 = MemoryNode::new("Important document");
        node1
            .metadata
            .insert("status".to_string(), serde_json::json!("active"));

        let mut node2 = MemoryNode::new("Archived document");
        node2
            .metadata
            .insert("status".to_string(), serde_json::json!("archived"));

        doc_store.put(node1).unwrap();
        doc_store.put(node2).unwrap();

        let query = HybridQuery::empty().filter(FilterCondition::eq("status", "active"));
        let results = engine.query_sync(&query);

        assert_eq!(results.len(), 1);
        assert!(results[0].node.content.contains("Important"));
    }

    #[test]
    fn test_load_ratio() {
        let (engine, _, _) = setup_test_engine();

        // Initially no active queries
        assert_eq!(engine.load_ratio(), 0.0);

        // Start a query
        let _timer = engine.metrics.start_query();
        assert!(engine.load_ratio() > 0.0);
    }

    #[tokio::test]
    async fn test_async_query() {
        let (engine, _, doc_store) = setup_test_engine();

        doc_store.put(MemoryNode::new("Test document")).unwrap();

        let query = HybridQuery::text("Test");
        let results = engine.query(&query).await.unwrap();

        assert_eq!(results.len(), 1);
    }

    #[tokio::test]
    async fn test_concurrent_queries() {
        let (engine, _, doc_store) = setup_test_engine();
        let engine = Arc::new(engine);

        // Add documents
        for i in 0..10 {
            doc_store
                .put(MemoryNode::new(format!("Document number {}", i)))
                .unwrap();
        }

        // Execute multiple queries concurrently
        let mut handles = Vec::new();
        for i in 0..5 {
            let engine = engine.clone();
            let handle = tokio::spawn(async move {
                let query = HybridQuery::text(format!("Document {}", i));
                engine.query(&query).await
            });
            handles.push(handle);
        }

        // Wait for all queries
        for handle in handles {
            let result = handle.await.unwrap();
            assert!(result.is_ok());
        }

        // Check metrics
        let snapshot = engine.metrics().snapshot();
        assert_eq!(snapshot.total_queries, 5);
        assert_eq!(snapshot.successful_queries, 5);
    }

    #[test]
    fn test_config_presets() {
        let low_latency = ConcurrentQueryConfig::low_latency();
        assert!(low_latency.max_concurrent_queries < ConcurrentQueryConfig::default().max_concurrent_queries);

        let high_throughput = ConcurrentQueryConfig::high_throughput();
        assert!(high_throughput.max_concurrent_queries > ConcurrentQueryConfig::default().max_concurrent_queries);
    }
}
