//! Concurrent Document Store Adapter - ConcurrentDocumentStore implementation of Store trait
//!
//! Wraps the high-concurrency ConcurrentDocumentStore (sharded WAL, batch aggregation,
//! backpressure control) to implement the unified Store trait for the federation layer.
//!
//! ## async/sync Bridging
//!
//! ConcurrentDocumentStore's write methods are async, but Store trait is sync.
//! We use `tokio::task::block_in_place + Handle::current().block_on()` to bridge,
//! which is the same pattern used by SocialStoreV2::new_async() (store_v2.rs:286-296).
//!
//! Read operations (get, contains, len) are naturally sync since they read from
//! the underlying DocumentStore directly.

use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;

use crate::storage::concurrent_store::ConcurrentDocumentStore;
use crate::types::{ContentType, MemoryNode};
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, SortOrder,
};

/// Adapter for ConcurrentDocumentStore implementing the Store trait
///
/// This adapter provides high-concurrency document storage with WAL durability
/// for the federation layer. For lightweight use cases, see `DocumentStoreAdapter`.
pub struct ConcurrentDocStoreAdapter {
    /// Store alias
    alias: String,
    /// Inner ConcurrentDocumentStore instance
    inner: Arc<ConcurrentDocumentStore>,
}

impl ConcurrentDocStoreAdapter {
    /// Create a new adapter wrapping an existing ConcurrentDocumentStore
    pub fn new(alias: String, store: Arc<ConcurrentDocumentStore>) -> Self {
        Self { alias, inner: store }
    }

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

    /// 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)
        }
    }

    /// Convert MemoryNode to Record::Document
    fn node_to_record(&self, node: &MemoryNode) -> Record {
        let content = serde_json::json!({
            "text": node.content,
            "content_type": format!("{:?}", node.content_type),
            "parent_id": node.parent_id,
            "metadata": node.metadata,
            "importance": node.importance,
            "access_count": node.access_count,
        });

        use super::super::types::OwnershipMeta;
        
        let ownership = node.owner_id.as_ref().map(|oid| {
            let mut meta = OwnershipMeta::new_for_user(oid)
                .with_visibility(node.visibility.clone())
                .with_protection(node.protection.clone());
            meta.collaborators = node.collaborators.clone();
            meta
        });
        
        Record::Document {
            id: node.id.clone(),
            doc_type: format!("{:?}", node.content_type).to_lowercase(),
            content,
            tags: node.tags.clone(),
            created_at: node.created_at as u64,
            updated_at: node.last_accessed_at as u64,
            ownership,
        }
    }

    /// Convert WriteOp::CreateDocument to MemoryNode
    fn create_op_to_node(id: &str, doc_type: &str, content: &serde_json::Value, tags: &[String]) -> MemoryNode {
        let text = content.get("text")
            .and_then(|v| v.as_str())
            .unwrap_or("")
            .to_string();

        let content_type = match doc_type {
            "text" => ContentType::Text,
            "image" => ContentType::Image,
            "code" => ContentType::Code,
            "conversation" => ContentType::Conversation,
            "document" => ContentType::Document,
            _ => ContentType::Other,
        };

        let metadata: HashMap<String, serde_json::Value> = content.get("metadata")
            .and_then(|v| serde_json::from_value(v.clone()).ok())
            .unwrap_or_default();

        let importance = content.get("importance")
            .and_then(|v| v.as_f64())
            .unwrap_or(0.5);

        let parent_id = content.get("parent_id")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis() as i64;

        MemoryNode {
            id: id.to_string(),
            content: text,
            embedding: None,
            content_type,
            parent_id,
            children: vec![],
            depth: 0,
            metadata,
            tags: tags.to_vec(),
            created_at: now,
            last_accessed_at: now,
            access_count: 0,
            importance,
            decay_rate: 0.0001,
            entity_ids: vec![],
            relation_ids: vec![],
            owner_id: None,
            visibility: crate::types::ownership::Visibility::default(),
            protection: crate::types::ownership::ProtectionLevel::default(),
            collaborators: vec![],
            semantic_axes: None,
        }
    }

    /// Apply Filter to a MemoryNode, returns true if the node matches
    fn matches_filter(node: &MemoryNode, filter: &Filter) -> bool {
        match filter {
            Filter::Eq(field, value) => {
                match field.as_str() {
                    "id" => value.as_str().map_or(false, |v| v == node.id),
                    "content_type" => value.as_str().map_or(false, |v| {
                        format!("{:?}", node.content_type).to_lowercase() == v.to_lowercase()
                    }),
                    "importance" => value.as_f64().map_or(false, |v| (node.importance - v).abs() < f64::EPSILON),
                    _ => node.metadata.get(field).map_or(false, |mv| mv == value),
                }
            }
            Filter::Contains(field, text) => {
                match field.as_str() {
                    "content" | "text" => node.content.contains(text.as_str()),
                    "tags" => node.tags.iter().any(|t| t.contains(text.as_str())),
                    _ => false,
                }
            }
            Filter::In(field, values) => {
                match field.as_str() {
                    "id" => values.iter().any(|v| v.as_str().map_or(false, |s| s == node.id)),
                    "tags" => {
                        let str_values: Vec<&str> = values.iter()
                            .filter_map(|v| v.as_str())
                            .collect();
                        node.tags.iter().any(|t| str_values.contains(&t.as_str()))
                    }
                    _ => false,
                }
            }
            Filter::Gt(field, value) => {
                match field.as_str() {
                    "importance" => value.as_f64().map_or(false, |v| node.importance > v),
                    "created_at" => value.as_i64().map_or(false, |v| node.created_at > v),
                    "access_count" => value.as_u64().map_or(false, |v| node.access_count > v),
                    _ => false,
                }
            }
            Filter::Gte(field, value) => {
                match field.as_str() {
                    "importance" => value.as_f64().map_or(false, |v| node.importance >= v),
                    "created_at" => value.as_i64().map_or(false, |v| node.created_at >= v),
                    _ => false,
                }
            }
            Filter::Lt(field, value) => {
                match field.as_str() {
                    "importance" => value.as_f64().map_or(false, |v| node.importance < v),
                    "created_at" => value.as_i64().map_or(false, |v| node.created_at < v),
                    _ => false,
                }
            }
            Filter::Lte(field, value) => {
                match field.as_str() {
                    "importance" => value.as_f64().map_or(false, |v| node.importance <= v),
                    "created_at" => value.as_i64().map_or(false, |v| node.created_at <= v),
                    _ => false,
                }
            }
            Filter::Ne(field, value) => {
                !Self::matches_filter(node, &Filter::Eq(field.clone(), value.clone()))
            }
            Filter::And(filters) => filters.iter().all(|f| Self::matches_filter(node, f)),
            Filter::Or(filters) => filters.iter().any(|f| Self::matches_filter(node, f)),
        }
    }
}

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

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

