//! Shard Unit Tests
//!
//! Comprehensive tests for GraphShard functionality including:
//! - Single shard CRUD operations
//! - Shard index consistency
//! - Shard boundary conditions
//! - Shard capacity testing

use std::collections::HashSet;
use std::sync::Arc;
use std::time::Duration;

use tempfile::tempdir;

use crate::storage::concurrent::WriteMetrics;
use crate::types::graph::{Entity, Relation, entity_types, relation_types};

use super::config::GraphShardConfig;
use super::shard::GraphShard;
use super::types::{GraphWriteOp, GraphStoreError};
use super::wal::GraphWALConfig;

// ============================================================================
// Test Helpers
// ============================================================================

/// Create a test shard without WAL
fn create_test_shard() -> GraphShard {
    let dir = tempdir().unwrap();
    let config = GraphShardConfig::new(0, dir.path());
    let metrics = Arc::new(WriteMetrics::new());
    GraphShard::new(config, metrics)
}

/// Create a test shard with Arc wrapper for async tests
fn create_test_shard_arc() -> Arc<GraphShard> {
    let dir = tempdir().unwrap();
    let config = GraphShardConfig::new(0, dir.path());
    let metrics = Arc::new(WriteMetrics::new());
    Arc::new(GraphShard::new(config, metrics))
}

/// Create a test shard with WAL enabled
fn create_test_shard_with_wal() -> Arc<GraphShard> {
    let dir = tempdir().unwrap();
    let config = GraphShardConfig::new(0, dir.path());
    let metrics = Arc::new(WriteMetrics::new());
    let wal_config = GraphWALConfig::new(dir.path().join("wal"));
    Arc::new(GraphShard::with_wal(config, metrics, wal_config).unwrap())
}

/// Create a test shard with custom queue capacity
fn create_test_shard_with_capacity(capacity: usize) -> Arc<GraphShard> {
    let dir = tempdir().unwrap();
    let mut config = GraphShardConfig::new(0, dir.path());
    config.queue_config.capacity = capacity;
    let metrics = Arc::new(WriteMetrics::new());
    Arc::new(GraphShard::new(config, metrics))
}

/// Create a test entity with given name
fn create_test_entity(name: &str) -> Entity {
    Entity::new(name, entity_types::PERSON)
}

/// Create a test relation between two entities
fn create_test_relation(source: &str, target: &str) -> Relation {
    Relation::new(source, target, relation_types::FRIEND)
}

// ============================================================================
// 5.1.1 Single Shard CRUD Operations - Entity Tests
// ============================================================================

#[test]
fn test_entity_create() {
    let shard = create_test_shard();
    
    let entity = create_test_entity("Alice");
    let entity_id = entity.id.clone();
    
    // Create entity
    let op = GraphWriteOp::PutEntity(entity);
    let result = shard.apply_op(&op);
    
    assert!(result.is_ok());
    assert_eq!(result.unwrap(), entity_id);
    assert!(shard.contains_entity(&entity_id));
    assert_eq!(shard.entity_count(), 1);
}

#[test]
fn test_entity_read() {
    let shard = create_test_shard();
    
    let entity = create_test_entity("Bob");
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    // Read entity
    let retrieved = shard.get_entity(&entity_id);
    assert!(retrieved.is_some());
    
    let retrieved = retrieved.unwrap();
    assert_eq!(retrieved.id, entity_id);
    assert_eq!(retrieved.name, "Bob");
    assert_eq!(retrieved.entity_type, entity_types::PERSON);
}

#[test]
fn test_entity_update() {
    let shard = create_test_shard();
    
    // Create initial entity
    let mut entity = create_test_entity("Carol");
    let entity_id = entity.id.clone();
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    // Update entity name
    entity.name = "Carol Updated".to_string();
    entity.properties.insert("age".to_string(), serde_json::json!(30));
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    // Verify update
    let retrieved = shard.get_entity(&entity_id).unwrap();
    assert_eq!(retrieved.name, "Carol Updated");
    assert_eq!(retrieved.properties.get("age"), Some(&serde_json::json!(30)));
    
    // Count should remain 1
    assert_eq!(shard.entity_count(), 1);
}

