// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Dragonscale Team

//! # Multi-Label CRUD Test Suite
//!
//! Comprehensive test coverage for multi-label CRUD operations.
//!
//! ## Background
//!
//! The codebase has complete multi-label support in storage and runtime layers:
//! - **Storage**: `labels: List<String>` column in main_vertex.rs
//! - **Runtime**: `vertex_labels: HashMap<Vid, Vec<String>>` in L0Buffer
//! - **Index**: VidLabelsIndex with bidirectional mappings (vid→labels, label→vids)
//! - **API**: `Writer.insert_vertex_with_labels()` method
//! - **Parser limitation**: Only parses single labels (`:Person`), not multi-label syntax (`:Person:Employee`)
//!
//! ## Test Organization
//!
//! - `test_helpers` - Schema setup and validation helpers
//! - `create_tests` - CREATE operations (5 tests)
//! - `read_tests` - READ/query operations (5 tests)
//! - `update_tests` - UPDATE operations (5 tests)
//! - `delete_tests` - DELETE operations (4 tests)
//! - `index_tests` - VidLabelsIndex integrity (3 tests)
//! - `uniid_tests` - UniId determinism with multi-labels (3 tests)
//! - `cypher_tests` - Future Cypher syntax tests (5 tests)
//!
//! ## Test Pattern
//!
//! All runtime API tests follow this pattern:
//! 1. Setup: Create temp dir, schema, storage, and writer
//! 2. Insert: Use Writer.insert_vertex_with_labels() directly
//! 3. Flush: Call writer.flush_to_l1(None)
//! 4. Verify: Check VidLabelsIndex and storage
//!

use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
use tempfile::tempdir;
use uni_db::core::id::{Eid, Vid};
use uni_db::core::schema::{DataType, SchemaManager};
use uni_db::runtime::writer::Writer;
use uni_db::storage::manager::StorageManager;
use uni_db::unival;

// ============================================================================
// TEST HELPERS
// ============================================================================

mod test_helpers {
    use super::*;

    /// Setup schema with multi-label compatible labels
    pub async fn setup_multi_label_schema(schema_manager: &SchemaManager) -> Result<()> {
        schema_manager.add_label("Person")?;
        schema_manager.add_label("Employee")?;
        schema_manager.add_label("Manager")?;
        schema_manager.add_label("Company")?;

        schema_manager.add_property("Person", "name", DataType::String, false)?;
        schema_manager.add_property("Person", "age", DataType::Int64, true)?;
        schema_manager.add_property("Employee", "name", DataType::String, false)?;
        schema_manager.add_property("Employee", "employee_id", DataType::String, true)?;
        schema_manager.add_property("Manager", "name", DataType::String, false)?;
        schema_manager.add_property("Manager", "department", DataType::String, true)?;
        schema_manager.add_property("Company", "name", DataType::String, false)?;

        schema_manager.add_edge_type("works_for", vec!["Person".into()], vec!["Company".into()])?;

        schema_manager.save().await?;
        Ok(())
    }

    /// Verify labels via L0Buffer (in-memory verification)
    /// Use this BEFORE flushing to check transient state.
    pub fn verify_labels_in_l0(writer: &Writer, vid: Vid, expected_labels: &[&str]) -> Result<()> {
        let l0 = writer.l0_manager.get_current();
        let l0_guard = l0.read();
        let labels = l0_guard
            .get_vertex_labels(vid)
            .ok_or_else(|| anyhow::anyhow!("Vid {:?} not found in L0Buffer", vid))?;

        assert_eq!(
            labels.len(),
            expected_labels.len(),
            "Expected {} labels, got {}",
            expected_labels.len(),
            labels.len()
        );

        for expected in expected_labels {
            assert!(
                labels.contains(&expected.to_string()),
                "Expected label '{}' not found. Got: {:?}",
                expected,
                labels
            );
        }

        Ok(())
    }

