//! Vector Store Adapter - ConcurrentHNSWIndex implementation of Store trait
//!
//! Wraps the high-concurrency ConcurrentHNSWIndex (sharded HNSW, write queues,
//! batch aggregation, backpressure control, rayon parallel search) to implement
//! the unified Store trait for the federation layer.
//!
//! ## async/sync Bridging
//!
//! ConcurrentHNSWIndex's write methods (insert, delete, flush) are async,
//! but Store trait is sync. We use `tokio::task::block_in_place + Handle::current().block_on()`
//! to bridge, the same pattern used by ConcurrentDocStoreAdapter and SocialStoreV2.
//!
//! Read/search operations (search, get, contains) are naturally sync.

use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

use crate::perception::PerceptionEngine;
use crate::vector::concurrent::{ConcurrentHNSWIndex, ConcurrentHNSWConfig, VectorSearchResult as HnswSearchResult};
use super::super::error::{FederationError, Result};
use super::super::traits::{Store, LocalTransaction, AsAny, StoreStats};
use super::super::types::{
    StoreType, Record, WriteOp, WriteResult, BatchWriteResult, UniversalQuery, Filter,
};

/// Adapter for ConcurrentHNSWIndex implementing the Store trait
///
/// This adapter provides high-concurrency vector storage with HNSW indexing
/// for the federation layer. It bridges the async write API to the sync Store trait.
///
/// # Architecture
///
/// ```text
/// VectorStoreAdapter (impl Store)
///     └── ConcurrentHNSWIndex
///             └── VectorShardRouter
///                     ├── VectorShard[0] (HNSWIndex + WriteQueue + BatchAggregator)
///                     ├── VectorShard[1] ...
///                     ├── GlobalVectorIndex
///                     ├── BackpressureController
///                     └── WriteMetrics
/// ```
pub struct VectorStoreAdapter {
    /// Store alias
    alias: String,
    /// Inner ConcurrentHNSWIndex instance
    inner: Arc<ConcurrentHNSWIndex>,
    /// Vector dimension (for validation)
    dimension: usize,
    /// In-memory metadata store (vector_id -> metadata)
    metadata_store: RwLock<HashMap<String, HashMap<String, serde_json::Value>>>,
    /// Optional embedding engine for text → vector conversion
    embedding_engine: Option<Arc<dyn PerceptionEngine>>,
}

impl VectorStoreAdapter {
    /// Create a new adapter wrapping an existing ConcurrentHNSWIndex
    pub fn new(alias: String, index: Arc<ConcurrentHNSWIndex>, dimension: usize) -> Self {
        Self {
            alias,
            inner: index,
            dimension,
            metadata_store: RwLock::new(HashMap::new()),
            embedding_engine: None,
        }
    }

    /// Create a new adapter with a fresh ConcurrentHNSWIndex from config
    pub fn with_config(alias: String, config: ConcurrentHNSWConfig) -> Self {
        let dimension = config.hnsw.dimension;
        let index = ConcurrentHNSWIndex::new(config);
        Self {
            alias,
            inner: Arc::new(index),
            dimension,
            metadata_store: RwLock::new(HashMap::new()),
            embedding_engine: None,
        }
    }

    /// Attach an embedding engine for automatic text → vector conversion
    pub fn with_embedding_engine(mut self, engine: Arc<dyn PerceptionEngine>) -> Self {
        self.embedding_engine = Some(engine);
        self
    }

    /// Set the embedding engine on an existing adapter
    pub fn set_embedding_engine(&mut self, engine: Arc<dyn PerceptionEngine>) {
        self.embedding_engine = Some(engine);
    }

    /// Check if an embedding engine is configured
    pub fn has_embedding_engine(&self) -> bool {
        self.embedding_engine.is_some()
    }

    /// Convert text to vector using the configured embedding engine
    pub fn embed_text(&self, text: &str) -> Result<Vec<f32>> {
        let engine = self.embedding_engine.as_ref()
            .ok_or_else(|| FederationError::ConfigError(
                "No embedding engine configured. Call with_embedding_engine() first.".to_string()
            ))?;

        engine.conceptualize(text)
            .map_err(|e| FederationError::WriteError(format!("Embedding failed: {}", e)))
    }