#[test]
fn test_entity_update_type_change() {
    let shard = create_test_shard();
    
    // Create entity as PERSON
    let mut entity = Entity::new("Organization", entity_types::PERSON);
    let entity_id = entity.id.clone();
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    // Verify in PERSON type index
    let persons = shard.find_entities_by_type(entity_types::PERSON);
    assert_eq!(persons.len(), 1);
    
    // Update to ORGANIZATION type
    entity.entity_type = "organization".to_string();
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    // Should be removed from PERSON index
    let persons = shard.find_entities_by_type(entity_types::PERSON);
    assert_eq!(persons.len(), 0);
    
    // Should be in organization index
    let orgs = shard.find_entities_by_type("organization");
    assert_eq!(orgs.len(), 1);
    assert_eq!(orgs[0].id, entity_id);
}

#[test]
fn test_entity_delete() {
    let shard = create_test_shard();
    
    let entity = create_test_entity("David");
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    assert!(shard.contains_entity(&entity_id));
    
    // Delete entity
    let result = shard.apply_op(&GraphWriteOp::DeleteEntity(entity_id.clone()));
    assert!(result.is_ok());
    assert!(!shard.contains_entity(&entity_id));
    assert_eq!(shard.entity_count(), 0);
}

#[test]
fn test_entity_delete_nonexistent() {
    let shard = create_test_shard();
    
    // Deleting nonexistent entity should succeed (idempotent)
    let result = shard.apply_op(&GraphWriteOp::DeleteEntity("nonexistent-id".to_string()));
    assert!(result.is_ok());
}

#[test]
fn test_entity_all_entities() {
    let shard = create_test_shard();
    
    // Add multiple entities
    let names = ["Alice", "Bob", "Carol", "David", "Eve"];
    let mut entity_ids = Vec::new();
    
    for name in names {
        let entity = create_test_entity(name);
        entity_ids.push(entity.id.clone());
        shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    }
    
    // Get all entities
    let all = shard.all_entities();
    assert_eq!(all.len(), 5);
    
    // Verify all IDs are present
    let retrieved_ids: HashSet<_> = all.iter().map(|e| e.id.clone()).collect();
    for id in &entity_ids {
        assert!(retrieved_ids.contains(id));
    }
}

// ============================================================================
// 5.1.1 Single Shard CRUD Operations - Relation Tests
// ============================================================================

#[test]
fn test_relation_create() {
    let shard = create_test_shard();
    
    // Create entities first
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let alice_id = alice.id.clone();
    let bob_id = bob.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob)).unwrap();
    
    // Create relation
    let relation = create_test_relation(&alice_id, &bob_id);
    let relation_id = relation.id.clone();
    
    let result = shard.apply_op(&GraphWriteOp::PutRelation(relation));
    assert!(result.is_ok());
    assert!(shard.contains_relation(&relation_id));
    assert_eq!(shard.relation_count(), 1);
}

#[test]
fn test_relation_read() {
    let shard = create_test_shard();
    
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let alice_id = alice.id.clone();
    let bob_id = bob.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob)).unwrap();
    
    let relation = create_test_relation(&alice_id, &bob_id);
    let relation_id = relation.id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Read relation
    let retrieved = shard.get_relation(&relation_id);
    assert!(retrieved.is_some());
    
    let retrieved = retrieved.unwrap();
    assert_eq!(retrieved.source, alice_id);
    assert_eq!(retrieved.target, bob_id);
    assert_eq!(retrieved.relation_type, relation_types::FRIEND);
}

#[test]
fn test_relation_update() {
    let shard = create_test_shard();
    
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let alice_id = alice.id.clone();
    let bob_id = bob.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob)).unwrap();
    
    let mut relation = create_test_relation(&alice_id, &bob_id);
    let relation_id = relation.id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation.clone())).unwrap();
    
    // Update relation metadata
    relation.metadata.insert("strength".to_string(), serde_json::json!(0.9));
    relation.metadata.insert("since".to_string(), serde_json::json!("2020-01-01"));
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Verify update
    let retrieved = shard.get_relation(&relation_id).unwrap();
    assert_eq!(retrieved.metadata.get("strength"), Some(&serde_json::json!(0.9)));
    assert_eq!(shard.relation_count(), 1);
}

