//! Lance Store Adapter - LanceCollectionManager implementation of Store trait
//!
//! Wraps a single Lance-backed Collection to implement the unified Store trait
//! for the federation layer. Each adapter represents one Lance collection.

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

use crate::storage::collection::Collection;
use crate::storage::lance::LanceCollectionManager;
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,
};

/// Adapter for a single Lance-backed Collection implementing the Store trait.
///
/// Each `LanceStoreAdapter` wraps one `Collection` obtained from a `LanceCollectionManager`.
/// Data is stored in a Lance columnar dataset, enabling versioning, compaction,
/// and high-throughput scans on top of the standard federation interface.
pub struct LanceStoreAdapter {
    /// Store alias
    alias: String,
    /// The underlying Lance-backed Collection
    collection: Arc<Collection>,
    /// Reference to the LanceCollectionManager (for version/schema ops)
    manager: Arc<LanceCollectionManager>,
    /// Collection name (for manager API calls)
    collection_name: String,
}

impl LanceStoreAdapter {
    /// Create a new adapter wrapping an existing Lance-backed Collection.
    pub fn new(
        alias: String,
        collection_name: String,
        collection: Arc<Collection>,
        manager: Arc<LanceCollectionManager>,
    ) -> Self {
        Self {
            alias,
            collection,
            manager,
            collection_name,
        }
    }

    /// Open or create a Lance-backed collection and return an adapter.
    pub fn open(
        alias: String,
        collection_name: &str,
        manager: Arc<LanceCollectionManager>,
        dimension: usize,
    ) -> Result<Self> {
        let config = crate::types::collection::CollectionConfig::new(collection_name, dimension);
        let collection = manager
            .get_or_create_collection(config)
            .map_err(|e| FederationError::WriteError(format!("Lance open failed: {}", e)))?;
        Ok(Self {
            alias,
            collection,
            manager,
            collection_name: collection_name.to_string(),
        })
    }

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

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

    /// Get the collection name this adapter represents.
    pub fn collection_name(&self) -> &str {
        &self.collection_name
    }

    /// 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 LanceStoreAdapter {
    fn as_any(&self) -> &dyn Any {
        self
    }

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

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

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

    fn query(&self, q: &UniversalQuery) -> Result<Vec<Record>> {
        let all_nodes = self.collection.docs.all()
            .map_err(|e| FederationError::QueryError(format!("Failed to read all nodes: {}", e)))?;

        let mut records: Vec<Record> = all_nodes
            .iter()
            .filter(|node| {
                if q.target.is_empty() || q.target == "*" {
                    true
                } else {
                    let ct = format!("{:?}", node.content_type).to_lowercase();
                    ct == q.target.to_lowercase()
                }
            })
            .filter(|node| 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 {
                    super::super::types::SortOrder::Asc => cmp,
                    super::super::types::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();
                if let Some(protection_val) = content.get("__protection__") {
                    if let Ok(protection) = serde_json::from_value::<
                        crate::types::ownership::ProtectionLevel,
                    >(protection_val.clone())
                    {
                        node.protection = protection;
                    }
                }
                self.collection
                    .docs
                    .put(node)
                    .map_err(|e| FederationError::WriteError(e.to_string()))?;
                Ok(WriteResult::Created(id))
            }
            WriteOp::UpdateDocument {
                id, content, tags, ..
            } => {
                let existing = self
                    .collection
                    .docs
                    .get(&id)
                    .map_err(|e| FederationError::WriteError(format!("Storage error: {}", e)))?
                    .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;

                self.collection
                    .docs
                    .put(updated)
                    .map_err(|e| FederationError::WriteError(e.to_string()))?;
                Ok(WriteResult::Updated(id))
            }
            WriteOp::DeleteDocument { id, .. } => {
                match self.collection.docs.delete(&id) {
                    Ok(Some(_)) => Ok(WriteResult::Deleted(id)),
                    Ok(None) => Ok(WriteResult::NoOp),
                    Err(e) => Err(FederationError::WriteError(format!("Delete failed: {}", e))),
                }
            }
            _ => Err(FederationError::UnsupportedOperation(
                "Lance store adapter only supports document 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()));
                }
            }
        }

        let _ = self.collection.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(LanceLocalTransaction::new(
            self.alias.clone(),
            self.collection.clone(),
        )))
    }

    fn get(&self, id: &str) -> Result<Option<Record>> {
        let node_opt = self.collection.docs.get(id)
            .map_err(|e| FederationError::QueryError(format!("Get failed: {}", e)))?;
        Ok(node_opt.map(|node| self.node_to_record(&node)))
    }

    fn exists(&self, id: &str) -> Result<bool> {
        self.collection.docs.contains(id)
            .map_err(|e| FederationError::QueryError(format!("Contains check failed: {}", e)))
    }

    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 {
        StoreStats {
            record_count: self.collection.document_count(),
            relation_count: None,
            index_size_bytes: 0,
            data_size_bytes: 0,
            last_modified: None,
        }
    }

    fn update_ownership(
        &self,
        id: &str,
        ownership: super::super::types::OwnershipMeta,
    ) -> Result<()> {
        let mut node = self
            .collection
            .docs
            .get(id)
            .map_err(|e| FederationError::WriteError(format!("Storage error: {}", e)))?
            .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.collection
            .docs
            .put(node)
            .map_err(|e| FederationError::WriteError(e.to_string()))
    }

    fn sync(&self) -> Result<()> {
        self.collection
            .flush()
            .map_err(|e| FederationError::IoError(e))
    }

    fn close(&self) -> Result<()> {
        self.sync()
    }
}

