use crate::error::Result;
use async_trait::async_trait;
use std::sync::Arc;

/// Abstract interface for Key-Value storage backends.
///
/// This trait allows us to swap the underlying storage engine (e.g., fjall, redb)
/// without changing the core logic of the database.
#[async_trait]
pub trait KVStore: Send + Sync {
    /// Retrieve a value by key.
    async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;

    /// Insert or update a value by key.
    async fn put(&self, key: &[u8], value: &[u8]) -> Result<()>;

    /// Delete a value by key.
    async fn delete(&self, key: &[u8]) -> Result<()>;

    /// Scan keys with a given prefix.
    async fn scan_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>>;

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

    /// Atomically add to a u64 counter stored at key.
    ///
    /// If key doesn't exist, initializes to 0 before adding.
    /// Returns the new value after addition.
    ///
    /// This is critical for correctness in high-velocity vote ingestion,
    /// where multiple concurrent writers update the same vote count.
    async fn fetch_and_add_u64(&self, key: &[u8], delta: u64) -> Result<u64>;

    /// Atomically update an f32 value using compare-and-swap semantics.
    ///
    /// If key doesn't exist, initializes to 0.0 before applying the update.
    /// The `update_fn` receives the current value and returns the new value.
    /// Returns the new value after update.
    ///
    /// Used for aggregate weight caches where we need thread-safe f32 updates.
    async fn compare_and_swap_f32<F>(&self, key: &[u8], update_fn: F) -> Result<f32>
    where
        F: Fn(f32) -> f32 + Send + Sync;
}

/// Implement KVStore for Arc<S> to enable sharing stores across components.
#[async_trait]
impl<S: KVStore> KVStore for Arc<S> {
    async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
        (**self).get(key).await
    }

    async fn put(&self, key: &[u8], value: &[u8]) -> Result<()> {
        (**self).put(key, value).await
    }

    async fn delete(&self, key: &[u8]) -> Result<()> {
        (**self).delete(key).await
    }

    async fn scan_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
        (**self).scan_prefix(prefix).await
    }

    async fn flush(&self) -> Result<()> {
        (**self).flush().await
    }

    async fn fetch_and_add_u64(&self, key: &[u8], delta: u64) -> Result<u64> {
        (**self).fetch_and_add_u64(key, delta).await
    }

    async fn compare_and_swap_f32<F>(&self, key: &[u8], update_fn: F) -> Result<f32>
    where
        F: Fn(f32) -> f32 + Send + Sync,
    {
        (**self).compare_and_swap_f32(key, update_fn).await
    }
}
