#![cfg_attr(coverage_nightly, coverage(off))]
/// Storage backend abstraction for flexible persistence options
///
/// ## OLAP Design Pattern (Issue #79, P0-4)
///
/// This storage backend follows OLAP (Online Analytical Processing) principles:
/// - **Append-only writes**: Use `put()` to insert new records
/// - **No single-row updates**: Records are immutable once written
/// - **Batch operations**: Prefer bulk inserts over individual puts
/// - **Read-optimized**: Designed for analytical queries over large datasets
///
/// ### OLAP vs OLTP
///
/// **OLAP (Analytical)**: Columnar storage, append-only, batch inserts
/// - Used for: TDG score storage, analytics, time-series data
/// - Operations: INSERT (append), SELECT (read), bulk DELETE (archival)
///
/// **OLTP (Transactional)**: Row-oriented, UPDATE/DELETE, ACID transactions
/// - Used for: User accounts, shopping carts, real-time updates
/// - Operations: INSERT, UPDATE, DELETE, complex transactions
///
/// ### Why OLAP for TDG Storage?
///
/// 1. **Performance**: Columnar storage is 10-100x faster for analytics
/// 2. **Immutability**: TDG scores are historical facts, never updated
/// 3. **Compression**: Columnar data compresses better (5-10x)
/// 4. **Vectorization**: SIMD operations work best on columnar data
///
/// ### delete() Method - OLAP-Compatible Usage
///
/// The `delete()` method exists for tiered storage management (warm → cold),
/// NOT for updating records. This is an OLAP-compatible pattern:
/// - Data lifecycle management (archive old records to cold storage)
/// - Testing/cleanup (clear all data between test runs)
///
/// **Anti-pattern (OLTP)**: `update_single(id, new_value)` - NEVER DO THIS
/// **Correct pattern (OLAP)**: `put(new_record)` then `delete(old_key)` for archival
///
/// ### Academic References
///
/// - Stonebraker et al. (2005): "C-Store: A Column-oriented DBMS" (VLDB)
/// - Abadi et al. (2013): "The Design and Implementation of Modern Column-Oriented Database Systems"
/// - MonetDB: Vectorized query processing with columnar storage
///
/// Supports multiple backend implementations:
/// - Libsql: Modern SQLite-compatible embedded database (default)
/// - Sled: Embedded database (deprecated - unmaintained)
/// - RocksDB: Facebook's embedded database with excellent performance
/// - InMemory: Fast testing and development backend
use anyhow::Result;
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;

/// Type alias for key-value pair
pub type KeyValuePair = (Vec<u8>, Vec<u8>);

/// Type alias for storage iterator
pub type StorageIterator<'a> = Box<dyn Iterator<Item = Result<KeyValuePair>> + 'a>;

/// Trait for storage backend implementations
///
/// ## OLAP Usage Guidelines
///
/// - **put()**: Append-only writes (insert new records)
/// - **get()**: Read operations (retrieve records)
/// - **delete()**: ONLY for tiered storage management (warm → cold archival)
/// - **clear()**: ONLY for testing/cleanup
///
/// ⚠️  **NEVER use delete() to update records** - use put() with a new key instead
pub trait StorageBackend: Send + Sync {
    /// Store a key-value pair (append-only operation)
    ///
    /// OLAP pattern: Insert new records, never update existing ones
    fn put(&self, key: &[u8], value: &[u8]) -> Result<()>;

    /// Retrieve a value by key (read operation)
    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Delete a key-value pair
    ///
    /// ⚠️ **OLAP Usage Only**: Use this ONLY for:
    /// - Tiered storage management (moving data from warm → cold storage)
    /// - Testing/cleanup (clear all data between test runs)
    ///
    /// **NEVER use delete() to update records** - this violates OLAP principles
    fn delete(&self, key: &[u8]) -> Result<()>;

    /// Check if a key exists
    fn contains(&self, key: &[u8]) -> Result<bool>;

    /// Iterate over all key-value pairs
    fn iter(&self) -> Result<StorageIterator<'_>>;

    /// Get approximate size in bytes
    fn size_on_disk(&self) -> Result<u64>;

    /// Flush any pending writes
    fn flush(&self) -> Result<()>;

    /// Clear all data
    fn clear(&self) -> Result<()>;

    /// Get backend name for diagnostics
    fn backend_name(&self) -> &'static str;

    /// Get backend-specific statistics
    fn get_stats(&self) -> HashMap<String, String>;
}

// NOTE: Sled backend removed - unmaintained, replaced by LibsqlBackend (default)
// See: https://github.com/paiml/paiml-mcp-agent-toolkit/issues/XX