impl Store for ConcurrentDocStoreAdapter {
    fn store_type(&self) -> StoreType {
        StoreType::Document
    }

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

    fn query(&self, q: &UniversalQuery) -> Result<Vec<Record>> {
        // Read all documents from the underlying sync DocumentStore
        let all_nodes = self.inner.doc_store().all();

        let mut records: Vec<Record> = all_nodes.iter()
            .filter(|node| {
                // Filter by target (doc_type / content_type)
                if q.target.is_empty() || q.target == "*" {
                    true
                } else {
                    let ct = format!("{:?}", node.content_type).to_lowercase();
                    ct == q.target.to_lowercase()
                }
            })
            .filter(|node| {
                // Apply query filters
                q.filters.iter().all(|f| Self::matches_filter(node, f))
            })
            .map(|node| self.node_to_record(node))
            .collect();

        // Apply ordering
        if let Some((field, order)) = &q.order_by {
            records.sort_by(|a, b| {
                let cmp = match (a, b) {
                    (Record::Document { created_at: ca, .. }, Record::Document { created_at: cb, .. }) => {
                        match field.as_str() {
                            "created_at" => ca.cmp(cb),
                            "updated_at" => {
                                let ua = if let Record::Document { updated_at, .. } = a { *updated_at } else { 0 };
                                let ub = if let Record::Document { updated_at, .. } = b { *updated_at } else { 0 };
                                ua.cmp(&ub)
                            }
                            _ => std::cmp::Ordering::Equal,
                        }
                    }
                    _ => std::cmp::Ordering::Equal,
                };
                match order {
                    SortOrder::Asc => cmp,
                    SortOrder::Desc => cmp.reverse(),
                }
            });
        }

        // 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();

        Ok(records)
    }