#[test]
fn test_relation_update_endpoints() {
    let shard = create_test_shard();
    
    // Create three entities
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let carol = create_test_entity("Carol");
    let alice_id = alice.id.clone();
    let bob_id = bob.id.clone();
    let carol_id = carol.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(carol)).unwrap();
    
    // Create initial relation Alice -> Bob
    let mut relation = create_test_relation(&alice_id, &bob_id);
    let _relation_id = relation.id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation.clone())).unwrap();
    
    // Verify initial indices
    assert_eq!(shard.outgoing_from(&alice_id).len(), 1);
    assert_eq!(shard.incoming_to(&bob_id).len(), 1);
    
    // Update relation to Alice -> Carol
    relation.target = carol_id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Bob should no longer have incoming relation
    assert_eq!(shard.incoming_to(&bob_id).len(), 0);
    // Carol should have incoming relation
    assert_eq!(shard.incoming_to(&carol_id).len(), 1);
    // Alice's outgoing should still be 1
    assert_eq!(shard.outgoing_from(&alice_id).len(), 1);
}

#[test]
fn test_relation_delete() {
    let shard = create_test_shard();
    
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let alice_id = alice.id.clone();
    let bob_id = bob.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice)).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob)).unwrap();
    
    let relation = create_test_relation(&alice_id, &bob_id);
    let relation_id = relation.id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Delete relation
    let result = shard.apply_op(&GraphWriteOp::DeleteRelation(relation_id.clone()));
    assert!(result.is_ok());
    assert!(!shard.contains_relation(&relation_id));
    assert_eq!(shard.relation_count(), 0);
    
    // Indices should be cleaned up
    assert!(shard.outgoing_from(&alice_id).is_empty());
    assert!(shard.incoming_to(&bob_id).is_empty());
}

#[test]
fn test_relation_all_relations() {
    let shard = create_test_shard();
    
    // Create entities
    let entities: Vec<_> = (0..5)
        .map(|i| create_test_entity(&format!("Person{}", i)))
        .collect();
    
    for entity in &entities {
        shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    }
    
    // Create relations: 0->1, 1->2, 2->3, 3->4
    let mut relation_ids = Vec::new();
    for i in 0..4 {
        let relation = create_test_relation(&entities[i].id, &entities[i + 1].id);
        relation_ids.push(relation.id.clone());
        shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    }
    
    // Get all relations
    let all = shard.all_relations();
    assert_eq!(all.len(), 4);
    
    // Verify all IDs present
    let retrieved_ids: HashSet<_> = all.iter().map(|r| r.id.clone()).collect();
    for id in &relation_ids {
        assert!(retrieved_ids.contains(id));
    }
}

// ============================================================================
// 5.1.1 Single Shard CRUD Operations - Batch Tests
// ============================================================================

#[test]
fn test_batch_operations() {
    let shard = create_test_shard();
    
    // Create batch of entities
    let entities: Vec<_> = (0..10)
        .map(|i| create_test_entity(&format!("BatchPerson{}", i)))
        .collect();
    
    let ops: Vec<GraphWriteOp> = entities
        .iter()
        .map(|e| GraphWriteOp::PutEntity(e.clone()))
        .collect();
    
    let batch_op = GraphWriteOp::Batch(ops);
    let result = shard.apply_op(&batch_op);
    
    assert!(result.is_ok());
    assert_eq!(shard.entity_count(), 10);
    
    // Verify all entities exist
    for entity in &entities {
        assert!(shard.contains_entity(&entity.id));
    }
}

#[test]
fn test_batch_mixed_operations() {
    let shard = create_test_shard();
    
    // Create some entities first
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    let carol = create_test_entity("Carol");
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob.clone())).unwrap();
    
    // Mixed batch: add carol, add relation, delete alice
    let relation = create_test_relation(&bob.id, &carol.id);
    let batch_op = GraphWriteOp::Batch(vec![
        GraphWriteOp::PutEntity(carol.clone()),
        GraphWriteOp::PutRelation(relation.clone()),
        GraphWriteOp::DeleteEntity(alice.id.clone()),
    ]);
    
    let result = shard.apply_op(&batch_op);
    assert!(result.is_ok());
    
    // Verify results
    assert!(!shard.contains_entity(&alice.id)); // deleted
    assert!(shard.contains_entity(&bob.id)); // unchanged
    assert!(shard.contains_entity(&carol.id)); // added
    assert!(shard.contains_relation(&relation.id)); // added
}