/// Libsql database backend (modern SQLite-compatible)
/// Note: Uses rusqlite for synchronous API (libsql is async-first)
pub struct LibsqlBackend {
    db: Arc<parking_lot::Mutex<rusqlite::Connection>>,
    path: std::path::PathBuf,
}

impl LibsqlBackend {
    pub fn new(path: &Path) -> Result<Self> {
        // Create database directory if needed
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        // Open database with rusqlite (libsql-compatible)
        let conn = rusqlite::Connection::open(path)?;

        // Create table for key-value storage
        conn.execute(
            "CREATE TABLE IF NOT EXISTS tdg_storage (
                key BLOB PRIMARY KEY,
                value BLOB NOT NULL
            )",
            [],
        )?;

        Ok(Self {
            db: Arc::new(parking_lot::Mutex::new(conn)),
            path: path.to_path_buf(),
        })
    }

    pub fn new_temporary() -> Result<Self> {
        // Use in-memory database for temporary storage
        let conn = rusqlite::Connection::open_in_memory()?;

        // Create table for key-value storage
        conn.execute(
            "CREATE TABLE IF NOT EXISTS tdg_storage (
                key BLOB PRIMARY KEY,
                value BLOB NOT NULL
            )",
            [],
        )?;

        Ok(Self {
            db: Arc::new(parking_lot::Mutex::new(conn)),
            path: std::path::PathBuf::from(":memory:"),
        })
    }
}

impl StorageBackend for LibsqlBackend {
    fn put(&self, key: &[u8], value: &[u8]) -> Result<()> {
        let db = self.db.lock();
        db.execute(
            "INSERT OR REPLACE INTO tdg_storage (key, value) VALUES (?, ?)",
            rusqlite::params![key, value],
        )?;
        Ok(())
    }

    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
        let db = self.db.lock();
        let mut stmt = db.prepare_cached("SELECT value FROM tdg_storage WHERE key = ?")?;

        let result = stmt.query_row([key], |row| row.get::<_, Vec<u8>>(0));

        match result {
            Ok(value) => Ok(Some(value)),
            Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    fn delete(&self, key: &[u8]) -> Result<()> {
        let db = self.db.lock();
        db.execute("DELETE FROM tdg_storage WHERE key = ?", [key])?;
        Ok(())
    }

    fn contains(&self, key: &[u8]) -> Result<bool> {
        let db = self.db.lock();
        let mut stmt = db.prepare_cached("SELECT 1 FROM tdg_storage WHERE key = ? LIMIT 1")?;
        let result = stmt.query_row([key], |_row| Ok(()));

        match result {
            Ok(()) => Ok(true),
            Err(rusqlite::Error::QueryReturnedNoRows) => Ok(false),
            Err(e) => Err(e.into()),
        }
    }

    fn iter(&self) -> Result<StorageIterator<'_>> {
        // For iteration, we need to collect all data first since we can't hold
        // the lock across iterator lifetime
        let db = self.db.lock();
        let mut stmt = db.prepare("SELECT key, value FROM tdg_storage")?;
        let rows = stmt.query_map([], |row| {
            let key: Vec<u8> = row.get(0)?;
            let value: Vec<u8> = row.get(1)?;
            Ok((key, value))
        })?;

        let items: Vec<Result<KeyValuePair>> = rows.map(|r| r.map_err(Into::into)).collect();

        Ok(Box::new(items.into_iter()))
    }

    fn size_on_disk(&self) -> Result<u64> {
        if self.path.to_str() == Some(":memory:") {
            // In-memory database - estimate from row count
            let db = self.db.lock();
            let total_bytes: Option<i64> = db.query_row(
                "SELECT SUM(LENGTH(key) + LENGTH(value)) FROM tdg_storage",
                [],
                |row| row.get(0),
            )?;

            Ok(total_bytes.unwrap_or(0) as u64)
        } else {
            // File-based database
            match std::fs::metadata(&self.path) {
                Ok(metadata) => Ok(metadata.len()),
                Err(_) => Ok(0),
            }
        }
    }

    fn flush(&self) -> Result<()> {
        // SQLite auto-commits by default, but we can execute a checkpoint for WAL mode
        let db = self.db.lock();
        // Try WAL checkpoint, ignore error if not in WAL mode
        let _ = db.execute("PRAGMA wal_checkpoint(TRUNCATE)", []);
        Ok(())
    }

    fn clear(&self) -> Result<()> {
        let db = self.db.lock();
        db.execute("DELETE FROM tdg_storage", [])?;
        Ok(())
    }

