//! Distributed storage backends for LightningStor
//!
//! This crate provides distributed storage backends that implement redundancy
//! through either Reed-Solomon erasure coding or N-way replication.
//!
//! # Features
//!
//! - **Erasure Coding**: Storage-efficient redundancy using Reed-Solomon codes.
//!   Configurable data/parity shard ratio (e.g., 4+2 for 1.5x overhead with 2-node
//!   fault tolerance).
//!
//! - **Replication**: Performance-oriented redundancy with N-way replication.
//!   Simple and fast, with configurable read/write quorums.
//!
//! - **Pluggable Node Management**: Support for static node configuration or
//!   dynamic discovery via ChainFire.
//!
//! - **Placement Strategies**: Consistent hashing, random, and round-robin
//!   node selection strategies.
//!
//! # Example
//!
//! ```rust,no_run
//! use lightningstor_distributed::{
//!     config::{DistributedConfig, RedundancyMode},
//!     backends::ErasureCodedBackend,
//!     node::StaticNodeRegistry,
//! };
//! use std::sync::Arc;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! // Configure 4+2 erasure coding
//! let config = DistributedConfig {
//!     redundancy: RedundancyMode::ErasureCoded {
//!         data_shards: 4,
//!         parity_shards: 2,
//!     },
//!     node_endpoints: vec![
//!         "http://node1:9002".into(),
//!         "http://node2:9002".into(),
//!         "http://node3:9002".into(),
//!         "http://node4:9002".into(),
//!         "http://node5:9002".into(),
//!         "http://node6:9002".into(),
//!     ],
//!     ..Default::default()
//! };
//!
//! // Create node registry
//! let registry = Arc::new(
//!     StaticNodeRegistry::new(&config.node_endpoints).await?
//! );
//!
//! // Create erasure-coded backend
//! let backend = ErasureCodedBackend::new(config, registry).await?;
//!
//! // Use the backend via StorageBackend trait
//! # Ok(())
//! # }
//! ```

pub mod backends;
pub mod chunk;
pub mod config;
pub mod erasure;
pub mod node;
pub mod placement;
pub mod repair;

// Re-export commonly used types
pub use backends::{ErasureCodedBackend, ReplicatedBackend};
pub use config::{BucketStorageConfig, ChunkConfig, DistributedConfig, RedundancyMode};
pub use node::{MockNodeClient, MockNodeRegistry, NodeRegistry, StaticNodeRegistry};
pub use placement::{ConsistentHashSelector, NodeSelector, RandomSelector, RoundRobinSelector};
pub use repair::{RepairQueue, ReplicatedRepairTask};

#[cfg(test)]
mod tests {
    use super::*;
    use bytes::Bytes;
    use lightningstor_storage::StorageBackend;
    use lightningstor_types::ObjectId;
    use std::sync::Arc;

    #[tokio::test]
    async fn test_ec_backend_integration() {
        let config = DistributedConfig {
            redundancy: RedundancyMode::ErasureCoded {
                data_shards: 4,
                parity_shards: 2,
            },
            ..Default::default()
        };

        let registry = Arc::new(MockNodeRegistry::with_nodes(6));
        let backend = ErasureCodedBackend::new(config, registry).await.unwrap();

        let object_id = ObjectId::new();
        let data = Bytes::from("Hello, erasure coded world!");

        backend.put_object(&object_id, data.clone()).await.unwrap();
        let retrieved = backend.get_object(&object_id).await.unwrap();

        assert!(retrieved.len() >= data.len());
        assert_eq!(&retrieved[..data.len()], &data[..]);
    }

    #[tokio::test]
    async fn test_replicated_backend_integration() {
        let config = DistributedConfig {
            redundancy: RedundancyMode::replicated(3),
            ..Default::default()
        };

        let registry = Arc::new(MockNodeRegistry::with_nodes(3));
        let backend = ReplicatedBackend::new(config, registry).await.unwrap();

        let object_id = ObjectId::new();
        let data = Bytes::from("Hello, replicated world!");

        backend.put_object(&object_id, data.clone()).await.unwrap();
        let retrieved = backend.get_object(&object_id).await.unwrap();

        assert_eq!(retrieved, data);
    }

    #[tokio::test]
    async fn test_ec_with_node_failures() {
        let config = DistributedConfig {
            redundancy: RedundancyMode::ErasureCoded {
                data_shards: 4,
                parity_shards: 2,
            },
            ..Default::default()
        };

        let registry = Arc::new(MockNodeRegistry::with_nodes(6));
        let backend = ErasureCodedBackend::new(config, registry.clone())
            .await
            .unwrap();

        let object_id = ObjectId::new();
        let data = Bytes::from(vec![42u8; 1000]);

        backend.put_object(&object_id, data.clone()).await.unwrap();

        // Fail 2 nodes (max tolerable for 4+2)
        let nodes = registry.all_mock_nodes();
        nodes[0].set_healthy(false);
        nodes[1].set_healthy(false);

        let retrieved = backend.get_object(&object_id).await.unwrap();
        assert!(retrieved.len() >= data.len());
        assert_eq!(&retrieved[..data.len()], &data[..]);
    }

    #[tokio::test]
    async fn test_replicated_with_node_failure() {
        let config = DistributedConfig {
            redundancy: RedundancyMode::replicated(3),
            ..Default::default()
        };

        let registry = Arc::new(MockNodeRegistry::with_nodes(3));
        let backend = ReplicatedBackend::new(config, registry.clone())
            .await
            .unwrap();

        let object_id = ObjectId::new();
        let data = Bytes::from(vec![42u8; 1000]);

        backend.put_object(&object_id, data.clone()).await.unwrap();

        // Fail 1 node
        let nodes = registry.all_mock_nodes();
        nodes[0].set_healthy(false);

        let retrieved = backend.get_object(&object_id).await.unwrap();
        assert_eq!(retrieved, data);
    }
}