    /// Upsert a text document: auto-embed text → vector, then store
    ///
    /// The text content is stored in metadata under the "text" key.
    pub fn upsert_text(&self, id: &str, text: &str, mut metadata: HashMap<String, serde_json::Value>) -> Result<WriteResult> {
        let vector = self.embed_text(text)?;

        // Store the original text in metadata
        metadata.insert("_text".to_string(), serde_json::Value::String(text.to_string()));

        self.write(WriteOp::UpsertVector {
            id: id.to_string(),
            vector,
            metadata,
            owner_id: None,
            visibility: None,
        })
    }

    /// Search by text: auto-embed query text → vector, then search
    pub fn search_text(&self, text: &str, top_k: usize, min_score: Option<f32>) -> Result<Vec<Record>> {
        let query_vector = self.embed_text(text)?;

        let q = UniversalQuery {
            target: String::new(),
            filters: vec![],
            limit: None,
            offset: None,
            order_by: None,
            traverse: None,
            vector_search: Some(super::super::types::VectorSearchSpec {
                query_vector,
                top_k,
                min_score,
                metadata_filters: vec![],
            }),
        };

        self.query(&q)
    }

    /// Get the inner ConcurrentHNSWIndex reference
    pub fn inner(&self) -> &Arc<ConcurrentHNSWIndex> {
        &self.inner
    }

    /// Get the vector dimension
    pub fn dimension(&self) -> usize {
        self.dimension
    }

    /// Bridge async operation to sync.
    ///
    /// When running on a Tokio multi-threaded worker thread, uses `block_in_place`
    /// to yield the worker slot. When called from an external thread (e.g. Python
    /// binding that only called `runtime.enter()`), falls back to `handle.block_on()`
    /// which is safe because the caller is already in a sync context.
    fn block_on_async<F: std::future::Future>(&self, f: F) -> F::Output {
        let handle = tokio::runtime::Handle::current();
        // Probe whether we are on a Tokio worker thread.
        // block_in_place panics if not; we catch that to decide.
        let on_worker = std::panic::catch_unwind(|| {
            tokio::task::block_in_place(|| {})
        })
        .is_ok();

        if on_worker {
            tokio::task::block_in_place(|| handle.block_on(f))
        } else {
            handle.block_on(f)
        }
    }

    /// Get metadata for a vector ID
    fn get_metadata(&self, id: &str) -> HashMap<String, serde_json::Value> {
        self.metadata_store.read()
            .map(|store| store.get(id).cloned().unwrap_or_default())
            .unwrap_or_default()
    }

    /// Store metadata for a vector ID
    fn set_metadata(&self, id: &str, metadata: HashMap<String, serde_json::Value>) {
        if !metadata.is_empty() {
            if let Ok(mut store) = self.metadata_store.write() {
                store.insert(id.to_string(), metadata);
            }
        }
    }

    /// Remove metadata for a vector ID
    fn remove_metadata(&self, id: &str) {
        if let Ok(mut store) = self.metadata_store.write() {
            store.remove(id);
        }
    }

    /// Convert HNSW search result to Record::Vector (with metadata lookup)
    fn search_result_to_record(&self, result: &HnswSearchResult) -> Record {
        // Convert distance to similarity score (1.0 - distance for cosine)
        let score = 1.0 - result.distance;
        let metadata = self.get_metadata(&result.id);

        Record::Vector {
            id: result.id.clone(),
            vector: vec![], // Don't include vector data in search results for efficiency
            metadata,
            score: Some(score),
            ownership: None,
        }
    }

    /// Convert a vector ID + data to Record::Vector (with metadata lookup)
    fn vector_to_record(&self, id: &str, vector: &[f32]) -> Record {
        let metadata = self.get_metadata(id);
        Record::Vector {
            id: id.to_string(),
            vector: vector.to_vec(),
            metadata,
            score: None,
            ownership: None,
        }
    }