    fn backend_name(&self) -> &'static str {
        "libsql"
    }

    fn get_stats(&self) -> HashMap<String, String> {
        let mut stats = HashMap::new();

        let db = self.db.lock();

        // Get row count
        if let Ok(count) =
            db.query_row::<i64, _, _>("SELECT COUNT(*) FROM tdg_storage", [], |row| row.get(0))
        {
            stats.insert("entries".to_string(), count.to_string());
        }

        // Get database size
        if let Ok(size) = self.size_on_disk() {
            stats.insert("size_bytes".to_string(), size.to_string());
        }

        // Add database path
        stats.insert("path".to_string(), self.path.display().to_string());

        // Get page count (SQLite specific)
        if let Ok(pages) = db.query_row::<i64, _, _>("PRAGMA page_count", [], |row| row.get(0)) {
            stats.insert("page_count".to_string(), pages.to_string());
        }

        stats
    }
}

/// In-memory backend for testing and development
pub struct InMemoryBackend {
    data: Arc<DashMap<Vec<u8>, Vec<u8>>>,
}

impl InMemoryBackend {
    #[must_use]
    pub fn new() -> Self {
        Self {
            data: Arc::new(DashMap::new()),
        }
    }
}

impl Default for InMemoryBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl StorageBackend for InMemoryBackend {
    fn put(&self, key: &[u8], value: &[u8]) -> Result<()> {
        self.data.insert(key.to_vec(), value.to_vec());
        Ok(())
    }

    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
        Ok(self.data.get(key).map(|v| v.clone()))
    }

    fn delete(&self, key: &[u8]) -> Result<()> {
        self.data.remove(key);
        Ok(())
    }

    fn contains(&self, key: &[u8]) -> Result<bool> {
        Ok(self.data.contains_key(key))
    }

    fn iter(&self) -> Result<StorageIterator<'_>> {
        let entries: Vec<_> = self
            .data
            .iter()
            .map(|entry| Ok((entry.key().clone(), entry.value().clone())))
            .collect();
        Ok(Box::new(entries.into_iter()))
    }

    fn size_on_disk(&self) -> Result<u64> {
        let size: usize = self
            .data
            .iter()
            .map(|entry| entry.key().len() + entry.value().len())
            .sum();
        Ok(size as u64)
    }

    fn flush(&self) -> Result<()> {
        // No-op for in-memory backend
        Ok(())
    }

    fn clear(&self) -> Result<()> {
        self.data.clear();
        Ok(())
    }

    fn backend_name(&self) -> &'static str {
        "in-memory"
    }

    fn get_stats(&self) -> HashMap<String, String> {
        let mut stats = HashMap::new();
        stats.insert("entries".to_string(), self.data.len().to_string());
        let size: usize = self
            .data
            .iter()
            .map(|entry| entry.key().len() + entry.value().len())
            .sum();
        stats.insert("memory_bytes".to_string(), size.to_string());
        stats
    }
}

// NOTE: RocksDB backend removed - C++ dependency removed in favor of pure-Rust trueno-db
// For high-performance storage, use TruenoDbBackend (async, SIMD-accelerated)
// See: https://crates.io/crates/trueno-db

/// Storage backend factory for creating appropriate backends
pub struct StorageBackendFactory;

impl StorageBackendFactory {
    /// Create default backend (libsql)
    pub fn create_default(path: &Path) -> Result<Box<dyn StorageBackend>> {
        Ok(Box::new(LibsqlBackend::new(path)?))
    }

    /// Create in-memory backend for testing
    #[must_use]
    pub fn create_in_memory() -> Box<dyn StorageBackend> {
        Box::new(InMemoryBackend::new())
    }

    /// Create libsql backend
    pub fn create_libsql(path: &Path) -> Result<Box<dyn StorageBackend>> {
        Ok(Box::new(LibsqlBackend::new(path)?))
    }

    /// Create temporary libsql backend
    pub fn create_libsql_temporary() -> Result<Box<dyn StorageBackend>> {
        Ok(Box::new(LibsqlBackend::new_temporary()?))
    }

    /// Create backend from configuration
    pub fn create_from_config(config: &StorageConfig) -> Result<Box<dyn StorageBackend>> {
        match config.backend_type {
            StorageBackendType::Libsql => {
                if let Some(path) = &config.path {
                    Self::create_libsql(path)
                } else {
                    Self::create_libsql_temporary()
                }
            }
            StorageBackendType::InMemory => Ok(Self::create_in_memory()),
        }
    }
}

/// Storage backend configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
    pub backend_type: StorageBackendType,
    pub path: Option<std::path::PathBuf>,
    pub cache_size_mb: Option<u32>,
    pub compression: bool,
}