// ============================================================================
// 5.1.2 Shard Index Consistency Tests
// ============================================================================

#[test]
fn test_type_index_consistency() {
    let shard = create_test_shard();
    
    // Add entities of different types
    let persons: Vec<_> = (0..5)
        .map(|i| Entity::new(&format!("Person{}", i), entity_types::PERSON))
        .collect();
    
    let items: Vec<_> = (0..3)
        .map(|i| Entity::new(&format!("Item{}", i), entity_types::ITEM))
        .collect();
    
    for entity in &persons {
        shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    }
    for entity in &items {
        shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    }
    
    // Verify type indices
    let found_persons = shard.find_entities_by_type(entity_types::PERSON);
    let found_items = shard.find_entities_by_type(entity_types::ITEM);
    
    assert_eq!(found_persons.len(), 5);
    assert_eq!(found_items.len(), 3);
    
    // Verify correct entities in each type
    let person_ids: HashSet<_> = found_persons.iter().map(|e| &e.id).collect();
    for person in &persons {
        assert!(person_ids.contains(&person.id));
    }
    
    let item_ids: HashSet<_> = found_items.iter().map(|e| &e.id).collect();
    for item in &items {
        assert!(item_ids.contains(&item.id));
    }
}

#[test]
fn test_type_index_after_delete() {
    let shard = create_test_shard();
    
    // Add entities
    let entities: Vec<_> = (0..5)
        .map(|i| create_test_entity(&format!("Person{}", i)))
        .collect();
    
    for entity in &entities {
        shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    }
    
    assert_eq!(shard.find_entities_by_type(entity_types::PERSON).len(), 5);
    
    // Delete some entities
    shard.apply_op(&GraphWriteOp::DeleteEntity(entities[1].id.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::DeleteEntity(entities[3].id.clone())).unwrap();
    
    // Verify type index updated
    let found = shard.find_entities_by_type(entity_types::PERSON);
    assert_eq!(found.len(), 3);
    
    let found_ids: HashSet<_> = found.iter().map(|e| &e.id).collect();
    assert!(found_ids.contains(&entities[0].id));
    assert!(!found_ids.contains(&entities[1].id)); // deleted
    assert!(found_ids.contains(&entities[2].id));
    assert!(!found_ids.contains(&entities[3].id)); // deleted
    assert!(found_ids.contains(&entities[4].id));
}

#[test]
fn test_outgoing_index_consistency() {
    let shard = create_test_shard();
    
    // Create entities
    let alice = create_test_entity("Alice");
    let friends: Vec<_> = (0..5)
        .map(|i| create_test_entity(&format!("Friend{}", i)))
        .collect();
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice.clone())).unwrap();
    for friend in &friends {
        shard.apply_op(&GraphWriteOp::PutEntity(friend.clone())).unwrap();
    }
    
    // Create relations from Alice to all friends
    let mut relation_ids = Vec::new();
    for friend in &friends {
        let relation = create_test_relation(&alice.id, &friend.id);
        relation_ids.push(relation.id.clone());
        shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    }
    
    // Verify outgoing index
    let outgoing = shard.outgoing_from(&alice.id);
    assert_eq!(outgoing.len(), 5);
    
    let outgoing_targets: HashSet<_> = outgoing.iter().map(|r| &r.target).collect();
    for friend in &friends {
        assert!(outgoing_targets.contains(&friend.id));
    }
    
    // Delete some relations
    shard.apply_op(&GraphWriteOp::DeleteRelation(relation_ids[1].clone())).unwrap();
    shard.apply_op(&GraphWriteOp::DeleteRelation(relation_ids[3].clone())).unwrap();
    
    // Verify outgoing index updated
    let outgoing = shard.outgoing_from(&alice.id);
    assert_eq!(outgoing.len(), 3);
}

