//! Graph Store Adapter - SocialStoreV2 implementation of Store trait
//!
//! ## Federation Adaptation (v2.4.1)
//!
//! This adapter bridges SocialStoreV2 into the unified Store trait for the
//! federation layer. Key design decisions:
//!
//! - **Universal entity access**: Uses `get_entity_by_id()` and `list_entities_by_type()`
//!   instead of type-specific methods, supporting custom entity types.
//! - **Runtime safety**: Uses SocialStoreV2's sync methods which internally manage
//!   their own tokio runtime, avoiding block_on nesting issues.
//!   When the Store trait becomes async, will switch to `*_async()` methods.

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

use chrono::Utc;

use crate::social::SocialStoreV2;
use crate::types::graph::{Entity, Relation};
use super::super::error::{FederationError, Result};
use super::super::traits::{Store, LocalTransaction, AsAny, StoreStats};
use super::super::types::{
    StoreType, Record, WriteOp, WriteResult, BatchWriteResult, UniversalQuery,
};

/// Adapter for SocialStoreV2 implementing the Store trait
pub struct GraphStoreAdapter {
    /// Store alias
    alias: String,
    /// Inner SocialStoreV2 instance
    inner: Arc<SocialStoreV2>,
}

impl GraphStoreAdapter {
    /// Create a new GraphStoreAdapter
    pub fn new(alias: String, store: Arc<SocialStoreV2>) -> Self {
        Self { alias, inner: store }
    }
    
    /// Get the inner SocialStoreV2 reference
    pub fn inner(&self) -> &Arc<SocialStoreV2> {
        &self.inner
    }
    
    /// Convert entity to Record
    fn entity_to_record(&self, entity: &Entity) -> Record {
        use super::super::types::OwnershipMeta;
        
        // Build ownership metadata from entity's owner_id (if set)
        let ownership = entity.owner_id.as_ref().map(|oid| {
            let mut meta = OwnershipMeta::new_for_user(oid)
                .with_visibility(entity.visibility.clone())
                .with_protection(entity.protection.clone());
            meta.collaborators = entity.collaborators.clone();
            meta
        });
        
        Record::GraphEntity {
            id: entity.id.clone(),
            entity_type: entity.entity_type.clone(),
            name: entity.name.clone(),
            properties: entity.properties.clone().into_iter().collect(),
            created_at: entity.created_at,
            ownership,
        }
    }
    
    /// Convert relation to Record
    fn relation_to_record(&self, relation: &Relation) -> Record {
        Record::GraphRelation {
            id: relation.id.clone(),
            source: relation.source.clone(),
            target: relation.target.clone(),
            relation_type: relation.relation_type.clone(),
            metadata: relation.metadata.clone().into_iter().collect(),
            ownership: None,
        }
    }
}

impl AsAny for GraphStoreAdapter {
    fn as_any(&self) -> &dyn Any {
        self
    }
    
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
        self
    }
}

impl Store for GraphStoreAdapter {
    fn store_type(&self) -> StoreType {
        StoreType::Graph
    }
    
    fn alias(&self) -> &str {
        &self.alias
    }
    