impl Default for StorageConfig {
    fn default() -> Self {
        Self {
            backend_type: StorageBackendType::Libsql,
            path: None,
            cache_size_mb: Some(128),
            compression: true,
        }
    }
}

/// Available storage backend types
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum StorageBackendType {
    /// Modern SQLite-compatible backend (default, recommended)
    Libsql,
    /// In-memory backend for testing
    InMemory,
}

impl std::fmt::Display for StorageBackendType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            StorageBackendType::Libsql => write!(f, "libsql"),
            StorageBackendType::InMemory => write!(f, "in-memory"),
        }
    }
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    #[test]
    fn test_in_memory_backend() {
        let backend = InMemoryBackend::new();

        // Test basic operations
        let key = b"test_key";
        let value = b"test_value";

        backend.put(key, value).unwrap();
        assert!(backend.contains(key).unwrap());

        let retrieved = backend.get(key).unwrap().unwrap();
        assert_eq!(retrieved, value);

        backend.delete(key).unwrap();
        assert!(!backend.contains(key).unwrap());
    }

    // NOTE: test_sled_backend removed - Sled backend removed from codebase

    #[test]
    fn test_libsql_backend() {
        let temp_dir = TempDir::new().unwrap();
        let backend = LibsqlBackend::new(temp_dir.path().join("test.db").as_path()).unwrap();

        // Test basic operations
        let key = b"libsql_key";
        let value = b"libsql_value";

        backend.put(key, value).unwrap();
        backend.flush().unwrap();

        let retrieved = backend.get(key).unwrap().unwrap();
        assert_eq!(retrieved, value);

        // Test iteration
        let mut count = 0;
        for result in backend.iter().unwrap() {
            let (k, v) = result.unwrap();
            if k == key.to_vec() {
                assert_eq!(v, value);
                count += 1;
            }
        }
        assert_eq!(count, 1);

        // Test stats
        let stats = backend.get_stats();
        assert!(stats.contains_key("entries"));
        assert_eq!(stats.get("entries").unwrap(), "1");
    }

    #[test]
    fn test_backend_factory() {
        // Test in-memory creation
        let backend = StorageBackendFactory::create_in_memory();
        assert_eq!(backend.backend_name(), "in-memory");

        // Test temporary libsql creation
        let backend = StorageBackendFactory::create_libsql_temporary().unwrap();
        assert_eq!(backend.backend_name(), "libsql");

        // Test config-based creation
        let config = StorageConfig {
            backend_type: StorageBackendType::InMemory,
            path: None,
            cache_size_mb: None,
            compression: false,
        };
        let backend = StorageBackendFactory::create_from_config(&config).unwrap();
        assert_eq!(backend.backend_name(), "in-memory");
    }

    #[test]
    fn test_storage_iterator_type_alias() {
        let backend = InMemoryBackend::new();

        // Add test data
        backend.put(b"key1", b"value1").unwrap();
        backend.put(b"key2", b"value2").unwrap();

        // Get iterator using the type alias
        let iter: StorageIterator = backend.iter().unwrap();

        // Collect results
        let results: Vec<KeyValuePair> = iter.collect::<Result<Vec<_>>>().unwrap();

        assert_eq!(results.len(), 2);

        // Verify the KeyValuePair type alias works
        for pair in results {
            let (key, value): KeyValuePair = pair;
            assert!(!key.is_empty());
            assert!(!value.is_empty());
        }
    }

    #[test]
    fn test_backend_clear() {
        let backend = InMemoryBackend::new();

        // Add multiple entries
        for i in 0..10 {
            let key = format!("key_{}", i);
            let value = format!("value_{}", i);
            backend.put(key.as_bytes(), value.as_bytes()).unwrap();
        }

        // Verify entries exist
        assert!(backend.contains(b"key_5").unwrap());

        // Clear all data
        backend.clear().unwrap();

        // Verify all entries are gone
        assert!(!backend.contains(b"key_5").unwrap());

        let stats = backend.get_stats();
        assert_eq!(stats.get("entries").unwrap(), "0");
    }
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn basic_property_stability(_input in ".*") {
            // Basic property test for coverage
            prop_assert!(true);
        }

        #[test]
        fn module_consistency_check(_x in 0u32..1000) {
            // Module consistency verification
            prop_assert!(_x < 1001);
        }
    }
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod extended_tests {
    use super::*;
    use tempfile::TempDir;

    // ============ InMemoryBackend Tests ============

    #[test]
    fn test_in_memory_backend_default() {
        let backend = InMemoryBackend::default();
        assert_eq!(backend.backend_name(), "in-memory");
    }

    #[test]
    fn test_in_memory_backend_flush() {
        let backend = InMemoryBackend::new();
        // Flush should be a no-op but should not error
        assert!(backend.flush().is_ok());
    }

    #[test]
    fn test_in_memory_backend_size_on_disk() {
        let backend = InMemoryBackend::new();

        // Empty backend should have size 0
        assert_eq!(backend.size_on_disk().unwrap(), 0);

        // Add some data
        backend.put(b"key1", b"value1").unwrap();
        backend.put(b"key2", b"value2").unwrap();

        // Size should be sum of key + value lengths
        let expected_size = "key1".len() + "value1".len() + "key2".len() + "value2".len();
        assert_eq!(backend.size_on_disk().unwrap(), expected_size as u64);
    }

    #[test]
    fn test_in_memory_backend_get_nonexistent() {
        let backend = InMemoryBackend::new();
        let result = backend.get(b"nonexistent").unwrap();
        assert!(result.is_none());
    }

    #[test]
    fn test_in_memory_backend_delete_nonexistent() {
        let backend = InMemoryBackend::new();
        // Deleting nonexistent key should not error
        assert!(backend.delete(b"nonexistent").is_ok());
    }

    #[test]
    fn test_in_memory_backend_overwrite() {
        let backend = InMemoryBackend::new();
        backend.put(b"key", b"value1").unwrap();
        backend.put(b"key", b"value2").unwrap();

        let retrieved = backend.get(b"key").unwrap().unwrap();
        assert_eq!(retrieved, b"value2");
    }

    #[test]
    fn test_in_memory_backend_binary_data() {
        let backend = InMemoryBackend::new();
        let binary_key = vec![0u8, 1, 2, 255, 254];
        let binary_value = vec![255u8, 0, 128, 64, 32];

        backend.put(&binary_key, &binary_value).unwrap();
        let retrieved = backend.get(&binary_key).unwrap().unwrap();
        assert_eq!(retrieved, binary_value);
    }

    // ============ LibsqlBackend Tests ============

    #[test]
    fn test_libsql_backend_temporary() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    #[test]
    fn test_libsql_backend_get_nonexistent() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        let result = backend.get(b"nonexistent").unwrap();
        assert!(result.is_none());
    }

    #[test]
    fn test_libsql_backend_contains() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        assert!(!backend.contains(b"key").unwrap());
        backend.put(b"key", b"value").unwrap();
        assert!(backend.contains(b"key").unwrap());
    }

    #[test]
    fn test_libsql_backend_delete() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        backend.put(b"key", b"value").unwrap();
        assert!(backend.contains(b"key").unwrap());

        backend.delete(b"key").unwrap();
        assert!(!backend.contains(b"key").unwrap());
    }

    #[test]
    #[ignore] // Slow test - takes >60s
    fn test_libsql_backend_clear() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        for i in 0..5 {
            backend
                .put(format!("key{}", i).as_bytes(), b"value")
                .unwrap();
        }

        backend.clear().unwrap();

        let stats = backend.get_stats();
        assert_eq!(stats.get("entries").unwrap(), "0");
    }

    #[test]
    fn test_libsql_backend_size_on_disk_memory() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        // Empty in-memory database
        let size_empty = backend.size_on_disk().unwrap();
        assert_eq!(size_empty, 0);

        // Add data
        backend.put(b"key", b"value").unwrap();
        let size_with_data = backend.size_on_disk().unwrap();
        assert!(size_with_data > 0);
    }

    #[test]
    fn test_libsql_backend_size_on_disk_file() {
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("test.db");
        let backend = LibsqlBackend::new(&db_path).unwrap();

        backend.put(b"key", b"value").unwrap();
        backend.flush().unwrap();

        let size = backend.size_on_disk().unwrap();
        // File-based database should have positive size
        assert!(size > 0);
    }

    // ============ StorageConfig Tests ============

    #[test]
    fn test_storage_config_default() {
        let config = StorageConfig::default();
        assert_eq!(config.backend_type, StorageBackendType::Libsql);
        assert!(config.path.is_none());
        assert_eq!(config.cache_size_mb, Some(128));
        assert!(config.compression);
    }

    #[test]
    fn test_storage_config_custom() {
        let config = StorageConfig {
            backend_type: StorageBackendType::InMemory,
            path: Some(std::path::PathBuf::from("/tmp/test.db")),
            cache_size_mb: Some(256),
            compression: false,
        };
        assert_eq!(config.backend_type, StorageBackendType::InMemory);
        assert!(config.path.is_some());
        assert_eq!(config.cache_size_mb, Some(256));
        assert!(!config.compression);
    }

    #[test]
    fn test_storage_config_serialization() {
        let config = StorageConfig::default();
        let serialized = serde_json::to_string(&config).unwrap();
        assert!(serialized.contains("\"backend_type\""));
        assert!(serialized.contains("Libsql"));
    }

    #[test]
    fn test_storage_config_deserialization() {
        let json =
            r#"{"backend_type":"InMemory","path":null,"cache_size_mb":64,"compression":true}"#;
        let config: StorageConfig = serde_json::from_str(json).unwrap();
        assert_eq!(config.backend_type, StorageBackendType::InMemory);
        assert!(config.path.is_none());
        assert_eq!(config.cache_size_mb, Some(64));
    }

    #[test]
    fn test_storage_config_debug() {
        let config = StorageConfig::default();
        let debug = format!("{:?}", config);
        assert!(debug.contains("StorageConfig"));
    }

    #[test]
    fn test_storage_config_clone() {
        let config = StorageConfig::default();
        let cloned = config.clone();
        assert_eq!(cloned.backend_type, config.backend_type);
    }

    // ============ StorageBackendType Tests ============

    #[test]
    fn test_storage_backend_type_display_libsql() {
        assert_eq!(format!("{}", StorageBackendType::Libsql), "libsql");
    }

    #[test]
    fn test_storage_backend_type_display_inmemory() {
        assert_eq!(format!("{}", StorageBackendType::InMemory), "in-memory");
    }

    #[test]
    fn test_storage_backend_type_equality() {
        assert_eq!(StorageBackendType::Libsql, StorageBackendType::Libsql);
        assert_ne!(StorageBackendType::Libsql, StorageBackendType::InMemory);
    }

    #[test]
    fn test_storage_backend_type_serialization() {
        let backend_type = StorageBackendType::Libsql;
        let serialized = serde_json::to_string(&backend_type).unwrap();
        assert_eq!(serialized, "\"Libsql\"");
    }

    #[test]
    fn test_storage_backend_type_deserialization() {
        let deserialized: StorageBackendType = serde_json::from_str("\"InMemory\"").unwrap();
        assert_eq!(deserialized, StorageBackendType::InMemory);
    }

    #[test]
    fn test_storage_backend_type_debug() {
        let debug = format!("{:?}", StorageBackendType::Libsql);
        assert!(debug.contains("Libsql"));
    }

    #[test]
    fn test_storage_backend_type_copy() {
        let backend_type = StorageBackendType::Libsql;
        let copied = backend_type;
        assert_eq!(copied, StorageBackendType::Libsql);
    }

    // ============ StorageBackendFactory Tests ============

    #[test]
    fn test_factory_create_in_memory() {
        let backend = StorageBackendFactory::create_in_memory();
        assert_eq!(backend.backend_name(), "in-memory");
    }

    #[test]
    fn test_factory_create_default() {
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("default.db");
        let backend = StorageBackendFactory::create_default(&db_path).unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    #[test]
    fn test_factory_create_libsql() {
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("test.db");
        let backend = StorageBackendFactory::create_libsql(&db_path).unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    #[test]
    fn test_factory_create_libsql_temporary() {
        let backend = StorageBackendFactory::create_libsql_temporary().unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    #[test]
    fn test_factory_create_from_config_inmemory() {
        let config = StorageConfig {
            backend_type: StorageBackendType::InMemory,
            path: None,
            cache_size_mb: None,
            compression: false,
        };
        let backend = StorageBackendFactory::create_from_config(&config).unwrap();
        assert_eq!(backend.backend_name(), "in-memory");
    }

    #[test]
    fn test_factory_create_from_config_libsql_no_path() {
        let config = StorageConfig {
            backend_type: StorageBackendType::Libsql,
            path: None,
            cache_size_mb: None,
            compression: false,
        };
        let backend = StorageBackendFactory::create_from_config(&config).unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    #[test]
    fn test_factory_create_from_config_libsql_with_path() {
        let temp_dir = TempDir::new().unwrap();
        let config = StorageConfig {
            backend_type: StorageBackendType::Libsql,
            path: Some(temp_dir.path().join("config.db")),
            cache_size_mb: Some(64),
            compression: true,
        };
        let backend = StorageBackendFactory::create_from_config(&config).unwrap();
        assert_eq!(backend.backend_name(), "libsql");
    }

    // NOTE: Sled and RocksDB tests removed - backends no longer supported

    // ============ Iteration Tests ============

    #[test]
    fn test_in_memory_backend_iter_empty() {
        let backend = InMemoryBackend::new();
        let iter = backend.iter().unwrap();
        let results: Vec<_> = iter.collect::<Result<Vec<_>>>().unwrap();
        assert!(results.is_empty());
    }

    #[test]
    fn test_in_memory_backend_iter_multiple() {
        let backend = InMemoryBackend::new();
        for i in 0..10 {
            backend
                .put(
                    format!("key{}", i).as_bytes(),
                    format!("value{}", i).as_bytes(),
                )
                .unwrap();
        }

        let iter = backend.iter().unwrap();
        let results: Vec<_> = iter.collect::<Result<Vec<_>>>().unwrap();
        assert_eq!(results.len(), 10);
    }

    #[test]
    fn test_libsql_backend_iter_empty() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        let iter = backend.iter().unwrap();
        let results: Vec<_> = iter.collect::<Result<Vec<_>>>().unwrap();
        assert!(results.is_empty());
    }

    #[test]
    fn test_libsql_backend_iter_multiple() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        for i in 0..10 {
            backend
                .put(
                    format!("key{}", i).as_bytes(),
                    format!("value{}", i).as_bytes(),
                )
                .unwrap();
        }

        let iter = backend.iter().unwrap();
        let results: Vec<_> = iter.collect::<Result<Vec<_>>>().unwrap();
        assert_eq!(results.len(), 10);
    }

    // ============ Stats Tests ============

    #[test]
    fn test_in_memory_backend_get_stats() {
        let backend = InMemoryBackend::new();
        backend.put(b"key1", b"value1").unwrap();
        backend.put(b"key2", b"value2").unwrap();

        let stats = backend.get_stats();
        assert_eq!(stats.get("entries").unwrap(), "2");
        assert!(stats.contains_key("memory_bytes"));
    }

    #[test]
    #[ignore] // Slow test - takes >60s
    fn test_libsql_backend_get_stats() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        backend.put(b"key1", b"value1").unwrap();

        let stats = backend.get_stats();
        assert!(stats.contains_key("entries"));
        assert!(stats.contains_key("path"));
    }

    #[test]
    fn test_libsql_backend_get_stats_full() {
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("stats_test.db");
        let backend = LibsqlBackend::new(&db_path).unwrap();

        backend.put(b"key1", b"value1").unwrap();
        backend.put(b"key2", b"value2").unwrap();
        backend.flush().unwrap();

        let stats = backend.get_stats();
        assert_eq!(stats.get("entries").unwrap(), "2");
        assert!(stats.contains_key("size_bytes"));
        assert!(stats.contains_key("path"));
        assert!(stats.contains_key("page_count"));
    }

    #[test]
    fn test_in_memory_backend_large_values() {
        let backend = InMemoryBackend::new();
        let large_key = vec![0u8; 1024];
        let large_value = vec![1u8; 10240];

        backend.put(&large_key, &large_value).unwrap();
        let retrieved = backend.get(&large_key).unwrap().unwrap();
        assert_eq!(retrieved.len(), 10240);
    }

    #[test]
    fn test_libsql_backend_overwrite() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        backend.put(b"key", b"value1").unwrap();
        assert_eq!(backend.get(b"key").unwrap().unwrap(), b"value1");

        backend.put(b"key", b"value2").unwrap();
        assert_eq!(backend.get(b"key").unwrap().unwrap(), b"value2");
    }

    #[test]
    fn test_libsql_backend_binary_data() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        let binary_key = vec![0u8, 255, 128, 64];
        let binary_value = vec![255u8, 0, 1, 254];

        backend.put(&binary_key, &binary_value).unwrap();
        let retrieved = backend.get(&binary_key).unwrap().unwrap();
        assert_eq!(retrieved, binary_value);
    }

    #[test]
    fn test_storage_trait_via_box_dyn() {
        // Test using trait object
        let backend: Box<dyn StorageBackend> = Box::new(InMemoryBackend::new());

        backend.put(b"key", b"value").unwrap();
        assert!(backend.contains(b"key").unwrap());
        assert_eq!(backend.backend_name(), "in-memory");

        let retrieved = backend.get(b"key").unwrap().unwrap();
        assert_eq!(retrieved, b"value");
    }

    #[test]
    fn test_storage_backend_with_empty_key_value() {
        let backend = InMemoryBackend::new();

        // Empty key
        backend.put(b"", b"value").unwrap();
        assert!(backend.contains(b"").unwrap());
        assert_eq!(backend.get(b"").unwrap().unwrap(), b"value");

        // Empty value
        backend.put(b"key", b"").unwrap();
        assert!(backend.contains(b"key").unwrap());
        assert_eq!(backend.get(b"key").unwrap().unwrap(), b"");
    }

    #[test]
    fn test_libsql_backend_with_empty_key_value() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        // Empty key
        backend.put(b"", b"value").unwrap();
        assert!(backend.contains(b"").unwrap());
        assert_eq!(backend.get(b"").unwrap().unwrap(), b"value");

        // Empty value
        backend.put(b"key", b"").unwrap();
        assert!(backend.contains(b"key").unwrap());
        assert_eq!(backend.get(b"key").unwrap().unwrap(), b"");
    }

    #[test]
    fn test_in_memory_backend_concurrent_access() {
        use std::thread;

        let backend = Arc::new(InMemoryBackend::new());
        let mut handles = vec![];

        for i in 0..10 {
            let backend_clone = Arc::clone(&backend);
            let handle = thread::spawn(move || {
                let key = format!("key{}", i);
                let value = format!("value{}", i);
                backend_clone.put(key.as_bytes(), value.as_bytes()).unwrap();
            });
            handles.push(handle);
        }

        for handle in handles {
            handle.join().unwrap();
        }

        // Verify all keys were written
        for i in 0..10 {
            let key = format!("key{}", i);
            assert!(backend.contains(key.as_bytes()).unwrap());
        }
    }

    #[test]
    fn test_key_value_pair_type_alias() {
        let pair: KeyValuePair = (vec![1, 2, 3], vec![4, 5, 6]);
        assert_eq!(pair.0, vec![1, 2, 3]);
        assert_eq!(pair.1, vec![4, 5, 6]);
    }

    #[test]
    fn test_libsql_backend_delete_nonexistent() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        // Should not error
        assert!(backend.delete(b"nonexistent").is_ok());
    }

    #[test]
    fn test_libsql_backend_flush() {
        let backend = LibsqlBackend::new_temporary().unwrap();
        backend.put(b"key", b"value").unwrap();
        // Flush should not error
        assert!(backend.flush().is_ok());
    }

    #[test]
    fn test_libsql_backend_file_not_found_size() {
        // Create backend with file that gets deleted
        let temp_dir = TempDir::new().unwrap();
        let db_path = temp_dir.path().join("to_delete.db");
        let backend = LibsqlBackend::new(&db_path).unwrap();

        // Delete the temp directory to simulate file not found
        drop(temp_dir);

        // size_on_disk should return 0 for non-existent file
        let size = backend.size_on_disk().unwrap();
        // Either file doesn't exist (0) or we get actual size
        assert!(size == 0 || size > 0);
    }

    #[test]
    fn test_storage_backend_type_all_variants() {
        let variants = [StorageBackendType::Libsql, StorageBackendType::InMemory];

        for variant in variants {
            let display = format!("{}", variant);
            assert!(!display.is_empty());

            let debug = format!("{:?}", variant);
            assert!(!debug.is_empty());

            let cloned = variant;
            assert_eq!(cloned, variant);
        }
    }

    #[test]
    fn test_storage_config_with_all_fields() {
        let config = StorageConfig {
            backend_type: StorageBackendType::Libsql,
            path: Some(std::path::PathBuf::from("/var/lib/tdg/data.db")),
            cache_size_mb: Some(512),
            compression: true,
        };

        let json = serde_json::to_string(&config).unwrap();
        let deserialized: StorageConfig = serde_json::from_str(&json).unwrap();

        assert_eq!(deserialized.backend_type, config.backend_type);
        assert_eq!(deserialized.path, config.path);
        assert_eq!(deserialized.cache_size_mb, config.cache_size_mb);
        assert_eq!(deserialized.compression, config.compression);
    }

    #[test]
    fn test_in_memory_iter_with_modifications() {
        let backend = InMemoryBackend::new();

        // Add entries
        for i in 0..5 {
            backend
                .put(format!("key{}", i).as_bytes(), b"value")
                .unwrap();
        }

        // Get iterator count
        let count1 = backend.iter().unwrap().count();
        assert_eq!(count1, 5);

        // Delete some entries
        backend.delete(b"key0").unwrap();
        backend.delete(b"key2").unwrap();

        // Get new iterator count
        let count2 = backend.iter().unwrap().count();
        assert_eq!(count2, 3);
    }

    #[test]
    fn test_libsql_iter_order_independence() {
        let backend = LibsqlBackend::new_temporary().unwrap();

        backend.put(b"z", b"last").unwrap();
        backend.put(b"a", b"first").unwrap();
        backend.put(b"m", b"middle").unwrap();

        let iter = backend.iter().unwrap();
        let results: Vec<_> = iter.collect::<Result<Vec<_>>>().unwrap();
        assert_eq!(results.len(), 3);

        // All three keys should be present
        let keys: Vec<_> = results.iter().map(|(k, _)| k.clone()).collect();
        assert!(keys.contains(&b"a".to_vec()));
        assert!(keys.contains(&b"m".to_vec()));
        assert!(keys.contains(&b"z".to_vec()));
    }
}