#[test]
fn test_incoming_index_consistency() {
    let shard = create_test_shard();
    
    // Create entities
    let bob = create_test_entity("Bob");
    let admirers: Vec<_> = (0..4)
        .map(|i| create_test_entity(&format!("Admirer{}", i)))
        .collect();
    
    shard.apply_op(&GraphWriteOp::PutEntity(bob.clone())).unwrap();
    for admirer in &admirers {
        shard.apply_op(&GraphWriteOp::PutEntity(admirer.clone())).unwrap();
    }
    
    // Create relations from admirers to Bob
    for admirer in &admirers {
        let relation = create_test_relation(&admirer.id, &bob.id);
        shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    }
    
    // Verify incoming index
    let incoming = shard.incoming_to(&bob.id);
    assert_eq!(incoming.len(), 4);
    
    let incoming_sources: HashSet<_> = incoming.iter().map(|r| &r.source).collect();
    for admirer in &admirers {
        assert!(incoming_sources.contains(&admirer.id));
    }
}

#[test]
fn test_bidirectional_relations() {
    let shard = create_test_shard();
    
    // Create two entities
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob.clone())).unwrap();
    
    // Create bidirectional relations
    let rel1 = create_test_relation(&alice.id, &bob.id);
    let rel2 = create_test_relation(&bob.id, &alice.id);
    
    shard.apply_op(&GraphWriteOp::PutRelation(rel1.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::PutRelation(rel2.clone())).unwrap();
    
    // Verify indices
    assert_eq!(shard.outgoing_from(&alice.id).len(), 1);
    assert_eq!(shard.incoming_to(&alice.id).len(), 1);
    assert_eq!(shard.outgoing_from(&bob.id).len(), 1);
    assert_eq!(shard.incoming_to(&bob.id).len(), 1);
    
    // Delete one relation
    shard.apply_op(&GraphWriteOp::DeleteRelation(rel1.id.clone())).unwrap();
    
    // Verify only affected indices updated
    assert_eq!(shard.outgoing_from(&alice.id).len(), 0); // deleted
    assert_eq!(shard.incoming_to(&bob.id).len(), 0); // deleted
    assert_eq!(shard.outgoing_from(&bob.id).len(), 1); // unchanged
    assert_eq!(shard.incoming_to(&alice.id).len(), 1); // unchanged
}

// ============================================================================
// 5.1.3 Shard Boundary Condition Tests
// ============================================================================

#[test]
fn test_empty_shard_operations() {
    let shard = create_test_shard();
    
    // Empty shard queries
    assert!(shard.get_entity("nonexistent").is_none());
    assert!(shard.get_relation("nonexistent").is_none());
    assert!(!shard.contains_entity("nonexistent"));
    assert!(!shard.contains_relation("nonexistent"));
    assert!(shard.outgoing_from("nonexistent").is_empty());
    assert!(shard.incoming_to("nonexistent").is_empty());
    assert!(shard.find_entities_by_type(entity_types::PERSON).is_empty());
    assert!(shard.all_entities().is_empty());
    assert!(shard.all_relations().is_empty());
    
    // Counts
    assert_eq!(shard.entity_count(), 0);
    assert_eq!(shard.relation_count(), 0);
}

#[test]
fn test_single_entity_shard() {
    let shard = create_test_shard();
    
    let entity = create_test_entity("OnlyOne");
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
    
    assert_eq!(shard.entity_count(), 1);
    assert!(shard.contains_entity(&entity_id));
    assert_eq!(shard.get_entity(&entity_id).unwrap().name, "OnlyOne");
    assert_eq!(shard.find_entities_by_type(entity_types::PERSON).len(), 1);
    
    // Delete the only entity
    shard.apply_op(&GraphWriteOp::DeleteEntity(entity_id)).unwrap();
    assert_eq!(shard.entity_count(), 0);
}

#[test]
fn test_special_character_ids() {
    let shard = create_test_shard();
    
    // Test various special characters in names/IDs
    let special_names = [
        "Test Name With Spaces",
        "Test-Name-With-Dashes",
        "Test_Name_With_Underscores",
        "Test.Name.With.Dots",
        "Test:Name:With:Colons",
        "Test/Name/With/Slashes",
        "Test@Name@With@At",
        "Test#Name#With#Hash",
        "中文名字",
        "日本語の名前",
        "Имя на русском",
        "🎉 Emoji Name 🎊",
    ];
    
    for name in special_names {
        let entity = create_test_entity(name);
        let entity_id = entity.id.clone();
        
        shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
        
        let retrieved = shard.get_entity(&entity_id);
        assert!(retrieved.is_some(), "Failed to retrieve entity with name: {}", name);
        assert_eq!(retrieved.unwrap().name, name);
    }
    
    assert_eq!(shard.entity_count(), special_names.len());
}