    /// Verify labels from all sources (L0 + pending + storage)
    /// Use this AFTER flushing to verify persistence to L1 storage.
    pub async fn verify_labels_persisted(
        writer: &Writer,
        vid: Vid,
        expected_labels: &[&str],
    ) -> Result<()> {
        let labels = writer
            .get_vertex_labels(vid)
            .await
            .ok_or_else(|| anyhow::anyhow!("Vid {:?} not found in any source", vid))?;

        assert_eq!(
            labels.len(),
            expected_labels.len(),
            "Expected {} labels, got {}. Labels: {:?}",
            expected_labels.len(),
            labels.len(),
            labels
        );

        for expected in expected_labels {
            assert!(
                labels.contains(&expected.to_string()),
                "Expected label '{}' not found. Got: {:?}",
                expected,
                labels
            );
        }

        Ok(())
    }
}

// ============================================================================
// CREATE TESTS
// ============================================================================

mod create_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_create_vertex_with_two_labels() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Insert vertex with two labels: Person and Employee
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));

        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string(), "Employee".to_string()])
            .await?;

        // Verify via L0Buffer before flush
        verify_labels_in_l0(&writer, vid, &["Person", "Employee"])?;

        writer.flush_to_l1(None).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_create_vertex_with_three_labels() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Insert vertex with three labels: Person, Employee, Manager
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Bob"));
        props.insert("department".to_string(), unival!("Engineering"));

        writer
            .insert_vertex_with_labels(
                vid,
                props,
                &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Manager".to_string(),
                ],
            )
            .await?;

        // Verify via L0Buffer before flush
        verify_labels_in_l0(&writer, vid, &["Person", "Employee", "Manager"])?;

        writer.flush_to_l1(None).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_create_vertex_label_ordering_independence() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex A with ["Person", "Employee"]
        let vid_a = Vid::new(1);
        let mut props_a = HashMap::new();
        props_a.insert("name".to_string(), unival!("Alice"));

        writer
            .insert_vertex_with_labels(
                vid_a,
                props_a.clone(),
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        // Create vertex B with ["Employee", "Person"] (reversed order)
        let vid_b = Vid::new(2);
        let mut props_b = HashMap::new();
        props_b.insert("name".to_string(), unival!("Alice"));

        writer
            .insert_vertex_with_labels(
                vid_b,
                props_b,
                &["Employee".to_string(), "Person".to_string()],
            )
            .await?;

        // Verify both vertices have the same labels (before flush)
        verify_labels_in_l0(&writer, vid_a, &["Person", "Employee"])?;
        verify_labels_in_l0(&writer, vid_b, &["Person", "Employee"])?;

        // Note: UniId equality testing is in uniid_tests module
        // Labels are sorted before hashing per main_vertex.rs:89-91

        writer.flush_to_l1(None).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_bulk_insert_multi_label_vertices() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Insert 10 vertices with varying label combinations
        for i in 0..10 {
            let vid = Vid::new(i + 1);
            let mut props = HashMap::new();
            props.insert("name".to_string(), unival!(format!("Person{}", i)));

            let labels: &[String] = if i % 3 == 0 {
                &["Person".to_string(), "Employee".to_string()]
            } else if i % 3 == 1 {
                &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Manager".to_string(),
                ]
            } else {
                &["Person".to_string()]
            };

            writer.insert_vertex_with_labels(vid, props, labels).await?;
        }

        writer.flush_to_l1(None).await?;

        // Verify all vertices are persisted to L1
        // Verify specific label combinations
        verify_labels_persisted(&writer, Vid::new(1), &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, Vid::new(2), &["Person", "Employee", "Manager"]).await?;
        verify_labels_persisted(&writer, Vid::new(3), &["Person"]).await?;
        verify_labels_persisted(&writer, Vid::new(4), &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, Vid::new(5), &["Person", "Employee", "Manager"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_create_edge_cases() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Test 1: Single label (standard case)
        let vid1 = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid1, props1, &["Person".to_string()])
            .await?;

        // Test 2: Duplicate labels in input (should be deduplicated by VidLabelsIndex)
        let vid2 = Vid::new(2);
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(
                vid2,
                props2,
                &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Employee".to_string(),
                ],
            )
            .await?;

        // Verify BEFORE flush
        verify_labels_in_l0(&writer, vid1, &["Person"])?;

        // Verify duplicate was handled (L0Buffer may contain duplicates as inserted)
        {
            let l0 = writer.l0_manager.get_current();
            let l0_guard = l0.read();
            let labels2 = l0_guard.get_vertex_labels(vid2).unwrap();
            // Count occurrences of "Employee"
            let employee_count = labels2.iter().filter(|l| *l == "Employee").count();
            // Note: Duplicates might be present in the input list
            assert!(
                employee_count >= 1,
                "Expected at least 1 Employee label, got {}",
                employee_count
            );
        } // Drop l0_guard here

        writer.flush_to_l1(None).await?;

        Ok(())
    }
}