    /// Check if a record's metadata matches all given filters
    fn matches_metadata_filters(metadata: &HashMap<String, serde_json::Value>, filters: &[Filter]) -> bool {
        filters.iter().all(|filter| Self::matches_single_filter(metadata, filter))
    }

    /// Check a single filter against metadata
    fn matches_single_filter(metadata: &HashMap<String, serde_json::Value>, filter: &Filter) -> bool {
        match filter {
            Filter::Eq(field, value) => {
                metadata.get(field).map_or(false, |v| v == value)
            }
            Filter::Ne(field, value) => {
                metadata.get(field).map_or(true, |v| v != value)
            }
            Filter::Contains(field, substr) => {
                metadata.get(field)
                    .and_then(|v| v.as_str())
                    .map_or(false, |s| s.contains(substr.as_str()))
            }
            Filter::In(field, values) => {
                metadata.get(field).map_or(false, |v| values.contains(v))
            }
            Filter::Gt(field, value) => Self::compare_values(metadata.get(field), value, |a, b| a > b),
            Filter::Gte(field, value) => Self::compare_values(metadata.get(field), value, |a, b| a >= b),
            Filter::Lt(field, value) => Self::compare_values(metadata.get(field), value, |a, b| a < b),
            Filter::Lte(field, value) => Self::compare_values(metadata.get(field), value, |a, b| a <= b),
            Filter::And(inner) => inner.iter().all(|f| Self::matches_single_filter(metadata, f)),
            Filter::Or(inner) => inner.iter().any(|f| Self::matches_single_filter(metadata, f)),
        }
    }

    /// Compare two JSON values numerically
    fn compare_values(actual: Option<&serde_json::Value>, expected: &serde_json::Value, cmp: fn(f64, f64) -> bool) -> bool {
        let a = actual.and_then(|v| v.as_f64());
        let b = expected.as_f64();
        match (a, b) {
            (Some(a), Some(b)) => cmp(a, b),
            _ => false,
        }
    }
}

impl AsAny for VectorStoreAdapter {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
        self
    }
}

impl Store for VectorStoreAdapter {
    fn store_type(&self) -> StoreType {
        StoreType::Vector
    }

    fn alias(&self) -> &str {
        &self.alias
    }

    fn query(&self, q: &UniversalQuery) -> Result<Vec<Record>> {
        // Check for vector search specification
        if let Some(ref vs) = q.vector_search {
            // Over-fetch when metadata filters are present (post-filtering may reduce results)
            let fetch_k = if !vs.metadata_filters.is_empty() || !q.filters.is_empty() {
                vs.top_k * 3  // fetch 3x to allow for filtering
            } else {
                vs.top_k
            };

            // Perform KNN search across all shards (sync, uses rayon parallel)
            let results = self.inner.search(&vs.query_vector, fetch_k);

            // Collect metadata filters (from VectorSearchSpec + UniversalQuery.filters)
            let has_meta_filters = !vs.metadata_filters.is_empty() || !q.filters.is_empty();

            let records: Vec<Record> = results.iter()
                .filter(|r| {
                    // Apply min_score filter
                    if let Some(min_score) = vs.min_score {
                        let score = 1.0 - r.distance;
                        if score < min_score {
                            return false;
                        }
                    }
                    // Apply metadata filters (post-filtering)
                    if has_meta_filters {
                        let metadata = self.get_metadata(&r.id);
                        if !vs.metadata_filters.is_empty()
                            && !Self::matches_metadata_filters(&metadata, &vs.metadata_filters) {
                            return false;
                        }
                        if !q.filters.is_empty()
                            && !Self::matches_metadata_filters(&metadata, &q.filters) {
                            return false;
                        }
                    }
                    true
                })
                .take(vs.top_k) // Limit to requested top_k after filtering
                .map(|r| self.search_result_to_record(r))
                .collect();

            // Apply pagination
            let offset = q.offset.unwrap_or(0);
            let limit = q.limit.unwrap_or(usize::MAX);

            let records = records.into_iter()
                .skip(offset)
                .take(limit)
                .collect();

            return Ok(records);
        }

        // Metadata-only query (no vector search) - scan all stored metadata
        if !q.filters.is_empty() {
            let store = self.metadata_store.read()
                .map_err(|_| FederationError::LockError("metadata store read".to_string()))?;
            let records: Vec<Record> = store.iter()
                .filter(|(_, meta)| Self::matches_metadata_filters(meta, &q.filters))
                .map(|(id, metadata)| {
                    let vector = self.inner.get(id)
                        .map(|arc| arc.as_ref().clone())
                        .unwrap_or_default();
                    Record::Vector {
                        id: id.clone(),
                        vector,
                        metadata: metadata.clone(),
                        score: None,
                        ownership: None,
                    }
                })
                .skip(q.offset.unwrap_or(0))
                .take(q.limit.unwrap_or(usize::MAX))
                .collect();
            return Ok(records);
        }

        Ok(vec![])
    }