#[test]
fn test_duplicate_entity_id() {
    let shard = create_test_shard();
    
    // Create an entity with a specific ID
    let mut entity1 = create_test_entity("First");
    let entity_id = entity1.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity1.clone())).unwrap();
    assert_eq!(shard.get_entity(&entity_id).unwrap().name, "First");
    
    // Create another entity with the same ID (should update)
    entity1.name = "Second".to_string();
    shard.apply_op(&GraphWriteOp::PutEntity(entity1)).unwrap();
    
    // Should be updated, not duplicated
    assert_eq!(shard.entity_count(), 1);
    assert_eq!(shard.get_entity(&entity_id).unwrap().name, "Second");
}

#[test]
fn test_duplicate_relation_id() {
    let shard = create_test_shard();
    
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob.clone())).unwrap();
    
    let mut relation = create_test_relation(&alice.id, &bob.id);
    let relation_id = relation.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutRelation(relation.clone())).unwrap();
    assert_eq!(shard.relation_count(), 1);
    
    // Update with same ID
    relation.metadata.insert("updated".to_string(), serde_json::json!(true));
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Should be updated, not duplicated
    assert_eq!(shard.relation_count(), 1);
    let retrieved = shard.get_relation(&relation_id).unwrap();
    assert_eq!(retrieved.metadata.get("updated"), Some(&serde_json::json!(true)));
}

#[test]
fn test_self_referencing_relation() {
    let shard = create_test_shard();
    
    let entity = create_test_entity("SelfRef");
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    
    // Create self-referencing relation
    let relation = create_test_relation(&entity_id, &entity_id);
    let relation_id = relation.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    // Should appear in both outgoing and incoming
    assert_eq!(shard.outgoing_from(&entity_id).len(), 1);
    assert_eq!(shard.incoming_to(&entity_id).len(), 1);
    
    // Delete relation
    shard.apply_op(&GraphWriteOp::DeleteRelation(relation_id)).unwrap();
    
    assert!(shard.outgoing_from(&entity_id).is_empty());
    assert!(shard.incoming_to(&entity_id).is_empty());
}

#[test]
fn test_empty_name_entity() {
    let shard = create_test_shard();
    
    let entity = Entity::new("", entity_types::PERSON);
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    
    let retrieved = shard.get_entity(&entity_id).unwrap();
    assert_eq!(retrieved.name, "");
}

#[test]
fn test_very_long_name_entity() {
    let shard = create_test_shard();
    
    let long_name = "A".repeat(10000);
    let entity = Entity::new(&long_name, entity_types::PERSON);
    let entity_id = entity.id.clone();
    
    shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    
    let retrieved = shard.get_entity(&entity_id).unwrap();
    assert_eq!(retrieved.name.len(), 10000);
}

// ============================================================================
// 5.1.4 Shard Capacity Tests
// ============================================================================

#[test]
fn test_large_entity_count() {
    let shard = create_test_shard();
    
    const ENTITY_COUNT: usize = 10_000;
    
    // Add many entities
    for i in 0..ENTITY_COUNT {
        let entity = create_test_entity(&format!("Entity{}", i));
        shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    }
    
    assert_eq!(shard.entity_count(), ENTITY_COUNT);
    
    // Verify type index
    let persons = shard.find_entities_by_type(entity_types::PERSON);
    assert_eq!(persons.len(), ENTITY_COUNT);
    
    // Verify random access
    for _i in (0..ENTITY_COUNT).step_by(1000) {
        let entities = shard.all_entities();
        // Just verify we can access all entities
        assert!(!entities.is_empty());
    }
}