    fn query(&self, q: &UniversalQuery) -> Result<Vec<Record>> {
        // Use universal list_entities_by_type which supports ALL entity types
        // including custom types like "article", "category", etc.
        let entities: Vec<Entity> = if q.target.is_empty() || q.target == "*" {
            // Wildcard: return all entities from all types
            let mut all = Vec::new();
            for entity_type in self.inner.get_all_entity_types() {
                all.extend(self.inner.list_entities_by_type(&entity_type));
            }
            all
        } else {
            // Query specific type (case-insensitive match)
            let target_lower = q.target.to_lowercase();
            self.inner.list_entities_by_type(&target_lower)
        };
        
        // Convert to records
        let mut records: Vec<Record> = entities.iter()
            .map(|e| self.entity_to_record(e))
            .collect();
        
        // Apply ordering
        if let Some((field, order)) = &q.order_by {
            records.sort_by(|a, b| {
                let cmp = match (a, b) {
                    (Record::GraphEntity { name: na, created_at: ca, .. }, 
                     Record::GraphEntity { name: nb, created_at: cb, .. }) => {
                        match field.as_str() {
                            "name" => na.cmp(nb),
                            "created_at" => ca.cmp(cb),
                            _ => 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::CreateEntity { entity_type, id, name, attributes, owner_id, visibility, .. } => {
                let properties: std::collections::BTreeMap<String, serde_json::Value> = attributes
                    .into_iter()
                    .collect();
                
                let now = Utc::now().timestamp();
                let entity = Entity {
                    id: id.clone(),
                    name,
                    entity_type,
                    properties,
                    memory_refs: vec![],
                    embedding: None,
                    created_at: now,
                    updated_at: now,
                    owner_id,
                    visibility: visibility.unwrap_or_default(),
                    protection: crate::types::ownership::ProtectionLevel::default(),
                    collaborators: vec![],
                };
                
                self.inner.put_entity(entity);
                Ok(WriteResult::Created(id))
            }
            WriteOp::UpdateEntity { id, name, attributes, .. } => {
                // Use universal get_entity_by_id to find existing entity
                let existing = self.inner.get_entity_by_id(&id);
                
                if let Some(mut entity) = existing {
                    // Update fields if provided
                    if let Some(new_name) = name {
                        entity.name = new_name;
                    }
                    if let Some(attrs) = attributes {
                        for (k, v) in attrs {
                            entity.properties.insert(k, v);
                        }
                    }
                    entity.updated_at = Utc::now().timestamp();
                    
                    self.inner.put_entity(entity);
                    Ok(WriteResult::Updated(id))
                } else {
                    // Entity not found - create new with provided attributes
                    let properties: std::collections::BTreeMap<String, serde_json::Value> = 
                        attributes.unwrap_or_default().into_iter().collect();
                    
                    let now = Utc::now().timestamp();
                    let entity = Entity {
                        id: id.clone(),
                        name: name.unwrap_or_default(),
                        entity_type: "unknown".to_string(),
                        properties,
                        memory_refs: vec![],
                        embedding: None,
                        created_at: now,
                        updated_at: now,
                        owner_id: None,
                        visibility: crate::types::ownership::Visibility::default(),
                        protection: crate::types::ownership::ProtectionLevel::default(),
                        collaborators: vec![],
                    };
                    
                    self.inner.put_entity(entity);
                    Ok(WriteResult::Created(id))
                }
            }
            WriteOp::DeleteEntity { id, .. } => {
                let deleted = self.inner.delete_entity(&id);
                if deleted {
                    Ok(WriteResult::Deleted(id))
                } else {
                    Ok(WriteResult::NoOp)
                }
            }
            WriteOp::CreateRelation { source_id, target_id, relation_type, attributes, .. } => {
                let metadata: std::collections::BTreeMap<String, serde_json::Value> = attributes
                    .into_iter()
                    .collect();
                
                let now = Utc::now().timestamp();
                let relation = Relation {
                    id: uuid::Uuid::new_v4().to_string(),
                    source: source_id,
                    target: target_id,
                    relation_type,
                    weight: 1.0,
                    bidirectional: false,
                    metadata,
                    created_at: now,
                };
                
                let rel_id = relation.id.clone();
                self.inner.put_relation(relation);
                Ok(WriteResult::Created(rel_id))
            }
            WriteOp::DeleteRelation { relation_id, .. } => {
                self.inner.delete_relation(&relation_id);
                Ok(WriteResult::Deleted(relation_id))
            }
            _ => Err(FederationError::UnsupportedOperation(
                "Graph store does not support document/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()));
                }
            }
        }
        
        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(GraphLocalTransaction::new(self.alias.clone(), self.inner.clone())))
    }
    
    fn get(&self, id: &str) -> Result<Option<Record>> {
        // Use universal get_entity_by_id: single O(1) lookup via GlobalGraphIndex
        // Works for ALL entity types including custom ones (article, category, etc.)
        Ok(self.inner.get_entity_by_id(id).map(|e| self.entity_to_record(&e)))
    }
    
    fn exists(&self, id: &str) -> Result<bool> {
        Ok(self.inner.contains_entity(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 {
        StoreStats {
            record_count: self.inner.entity_count(),
            relation_count: Some(self.inner.relation_count()),
            index_size_bytes: 0, // TODO: Calculate from WAL
            data_size_bytes: 0,
            last_modified: None,
        }
    }
    
    fn update_ownership(&self, id: &str, ownership: super::super::types::OwnershipMeta) -> Result<()> {
        let mut entity = self.inner.get_entity_by_id(id)
            .ok_or_else(|| FederationError::WriteError(format!("Entity not found: {}", id)))?;
        entity.owner_id = Some(ownership.owner_id);
        entity.visibility = ownership.visibility;
        entity.protection = ownership.protection;
        entity.collaborators = ownership.collaborators;
        entity.updated_at = chrono::Utc::now().timestamp();
        self.inner.put_entity(entity);
        Ok(())
    }
    
    fn sync(&self) -> Result<()> {
        self.inner.sync()
            .map_err(|e| FederationError::IoError(std::io::Error::new(
                std::io::ErrorKind::Other, e.to_string()
            )))
    }
    
    fn close(&self) -> Result<()> {
        // SocialStoreV2 doesn't have explicit close, sync is sufficient
        self.sync()
    }
}

/// Local transaction for graph store
struct GraphLocalTransaction {
    alias: String,
    store: Arc<SocialStoreV2>,
    pending_ops: Vec<WriteOp>,
    prepared: bool,
}

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

impl LocalTransaction for GraphLocalTransaction {
    fn store_alias(&self) -> &str {
        &self.alias
    }
    
    fn write(&mut self, op: WriteOp) -> Result<()> {
        if !op.is_compatible_with(StoreType::Graph) {
            return Err(FederationError::UnsupportedOperation(
                "Operation not compatible with graph store".to_string()
            ));
        }
        self.pending_ops.push(op);
        Ok(())
    }
    
    fn prepare(&mut self) -> Result<bool> {
        // For graph store, we just validate the operations
        // Actual 2PC would require WAL support
        self.prepared = true;
        Ok(true)
    }
    
    fn commit(&mut self) -> Result<()> {
        if !self.prepared {
            return Err(FederationError::TransactionError(
                "Transaction not prepared".to_string()
            ));
        }
        
        // Execute all pending operations
        let adapter = GraphStoreAdapter::new(self.alias.clone(), self.store.clone());
        for op in std::mem::take(&mut self.pending_ops) {
            adapter.write(op)?;
        }
        
        // Sync to disk
        adapter.sync()?;
        
        Ok(())
    }
    
    fn rollback(&mut self) -> Result<()> {
        self.pending_ops.clear();
        self.prepared = false;
        Ok(())
    }
}