    fn write(&self, op: WriteOp) -> Result<WriteResult> {
        match op {
            WriteOp::UpsertVector { id, vector, metadata, .. } => {
                // Validate dimension
                if vector.len() != self.dimension {
                    return Err(FederationError::WriteError(
                        format!("Dimension mismatch: expected {}, got {}", self.dimension, vector.len())
                    ));
                }

                // Bridge async insert to sync
                self.block_on_async(self.inner.insert(id.clone(), vector))
                    .map_err(|e| FederationError::WriteError(e.to_string()))?;

                // Store metadata
                self.set_metadata(&id, metadata);

                Ok(WriteResult::Created(id))
            }
            WriteOp::DeleteVector { id, .. } => {
                // Bridge async delete to sync
                match self.block_on_async(self.inner.delete(&id)) {
                    Ok(_) => {
                        self.remove_metadata(&id);
                        Ok(WriteResult::Deleted(id))
                    }
                    Err(e) => {
                        // If not found, return NoOp instead of error
                        let err_str = e.to_string();
                        if err_str.contains("not found") {
                            Ok(WriteResult::NoOp)
                        } else {
                            Err(FederationError::WriteError(err_str))
                        }
                    }
                }
            }
            _ => Err(FederationError::UnsupportedOperation(
                "Vector store only supports UpsertVector and DeleteVector operations".to_string()
            ))
        }
    }

    fn batch_write(&self, ops: Vec<WriteOp>) -> Result<BatchWriteResult> {
        let start = std::time::Instant::now();
        let mut result = BatchWriteResult::default();

        // Separate vector inserts for batch optimization
        let mut inserts: Vec<(String, Vec<f32>)> = Vec::new();
        let mut other_ops: Vec<WriteOp> = Vec::new();

        for op in ops {
            match op {
                WriteOp::UpsertVector { id, vector, metadata: _, .. } => {
                    if vector.len() != self.dimension {
                        result.failed_count += 1;
                        result.results.push(Err(format!(
                            "Dimension mismatch for '{}': expected {}, got {}",
                            id, self.dimension, vector.len()
                        )));
                    } else {
                        inserts.push((id, vector));
                    }
                }
                _ => other_ops.push(op),
            }
        }

        // Batch insert vectors (much more efficient than individual inserts)
        if !inserts.is_empty() {
            let count = inserts.len();
            match self.block_on_async(self.inner.insert_batch(inserts)) {
                Ok(results) => {
                    for r in results {
                        if r.success {
                            result.success_count += 1;
                            result.results.push(Ok(WriteResult::Created(String::new())));
                        } else {
                            result.failed_count += 1;
                            result.results.push(Err(r.error.unwrap_or_default()));
                        }
                    }
                }
                Err(e) => {
                    // Entire batch failed
                    for _ in 0..count {
                        result.failed_count += 1;
                        result.results.push(Err(e.to_string()));
                    }
                }
            }
        }

        // Process remaining ops individually
        for op in other_ops {
            match self.write(op) {
                Ok(r) => {
                    result.success_count += 1;
                    result.results.push(Ok(r));
                }
                Err(e) => {
                    result.failed_count += 1;
                    result.results.push(Err(e.to_string()));
                }
            }
        }

        result.execution_time_ms = start.elapsed().as_millis() as u64;
        Ok(result)
    }