#[test]
fn test_large_relation_count() {
    let shard = create_test_shard();
    
    // Create entities
    const ENTITY_COUNT: usize = 100;
    let entities: Vec<_> = (0..ENTITY_COUNT)
        .map(|i| {
            let entity = create_test_entity(&format!("Entity{}", i));
            shard.apply_op(&GraphWriteOp::PutEntity(entity.clone())).unwrap();
            entity
        })
        .collect();
    
    // Create dense relations (each entity connected to next 10)
    let mut relation_count = 0;
    for i in 0..ENTITY_COUNT {
        for j in 1..=10 {
            let target_idx = (i + j) % ENTITY_COUNT;
            let relation = create_test_relation(&entities[i].id, &entities[target_idx].id);
            shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
            relation_count += 1;
        }
    }
    
    assert_eq!(shard.relation_count(), relation_count);
    
    // Verify indices
    for entity in &entities {
        let outgoing = shard.outgoing_from(&entity.id);
        assert_eq!(outgoing.len(), 10);
    }
}

#[test]
fn test_queue_metrics() {
    let shard = create_test_shard();
    
    // Initial state
    assert!(shard.is_queue_empty());
    assert_eq!(shard.queue_len(), 0);
    assert_eq!(shard.queue_fill_ratio(), 0.0);
    
    // Check capacity is reasonable
    assert!(shard.queue_capacity() > 0);
}

#[tokio::test]
async fn test_async_write_with_completion() {
    let shard = create_test_shard_arc();
    shard.start();
    
    let entity = create_test_entity("AsyncEntity");
    let entity_id = entity.id.clone();
    
    // Use write_sync which waits for completion
    let op = GraphWriteOp::PutEntity(entity);
    let result = shard.write_sync(op).await.unwrap();
    
    assert!(result.success);
    assert!(shard.contains_entity(&entity_id));
}

#[tokio::test]
async fn test_high_concurrency_writes() {
    let shard = create_test_shard_arc();
    shard.start();
    
    const CONCURRENT_WRITES: usize = 100;
    
    // Spawn multiple concurrent writes
    let mut handles = Vec::new();
    for i in 0..CONCURRENT_WRITES {
        let shard_clone = Arc::clone(&shard);
        let handle = tokio::spawn(async move {
            let entity = create_test_entity(&format!("ConcurrentEntity{}", i));
            let op = GraphWriteOp::PutEntity(entity);
            shard_clone.write_sync(op).await
        });
        handles.push(handle);
    }
    
    // Wait for all writes
    for handle in handles {
        let result = handle.await.unwrap();
        assert!(result.is_ok());
    }
    
    // Verify all entities written
    // Note: Due to batching, we need to wait a bit for all writes to complete
    tokio::time::sleep(Duration::from_millis(100)).await;
    assert_eq!(shard.entity_count(), CONCURRENT_WRITES);
}

#[tokio::test]
async fn test_write_queue_try_write() {
    let shard = create_test_shard_arc();
    // Don't start worker - queue will fill up
    
    // Try write should succeed initially
    let entity = create_test_entity("TryWriteEntity");
    let op = GraphWriteOp::PutEntity(entity);
    let result = shard.try_write(op);
    
    // Should succeed as queue is not full yet
    assert!(result.is_ok());
}

#[tokio::test]
async fn test_queue_fill_ratio_tracking() {
    let shard = create_test_shard_with_capacity(10);
    // Don't start worker - items will accumulate
    
    // Fill queue partially
    for i in 0..5 {
        let entity = create_test_entity(&format!("FillEntity{}", i));
        let op = GraphWriteOp::PutEntity(entity);
        let _ = shard.try_write(op);
    }
    
    // Check fill ratio
    let ratio = shard.queue_fill_ratio();
    assert!(ratio > 0.0);
    assert!(ratio <= 1.0);
}

#[tokio::test]
async fn test_queue_full_rejection() {
    let shard = create_test_shard_with_capacity(5);
    // Don't start worker - queue will fill up
    
    // Fill the queue
    for i in 0..5 {
        let entity = create_test_entity(&format!("FullEntity{}", i));
        let op = GraphWriteOp::PutEntity(entity);
        let _ = shard.try_write(op);
    }
    
    // Next write should fail
    let entity = create_test_entity("OverflowEntity");
    let op = GraphWriteOp::PutEntity(entity);
    let result = shard.try_write(op);
    
    assert!(matches!(result, Err(GraphStoreError::QueueFull)));
}