/// Local transaction for Lance store
struct LanceLocalTransaction {
    alias: String,
    collection: Arc<Collection>,
    pending_ops: Vec<WriteOp>,
    prepared: bool,
}

impl LanceLocalTransaction {
    fn new(alias: String, collection: Arc<Collection>) -> Self {
        Self {
            alias,
            collection,
            pending_ops: Vec::new(),
            prepared: false,
        }
    }
}

impl LocalTransaction for LanceLocalTransaction {
    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 Lance store".to_string(),
            ));
        }
        self.pending_ops.push(op);
        Ok(())
    }

    fn prepare(&mut self) -> Result<bool> {
        for op in &self.pending_ops {
            match op {
                WriteOp::UpdateDocument { id, .. } | WriteOp::DeleteDocument { id, .. } => {
                    let exists = self.collection.docs.contains(id)
                        .map_err(|e| FederationError::WriteError(format!("Storage error: {}", e)))?;
                    if !exists {
                        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(),
            ));
        }

        for op in std::mem::take(&mut self.pending_ops) {
            match op {
                WriteOp::CreateDocument {
                    id,
                    doc_type,
                    content,
                    tags,
                    owner_id,
                    visibility,
                    ..
                } => {
                    let mut node =
                        LanceStoreAdapter::create_op_to_node(&id, &doc_type, &content, &tags);
                    node.owner_id = owner_id;
                    node.visibility = visibility.unwrap_or_default();
                    self.collection
                        .docs
                        .put(node)
                        .map_err(|e| FederationError::WriteError(e.to_string()))?;
                }
                WriteOp::UpdateDocument {
                    id, content, tags, ..
                } => {
                    let existing = self.collection.docs.get(&id)
                        .map_err(|e| FederationError::WriteError(format!("Storage error: {}", e)))?
                        .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(t) = tags {
                        updated.tags = t;
                    }
                    self.collection
                        .docs
                        .put(updated)
                        .map_err(|e| FederationError::WriteError(e.to_string()))?;
                }
                WriteOp::DeleteDocument { id, .. } => {
                    let _ = self.collection.docs.delete(&id);
                }
                _ => {}
            }
        }

        let _ = self.collection.flush();
        Ok(())
    }

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