    fn write(&self, op: WriteOp) -> Result<WriteResult> {
        match op {
            WriteOp::CreateDocument { id, doc_type, content, tags, owner_id, visibility, .. } => {
                let mut node = Self::create_op_to_node(&id, &doc_type, &content, &tags);
                node.owner_id = owner_id;
                node.visibility = visibility.unwrap_or_default();
                // Bridge async write to sync via block_in_place
                self.block_on_async(self.inner.write(node))
                    .map_err(|e| FederationError::WriteError(e.to_string()))?;
                Ok(WriteResult::Created(id))
            }
            WriteOp::UpdateDocument { id, content, tags, .. } => {
                // Get existing node (sync read from underlying DocumentStore)
                let existing = self.inner.get(&id)
                    .ok_or_else(|| FederationError::WriteError(format!("Document not found: {}", id)))?;

                let mut updated = existing;

                if let Some(c) = content {
                    if let Some(text) = c.get("text").and_then(|v| v.as_str()) {
                        updated.content = text.to_string();
                    }
                    if let Some(metadata) = c.get("metadata") {
                        if let Ok(m) = serde_json::from_value::<HashMap<String, serde_json::Value>>(metadata.clone()) {
                            updated.metadata = m;
                        }
                    }
                    if let Some(importance) = c.get("importance").and_then(|v| v.as_f64()) {
                        updated.importance = importance;
                    }
                }

                if let Some(t) = tags {
                    updated.tags = t;
                }

                updated.last_accessed_at = std::time::SystemTime::now()
                    .duration_since(std::time::UNIX_EPOCH)
                    .unwrap_or_default()
                    .as_millis() as i64;

                // Bridge async write
                self.block_on_async(self.inner.write(updated))
                    .map_err(|e| FederationError::WriteError(e.to_string()))?;
                Ok(WriteResult::Updated(id))
            }
            WriteOp::DeleteDocument { id, .. } => {
                // Delete from underlying DocumentStore (sync)
                if self.inner.doc_store().delete(&id).is_some() {
                    Ok(WriteResult::Deleted(id))
                } else {
                    Ok(WriteResult::NoOp)
                }
            }
            _ => Err(FederationError::UnsupportedOperation(
                "Concurrent document store does not support graph/vector operations".to_string()
            ))
        }
    }

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

        for op in 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()));
                }
            }
        }

        // Flush after batch via async bridge
        let _ = self.block_on_async(self.inner.flush());

        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(ConcurrentDocLocalTransaction::new(
            self.alias.clone(),
            self.inner.clone(),
        )))
    }

    fn get(&self, id: &str) -> Result<Option<Record>> {
        // Sync read from underlying DocumentStore
        Ok(self.inner.get(id).map(|node| self.node_to_record(&node)))
    }

    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<()> {
        let mut node = self.inner.get(id)
            .ok_or_else(|| FederationError::WriteError(format!("Document not found: {}", id)))?;
        node.owner_id = Some(ownership.owner_id);
        node.visibility = ownership.visibility;
        node.protection = ownership.protection;
        node.collaborators = ownership.collaborators;
        self.block_on_async(self.inner.write(node))
            .map(|_| ())
            .map_err(|e| FederationError::WriteError(e.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<()> {
        // Bridge async shutdown to sync
        self.block_on_async(self.inner.shutdown())
            .map_err(|e| FederationError::IoError(std::io::Error::new(
                std::io::ErrorKind::Other, e.to_string()
            )))
    }
}

/// Local transaction for concurrent document store
struct ConcurrentDocLocalTransaction {
    alias: String,
    store: Arc<ConcurrentDocumentStore>,
    pending_ops: Vec<WriteOp>,
    prepared: bool,
}

impl ConcurrentDocLocalTransaction {
    fn new(alias: String, store: Arc<ConcurrentDocumentStore>) -> Self {
        Self {
            alias,
            store,
            pending_ops: Vec::new(),
            prepared: false,
        }
    }
}

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

    fn write(&mut self, op: WriteOp) -> Result<()> {
        if !op.is_compatible_with(StoreType::Document) {
            return Err(FederationError::UnsupportedOperation(
                "Operation not compatible with document store".to_string()
            ));
        }
        self.pending_ops.push(op);
        Ok(())
    }

    fn prepare(&mut self) -> Result<bool> {
        // Validate pending ops
        for op in &self.pending_ops {
            match op {
                WriteOp::UpdateDocument { id, .. } | WriteOp::DeleteDocument { id, .. } => {
                    if !self.store.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 = ConcurrentDocStoreAdapter::new(self.alias.clone(), self.store.clone());
        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(())
    }
}