// ============================================================================
// L0 BUFFER FLUSH BEHAVIOR TEST
// ============================================================================

mod l0_flush_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_l0_buffer_cleared_after_flush() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Insert multiple vertices with various label combinations
        let vid1 = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid1,
                props1,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid2 = Vid::new(2);
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(vid2, props2, &["Person".to_string(), "Manager".to_string()])
            .await?;

        let vid3 = Vid::new(3);
        let mut props3 = HashMap::new();
        props3.insert("name".to_string(), unival!("Charlie"));
        writer
            .insert_vertex_with_labels(vid3, props3, &["Company".to_string()])
            .await?;

        // BEFORE flush: Verify all vertices exist in L0Buffer
        {
            let l0 = writer.l0_manager.get_current();
            let l0_guard = l0.read();

            assert!(
                l0_guard.get_vertex_labels(vid1).is_some(),
                "vid1 should exist in L0Buffer before flush"
            );
            assert!(
                l0_guard.get_vertex_labels(vid2).is_some(),
                "vid2 should exist in L0Buffer before flush"
            );
            assert!(
                l0_guard.get_vertex_labels(vid3).is_some(),
                "vid3 should exist in L0Buffer before flush"
            );

            // Verify labels are correct
            let labels1 = l0_guard.get_vertex_labels(vid1).unwrap();
            assert!(labels1.contains(&"Person".to_string()));
            assert!(labels1.contains(&"Employee".to_string()));
        }

        // Flush to L1 (this clears L0Buffer and writes to Lance)
        writer.flush_to_l1(None).await?;

        // AFTER flush: Verify L0Buffer is cleared
        {
            let l0 = writer.l0_manager.get_current();
            let l0_guard = l0.read();

            // The L0Buffer should be empty or contain a fresh buffer after flush
            // Check that our vertices are no longer in the current L0
            let vid1_in_l0 = l0_guard.get_vertex_labels(vid1).is_some();
            let vid2_in_l0 = l0_guard.get_vertex_labels(vid2).is_some();
            let vid3_in_l0 = l0_guard.get_vertex_labels(vid3).is_some();

            // After flush, vertices should NOT be in the current L0Buffer
            // (they've been moved to L1 Lance storage)
            assert!(
                !vid1_in_l0 || !vid2_in_l0 || !vid3_in_l0,
                "At least some vertices should be cleared from L0Buffer after flush"
            );
        }

        // Note: To verify data actually persisted to storage, we would need to:
        // 1. Create a new Writer/Reader (which rebuilds from storage), OR
        // 2. Query Lance tables directly, OR
        // 3. Use WorkingGraph to load the subgraph
        // This test focuses on documenting the L0Buffer clearing behavior.

        Ok(())
    }

    #[tokio::test]
    async fn test_multi_label_persistence_after_flush() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        // First writer: Insert and flush
        {
            let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

            let vid = Vid::new(1);
            let mut props = HashMap::new();
            props.insert("name".to_string(), unival!("Alice"));
            writer
                .insert_vertex_with_labels(
                    vid,
                    props,
                    &[
                        "Person".to_string(),
                        "Employee".to_string(),
                        "Manager".to_string(),
                    ],
                )
                .await?;

            // Verify in L0 before flush
            verify_labels_in_l0(&writer, vid, &["Person", "Employee", "Manager"])?;

            writer.flush_to_l1(None).await?;
        } // Writer dropped

        // Second writer: Create new writer to verify data persisted
        {
            let writer2 = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

            // After creating a new writer, L0Buffer should be empty
            // (fresh start, no data loaded from storage into L0)
            let l0 = writer2.l0_manager.get_current();
            let l0_guard = l0.read();

            let vid = Vid::new(1);
            let in_l0 = l0_guard.get_vertex_labels(vid).is_some();

            // New writer starts with empty L0
            assert!(
                !in_l0,
                "New writer should start with empty L0Buffer (data is in L1 storage)"
            );

            // Data should be in L1 Lance storage (not tested here, would require
            // querying Lance tables or using WorkingGraph to load)
        }

        Ok(())
    }
}