// ============================================================================
// WAL Integration Tests (with shard)
// ============================================================================

#[tokio::test]
async fn test_shard_with_wal_enabled() {
    let shard = create_test_shard_with_wal();
    assert!(shard.is_wal_enabled());
    
    shard.start();
    
    // Write entities
    for i in 0..10 {
        let entity = create_test_entity(&format!("WalEntity{}", i));
        let op = GraphWriteOp::PutEntity(entity);
        shard.write_sync(op).await.unwrap();
    }
    
    // Sync WAL
    shard.sync_wal().unwrap();
    
    assert_eq!(shard.entity_count(), 10);
}

#[test]
fn test_shard_wal_can_be_disabled() {
    let dir = tempdir().unwrap();
    let config = GraphShardConfig::new(0, dir.path());
    let metrics = Arc::new(WriteMetrics::new());
    let shard = GraphShard::new(config, metrics);
    
    assert!(!shard.is_wal_enabled());
}

// ============================================================================
// Edge Cases and Regression Tests
// ============================================================================

#[test]
fn test_entity_with_many_properties() {
    let shard = create_test_shard();
    
    let mut entity = create_test_entity("PropsEntity");
    
    // Add many properties
    for i in 0..100 {
        entity.properties.insert(
            format!("prop_{}", i),
            serde_json::json!({
                "index": i,
                "data": format!("value_{}", i),
                "nested": { "a": i, "b": i * 2 }
            })
        );
    }
    
    let entity_id = entity.id.clone();
    shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
    
    let retrieved = shard.get_entity(&entity_id).unwrap();
    assert_eq!(retrieved.properties.len(), 100);
}

#[test]
fn test_relation_with_many_properties() {
    let shard = create_test_shard();
    
    let alice = create_test_entity("Alice");
    let bob = create_test_entity("Bob");
    
    shard.apply_op(&GraphWriteOp::PutEntity(alice.clone())).unwrap();
    shard.apply_op(&GraphWriteOp::PutEntity(bob.clone())).unwrap();
    
    let mut relation = create_test_relation(&alice.id, &bob.id);
    
    // Add many metadata entries
    for i in 0..50 {
        relation.metadata.insert(
            format!("attr_{}", i),
            serde_json::json!(format!("value_{}", i))
        );
    }
    
    let relation_id = relation.id.clone();
    shard.apply_op(&GraphWriteOp::PutRelation(relation)).unwrap();
    
    let retrieved = shard.get_relation(&relation_id).unwrap();
    assert_eq!(retrieved.metadata.len(), 50);
}

#[test]
fn test_multiple_entity_types() {
    let shard = create_test_shard();
    
    let types = [
        entity_types::PERSON,
        entity_types::ITEM,
        entity_types::TAG,
        entity_types::ATTRIBUTE,
        "custom_type_1",
        "custom_type_2",
    ];
    
    // Add 5 entities of each type
    for entity_type in types {
        for i in 0..5 {
            let mut entity = Entity::new(&format!("{}_{}", entity_type, i), entity_type);
            entity.entity_type = entity_type.to_string();
            shard.apply_op(&GraphWriteOp::PutEntity(entity)).unwrap();
        }
    }
    
    // Verify type indices
    for entity_type in types {
        let found = shard.find_entities_by_type(entity_type);
        assert_eq!(found.len(), 5, "Wrong count for type: {}", entity_type);
    }
}

#[test]
fn test_shard_id() {
    let dir = tempdir().unwrap();
    
    // Create shards with different IDs
    for shard_id in 0..5 {
        let config = GraphShardConfig::new(shard_id, dir.path());
        let metrics = Arc::new(WriteMetrics::new());
        let shard = GraphShard::new(config, metrics);
        
        assert_eq!(shard.id(), shard_id);
    }
}

#[test]
fn test_shard_config() {
    let dir = tempdir().unwrap();
    let config = GraphShardConfig::new(42, dir.path());
    let metrics = Arc::new(WriteMetrics::new());
    let shard = GraphShard::new(config, metrics);
    
    assert_eq!(shard.config().id, 42);
}