    fn begin_local_tx(&self) -> Result<Box<dyn LocalTransaction + Send>> {
        Ok(Box::new(VectorLocalTransaction::new(
            self.alias.clone(),
            self.inner.clone(),
            self.dimension,
        )))
    }

    fn get(&self, id: &str) -> Result<Option<Record>> {
        // Sync read from global index → shard → HNSW
        Ok(self.inner.get(id).map(|arc| self.vector_to_record(id, &arc)))
    }

    fn exists(&self, id: &str) -> Result<bool> {
        Ok(self.inner.contains(id))
    }

    fn query_iter(&self, q: &UniversalQuery) -> Result<Box<dyn Iterator<Item = Result<Record>> + Send>> {
        let records = self.query(q)?;
        Ok(Box::new(records.into_iter().map(Ok)))
    }

    fn stats(&self) -> StoreStats {
        let metrics = self.inner.metrics();
        StoreStats {
            record_count: self.inner.len(),
            relation_count: None,
            index_size_bytes: 0,
            data_size_bytes: metrics.bytes_written + metrics.wal_bytes_written,
            last_modified: None,
        }
    }

    fn update_ownership(&self, _id: &str, _ownership: super::super::types::OwnershipMeta) -> Result<()> {
        // Vector store does not yet support ownership metadata persistence.
        // Vectors are stored in HNSW index without ownership fields.
        Err(FederationError::WriteError(
            "Vector store does not support ownership updates".to_string()
        ))
    }

    fn sync(&self) -> Result<()> {
        // Bridge async flush to sync
        self.block_on_async(self.inner.flush())
            .map_err(|e| FederationError::IoError(std::io::Error::new(
                std::io::ErrorKind::Other, e.to_string()
            )))
    }

    fn close(&self) -> Result<()> {
        // Flush all pending writes before closing
        self.sync()
    }
}

// ============================================================================
// Local Transaction
// ============================================================================

/// Local transaction for vector store
struct VectorLocalTransaction {
    alias: String,
    index: Arc<ConcurrentHNSWIndex>,
    dimension: usize,
    pending_ops: Vec<WriteOp>,
    prepared: bool,
}

impl VectorLocalTransaction {
    fn new(alias: String, index: Arc<ConcurrentHNSWIndex>, dimension: usize) -> Self {
        Self {
            alias,
            index,
            dimension,
            pending_ops: Vec::new(),
            prepared: false,
        }
    }
}

impl LocalTransaction for VectorLocalTransaction {
    fn store_alias(&self) -> &str {
        &self.alias
    }

    fn write(&mut self, op: WriteOp) -> Result<()> {
        if !op.is_compatible_with(StoreType::Vector) {
            return Err(FederationError::UnsupportedOperation(
                "Operation not compatible with vector store".to_string()
            ));
        }
        // Validate dimension for inserts
        if let WriteOp::UpsertVector { ref vector, ref id, .. } = op {
            if vector.len() != self.dimension {
                return Err(FederationError::WriteError(
                    format!("Dimension mismatch for '{}': expected {}, got {}", id, self.dimension, vector.len())
                ));
            }
        }
        self.pending_ops.push(op);
        Ok(())
    }

    fn prepare(&mut self) -> Result<bool> {
        // Validate pending ops
        for op in &self.pending_ops {
            if let WriteOp::DeleteVector { id, .. } = op {
                if !self.index.contains(id) {
                    return Ok(false);
                }
            }
        }
        self.prepared = true;
        Ok(true)
    }

    fn commit(&mut self) -> Result<()> {
        if !self.prepared {
            return Err(FederationError::TransactionError(
                "Transaction not prepared".to_string()
            ));
        }

        let adapter = VectorStoreAdapter::new(
            self.alias.clone(),
            self.index.clone(),
            self.dimension,
        );
        for op in std::mem::take(&mut self.pending_ops) {
            adapter.write(op)?;
        }

        adapter.sync()?;
        Ok(())
    }

    fn rollback(&mut self) -> Result<()> {
        self.pending_ops.clear();
        self.prepared = false;
        Ok(())
    }
}