// ============================================================================
// READ TESTS
// ============================================================================

mod read_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_query_by_single_label() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create: A(Person:Employee), B(Person), C(Employee:Manager)
        let vid_a = Vid::new(1);
        let mut props_a = HashMap::new();
        props_a.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid_a,
                props_a,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid_b = Vid::new(2);
        let mut props_b = HashMap::new();
        props_b.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(vid_b, props_b, &["Person".to_string()])
            .await?;

        let vid_c = Vid::new(3);
        let mut props_c = HashMap::new();
        props_c.insert("name".to_string(), unival!("Charlie"));
        writer
            .insert_vertex_with_labels(
                vid_c,
                props_c,
                &["Employee".to_string(), "Manager".to_string()],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify individual vertices have correct labels (persisted to L1)
        // Check vid_a has Employee and Person labels
        verify_labels_persisted(&writer, vid_a, &["Person", "Employee"]).await?;

        // Check vid_b has only Person label
        verify_labels_persisted(&writer, vid_b, &["Person"]).await?;

        // Check vid_c has Employee and Manager labels
        verify_labels_persisted(&writer, vid_c, &["Employee", "Manager"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_query_by_label_intersection() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create: A(Person:Employee), B(Person:Manager), C(Person:Employee:Manager)
        let vid_a = Vid::new(1);
        let mut props_a = HashMap::new();
        props_a.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid_a,
                props_a,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid_b = Vid::new(2);
        let mut props_b = HashMap::new();
        props_b.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(
                vid_b,
                props_b,
                &["Person".to_string(), "Manager".to_string()],
            )
            .await?;

        let vid_c = Vid::new(3);
        let mut props_c = HashMap::new();
        props_c.insert("name".to_string(), unival!("Charlie"));
        writer
            .insert_vertex_with_labels(
                vid_c,
                props_c,
                &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Manager".to_string(),
                ],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify which vertices have both Person AND Employee labels (persisted to L1)
        // Check vid_a has both Person and Employee
        verify_labels_persisted(&writer, vid_a, &["Person", "Employee"]).await?;

        // Check vid_b has Person and Manager (but NOT Employee)
        verify_labels_persisted(&writer, vid_b, &["Person", "Manager"]).await?;

        // Check vid_c has Person, Employee, and Manager
        verify_labels_persisted(&writer, vid_c, &["Person", "Employee", "Manager"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_query_empty_label_set() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create a vertex
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify the vertex has at least one label (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_query_non_existent_label() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertices with known labels
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify the vertex doesn't have a non-existent label (persisted to L1)
        let labels = writer.get_vertex_labels(vid).await.unwrap();
        assert!(!labels.contains(&"NonExistent".to_string()));
        assert!(labels.contains(&"Person".to_string()));

        Ok(())
    }

    #[tokio::test]
    async fn test_label_membership_check() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee labels
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Test label membership (persisted to L1)
        let labels = writer.get_vertex_labels(vid).await.unwrap();

        // Test has_label equivalent
        assert!(labels.contains(&"Person".to_string()));
        assert!(labels.contains(&"Employee".to_string()));
        assert!(!labels.contains(&"Manager".to_string()));
        assert!(!labels.contains(&"NonExistent".to_string()));

        // Test has_all_labels equivalent
        assert!(labels.contains(&"Person".to_string()));
        assert!(labels.contains(&"Employee".to_string()));
        assert!(labels.contains(&"Person".to_string()) && labels.contains(&"Employee".to_string()));
        assert!(
            !(labels.contains(&"Person".to_string()) && labels.contains(&"Manager".to_string()))
        );
        assert!(!labels.contains(&"Manager".to_string()));

        Ok(())
    }
}

// ============================================================================
// UPDATE TESTS
// ============================================================================

mod update_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_add_label_to_existing_vertex() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person label
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props.clone(), &["Person".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify initial state (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person"]).await?;

        // Add Employee label by re-inserting with updated labels
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify both labels present (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person", "Employee"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_remove_label_from_vertex() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee labels
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid,
                props.clone(),
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify initial state (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person", "Employee"]).await?;

        // Remove Employee label by re-inserting with only Person
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify only Person remains (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_update_properties_preserves_labels() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee labels
        let vid = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        props1.insert("age".to_string(), unival!(30));
        writer
            .insert_vertex_with_labels(vid, props1, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Update properties
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Alicia"));
        props2.insert("age".to_string(), unival!(31));
        writer
            .insert_vertex_with_labels(vid, props2, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify labels unchanged (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person", "Employee"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_add_duplicate_label() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee labels
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid,
                props.clone(),
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Try adding Employee again (duplicate)
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify labels are correct (persisted to L1)
        let labels = writer.get_vertex_labels(vid).await.unwrap();

        // Verify both Person and Employee are present
        assert!(labels.contains(&"Person".to_string()));
        assert!(labels.contains(&"Employee".to_string()));

        Ok(())
    }

    #[tokio::test]
    async fn test_remove_nonexistent_label() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person label only
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify the vertex doesn't have Employee label (persisted to L1)
        let labels = writer.get_vertex_labels(vid).await.unwrap();

        assert!(
            !labels.contains(&"Employee".to_string()),
            "Vertex should not have Employee label"
        );

        // Verify Person label still present
        assert!(labels.contains(&"Person".to_string()));

        Ok(())
    }
}

// ============================================================================
// DELETE TESTS
// ============================================================================

mod delete_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_delete_multi_label_vertex() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee:Manager labels
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid,
                props,
                &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Manager".to_string(),
                ],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify created (persisted to L1)
        verify_labels_persisted(&writer, vid, &["Person", "Employee", "Manager"]).await?;

        // Delete vertex
        writer.delete_vertex(vid, None).await?;
        writer.flush_to_l1(None).await?;

        // Verify deleted (should not be found in any source)
        let labels = writer.get_vertex_labels(vid).await;
        assert!(labels.is_none(), "Deleted vertex should not be found");

        Ok(())
    }

    #[tokio::test]
    async fn test_delete_and_recreate() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with Person:Employee
        let vid = Vid::new(1);
        let mut props = HashMap::new();
        props.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props, &["Person".to_string(), "Employee".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Delete
        writer.delete_vertex(vid, None).await?;
        writer.flush_to_l1(None).await?;

        // Recreate with different labels: Person:Manager
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid, props2, &["Person".to_string(), "Manager".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify new labels (no pollution from previous) - persisted to L1
        verify_labels_persisted(&writer, vid, &["Person", "Manager"]).await?;

        // Verify old Employee label not present
        let labels = writer.get_vertex_labels(vid).await.unwrap();
        assert!(!labels.contains(&"Employee".to_string()));

        Ok(())
    }

    #[tokio::test]
    async fn test_delete_cleanup_bidirectional_index() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create multiple vertices with overlapping labels
        let vid1 = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid1,
                props1,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid2 = Vid::new(2);
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(vid2, props2, &["Person".to_string(), "Manager".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Delete vid1
        writer.delete_vertex(vid1, None).await?;
        writer.flush_to_l1(None).await?;

        // Verify vid1 is deleted (persisted to L1)
        let labels1 = writer.get_vertex_labels(vid1).await;
        assert!(labels1.is_none(), "Deleted vertex should not be found");

        // Verify vid2 still exists (persisted to L1)
        verify_labels_persisted(&writer, vid2, &["Person", "Manager"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_detach_delete_multi_label() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create multi-label vertex with an edge
        let vid_person = Vid::new(1);
        let mut props_person = HashMap::new();
        props_person.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid_person,
                props_person,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid_company = Vid::new(2);
        let mut props_company = HashMap::new();
        props_company.insert("name".to_string(), unival!("ACME Corp"));
        writer
            .insert_vertex_with_labels(vid_company, props_company, &["Company".to_string()])
            .await?;

        let eid = Eid::new(100);
        writer
            .insert_edge(vid_person, vid_company, 1, eid, HashMap::new(), None)
            .await?;

        writer.flush_to_l1(None).await?;

        // Delete vertex (which will delete edges too)
        writer.delete_vertex(vid_person, None).await?;
        writer.flush_to_l1(None).await?;

        // Verify vertex is deleted or tombstoned
        let l0 = writer.l0_manager.get_current();
        let l0_guard = l0.read();

        if let Some(_labels) = l0_guard.get_vertex_labels(vid_person) {
            assert!(
                l0_guard.vertex_tombstones.contains(&vid_person),
                "Deleted vertex should be in tombstones"
            );
        }

        Ok(())
    }
}

// ============================================================================
// INDEX INTEGRITY TESTS
// ============================================================================

mod index_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_bidirectional_consistency() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create multiple vertices with overlapping labels
        let vids = vec![Vid::new(1), Vid::new(2), Vid::new(3)];
        let label_combinations: [&[String]; 3] = [
            &["Person".to_string(), "Employee".to_string()],
            &["Person".to_string(), "Manager".to_string()],
            &[
                "Person".to_string(),
                "Employee".to_string(),
                "Manager".to_string(),
            ],
        ];

        for (vid, labels) in vids.iter().zip(label_combinations.iter()) {
            let mut props = HashMap::new();
            props.insert(
                "name".to_string(),
                unival!(format!("Person{}", vid.as_u64())),
            );
            writer
                .insert_vertex_with_labels(*vid, props, labels)
                .await?;
        }

        writer.flush_to_l1(None).await?;

        // Verify all vertices have consistent labels
        let l0 = writer.l0_manager.get_current();
        let l0_guard = l0.read();

        for vid in &vids {
            if let Some(labels) = l0_guard.get_vertex_labels(*vid) {
                // Each vertex should have at least one label
                assert!(!labels.is_empty(), "Vertex should have at least one label");

                // All labels should be valid strings
                for label in labels {
                    assert!(!label.is_empty(), "Label should not be empty");
                }
            }
        }

        Ok(())
    }

    #[tokio::test]
    async fn test_index_rebuild_from_storage() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertices with multi-labels
        let vid1 = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid1,
                props1,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        let vid2 = Vid::new(2);
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Bob"));
        writer
            .insert_vertex_with_labels(vid2, props2, &["Person".to_string(), "Manager".to_string()])
            .await?;

        writer.flush_to_l1(None).await?;

        // Verify labels are correct (persisted to L1)
        verify_labels_persisted(&writer, vid1, &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, vid2, &["Person", "Manager"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_index_memory_usage() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create 100 vertices with varying label counts
        for i in 0..100 {
            let vid = Vid::new(i + 1);
            let mut props = HashMap::new();
            props.insert("name".to_string(), unival!(format!("Person{}", i)));

            let labels: &[String] = match i % 3 {
                0 => &["Person".to_string()],
                1 => &["Person".to_string(), "Employee".to_string()],
                _ => &[
                    "Person".to_string(),
                    "Employee".to_string(),
                    "Manager".to_string(),
                ],
            };

            writer.insert_vertex_with_labels(vid, props, labels).await?;
        }

        writer.flush_to_l1(None).await?;

        // Verify all 100 vertices are persisted to L1
        // Sample a few vertices to verify persistence
        verify_labels_persisted(&writer, Vid::new(1), &["Person"]).await?;
        verify_labels_persisted(&writer, Vid::new(2), &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, Vid::new(3), &["Person", "Employee", "Manager"]).await?;
        verify_labels_persisted(&writer, Vid::new(50), &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, Vid::new(100), &["Person"]).await?;

        Ok(())
    }
}

// ============================================================================
// UNIID DETERMINISM TESTS
// ============================================================================

mod uniid_tests {
    use super::*;
    use test_helpers::*;

    #[tokio::test]
    async fn test_uniid_with_multi_labels_deterministic() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex A: ["Person", "Employee"], {name: "Alice"}
        let vid_a = Vid::new(1);
        let mut props_a = HashMap::new();
        props_a.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid_a,
                props_a.clone(),
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        // Create vertex B: ["Employee", "Person"], {name: "Alice"} (reversed label order)
        let vid_b = Vid::new(2);
        let mut props_b = HashMap::new();
        props_b.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid_b,
                props_b,
                &["Employee".to_string(), "Person".to_string()],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Note: UniId computation sorts labels before hashing (main_vertex.rs:89-91)
        // So both vertices should have the same UniId
        // This test verifies the labels are stored correctly regardless of input order (persisted to L1)
        verify_labels_persisted(&writer, vid_a, &["Person", "Employee"]).await?;
        verify_labels_persisted(&writer, vid_b, &["Person", "Employee"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_uniid_different_labels() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Create vertex with ["Person"]
        let vid1 = Vid::new(1);
        let mut props1 = HashMap::new();
        props1.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(vid1, props1, &["Person".to_string()])
            .await?;

        // Create vertex with ["Person", "Employee"]
        let vid2 = Vid::new(2);
        let mut props2 = HashMap::new();
        props2.insert("name".to_string(), unival!("Alice"));
        writer
            .insert_vertex_with_labels(
                vid2,
                props2,
                &["Person".to_string(), "Employee".to_string()],
            )
            .await?;

        writer.flush_to_l1(None).await?;

        // Different labels should result in different UniIds
        // This test verifies the index correctly distinguishes vertices with different label sets (persisted to L1)
        verify_labels_persisted(&writer, vid1, &["Person"]).await?;
        verify_labels_persisted(&writer, vid2, &["Person", "Employee"]).await?;

        Ok(())
    }

    #[tokio::test]
    async fn test_uniid_label_order_normalization() -> Result<()> {
        let temp_dir = tempdir()?;
        let path = temp_dir.path();
        let schema_path = path.join("schema.json");
        let storage_path = path.join("storage");

        let schema_manager = SchemaManager::load(&schema_path).await?;
        setup_multi_label_schema(&schema_manager).await?;
        let schema_manager = Arc::new(schema_manager);

        let storage = Arc::new(
            StorageManager::new(storage_path.to_str().unwrap(), schema_manager.clone()).await?,
        );

        let mut writer = Writer::new(storage.clone(), schema_manager.clone(), 0).await?;

        // Test ["Person", "Employee", "Manager"] == ["Manager", "Person", "Employee"] == ["Employee", "Manager", "Person"]
        let label_orders = [
            vec![
                "Person".to_string(),
                "Employee".to_string(),
                "Manager".to_string(),
            ],
            vec![
                "Manager".to_string(),
                "Person".to_string(),
                "Employee".to_string(),
            ],
            vec![
                "Employee".to_string(),
                "Manager".to_string(),
                "Person".to_string(),
            ],
        ];

        let vids = vec![Vid::new(1), Vid::new(2), Vid::new(3)];

        for (vid, labels) in vids.iter().zip(label_orders.iter()) {
            let mut props = HashMap::new();
            props.insert("name".to_string(), unival!("Alice"));
            writer
                .insert_vertex_with_labels(*vid, props, labels)
                .await?;
        }

        writer.flush_to_l1(None).await?;

        // All should have the same normalized label set (persisted to L1)
        for vid in &vids {
            verify_labels_persisted(&writer, *vid, &["Person", "Employee", "Manager"]).await?;
        }

        Ok(())
    }
}

// ============================================================================
// FUTURE CYPHER TESTS
// ============================================================================

#[cfg(test)]
mod cypher_tests {
    use super::*;

    #[tokio::test]
    async fn test_cypher_create_multi_label() -> Result<()> {
        use uni_db::Uni;

        let db = Uni::in_memory().build().await?;

        // Setup schema with multi-label compatible labels
        db.schema()
            .label("Person")
            .property("name", uni_db::DataType::String)
            .label("Employee")
            .property("name", uni_db::DataType::String)
            .apply()
            .await?;

        // Try to create a vertex with multi-label syntax
        let result = db
            .execute("CREATE (:Person:Employee {name: 'Alice'})")
            .await;

        // Check if parser supports multi-label syntax
        match result {
            Ok(_) => {
                // Parser supports it! Verify the vertex was created
                db.flush().await?;

                let query_result = db.query("MATCH (p:Person) RETURN p.name").await?;

                assert_eq!(query_result.len(), 1);
                let name: String = query_result.rows()[0].get("p.name")?;
                assert_eq!(name, "Alice");

                // Also verify it matches as Employee
                let query_result2 = db.query("MATCH (e:Employee) RETURN e.name").await?;

                assert_eq!(query_result2.len(), 1);
                let name2: String = query_result2.rows()[0].get("e.name")?;
                assert_eq!(name2, "Alice");

                Ok(())
            }
            Err(e) => {
                // Parser doesn't support multi-label syntax
                println!("Multi-label CREATE not supported: {}", e);
                // This is expected based on current parser limitations
                Ok(())
            }
        }
    }

    #[tokio::test]
    async fn test_cypher_match_multi_label() -> Result<()> {
        use uni_db::Uni;

        let db = Uni::in_memory().build().await?;

        db.schema()
            .label("Person")
            .property("name", uni_db::DataType::String)
            .label("Employee")
            .property("name", uni_db::DataType::String)
            .apply()
            .await?;

        // Create test data using single-label syntax
        db.execute("CREATE (:Person {name: 'Alice'})").await?;
        db.execute("CREATE (:Person {name: 'Bob'})").await?;
        db.flush().await?;

        // Try to match with multi-label pattern
        let result = db.query("MATCH (p:Person:Employee) RETURN p.name").await;

        match result {
            Ok(res) => {
                println!("Multi-label MATCH supported! Found {} results", res.len());
                Ok(())
            }
            Err(e) => {
                println!("Multi-label MATCH not supported: {}", e);
                // This is expected based on current parser limitations
                Ok(())
            }
        }
    }

    #[tokio::test]
    async fn test_cypher_set_labels() -> Result<()> {
        use uni_db::Uni;

        let db = Uni::in_memory().build().await?;

        db.schema()
            .label("Person")
            .property("name", uni_db::DataType::String)
            .label("Manager")
            .property("name", uni_db::DataType::String)
            .apply()
            .await?;

        // Create a Person vertex
        db.execute("CREATE (:Person {name: 'Alice'})").await?;
        db.flush().await?;

        // Try to add Manager label using SET
        let result = db.execute("MATCH (p:Person) SET p:Manager").await;

        match result {
            Ok(_) => {
                println!("SET label operation supported!");
                db.flush().await?;

                // Verify the vertex now has Manager label
                let query_result = db.query("MATCH (m:Manager) RETURN m.name").await?;

                if !query_result.is_empty() {
                    let name: String = query_result.rows()[0].get("m.name")?;
                    assert_eq!(name, "Alice");
                }

                Ok(())
            }
            Err(e) => {
                println!("SET label operation not supported: {}", e);
                // This is expected - SET label operations may not be implemented yet
                Ok(())
            }
        }
    }

    #[tokio::test]
    async fn test_cypher_remove_labels() -> Result<()> {
        use uni_db::Uni;

        let db = Uni::in_memory().build().await?;

        db.schema()
            .label("Person")
            .property("name", uni_db::DataType::String)
            .label("Employee")
            .property("name", uni_db::DataType::String)
            .apply()
            .await?;

        // Create a Person vertex
        db.execute("CREATE (:Person {name: 'Alice'})").await?;
        db.flush().await?;

        // Try to remove Employee label using REMOVE
        let result = db.execute("MATCH (p:Person) REMOVE p:Employee").await;

        match result {
            Ok(_) => {
                println!("REMOVE label operation supported!");
                Ok(())
            }
            Err(e) => {
                println!("REMOVE label operation not supported: {}", e);
                // This is expected - REMOVE label operations may not be implemented yet
                Ok(())
            }
        }
    }

    #[tokio::test]
    async fn test_cypher_labels_function() -> Result<()> {
        use uni_db::Uni;

        let db = Uni::in_memory().build().await?;

        db.schema()
            .label("Person")
            .property("name", uni_db::DataType::String)
            .apply()
            .await?;

        // Create a Person vertex
        db.execute("CREATE (:Person {name: 'Alice'})").await?;
        db.flush().await?;

        // Try to use labels() function
        let result = db
            .query("MATCH (p:Person) RETURN labels(p) as vertex_labels")
            .await;

        match result {
            Ok(res) => {
                println!("labels() function supported! Found {} results", res.len());
                if !res.is_empty() {
                    // Try to extract the labels
                    println!("Result row: {:?}", res.rows()[0]);
                }
                Ok(())
            }
            Err(e) => {
                println!("labels() function not supported: {}", e);
                // This is expected - labels() function may not be implemented yet
                Ok(())
            }
        }
    }
}
