//! Adapter that exposes a host-provided [`crate::Storage`] as the
//! [`ping_mls_store::AsyncBlobStore`] the persistent provider needs.
//!
//! Hosts already implement `Storage` (web bindings via `PingStorageWeb`,
//! react-native-macos via `JsStorageBridge` on top of AsyncStorage,
//! iOS / Android via the uniffi callback shape, etc.). Wrapping it as a
//! single-blob backend means the OpenMLS snapshot lives under a
//! reserved `("__mls", "snapshot")` slot inside the host's existing
//! KV — no separate file path, Keychain key, or SQLCipher binding
//! required.
//!
//! The reserved namespace `__mls` MUST NOT collide with host keys;
//! hosts that own their own namespacing are advised to reserve this
//! prefix (the SDK's own metadata uses `device`, `groups`, `cursors`,
//! `device_leaves` — `__mls` is intentionally `__`-prefixed to mark it
//! as SDK-internal).

use std::sync::Arc;

use ping_mls_store::{AsyncBlobStore, BlobFuture};

use crate::storage::Storage;

const BLOB_NAMESPACE: &str = "__mls";
const BLOB_KEY: &str = "snapshot";

/// Wrap an `Arc<dyn Storage>` as an `Arc<dyn AsyncBlobStore>`. Returns
/// the wrapper directly as an `Arc` so callers can plug it straight
/// into `StorageBackend::AsyncBlob { blob_store }`.
pub fn storage_as_blob_store(storage: Arc<dyn Storage>) -> Arc<dyn AsyncBlobStore> {
    Arc::new(StorageAsBlob { storage })
}

struct StorageAsBlob {
    storage: Arc<dyn Storage>,
}

impl std::fmt::Debug for StorageAsBlob {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("StorageAsBlob")
    }
}

impl AsyncBlobStore for StorageAsBlob {
    fn read_blob(&self) -> BlobFuture<'_, std::result::Result<Option<Vec<u8>>, String>> {
        let fut = self.storage.get(BLOB_NAMESPACE, BLOB_KEY);
        Box::pin(async move {
            match fut.await {
                Ok(v) => Ok(v),
                Err(e) => Err(format!("Storage.get(__mls, snapshot): {e}")),
            }
        })
    }

    fn write_blob(&self, bytes: Vec<u8>) -> BlobFuture<'_, std::result::Result<(), String>> {
        let fut = self.storage.put(BLOB_NAMESPACE, BLOB_KEY, bytes);
        Box::pin(async move {
            match fut.await {
                Ok(()) => Ok(()),
                Err(e) => Err(format!("Storage.put(__mls, snapshot): {e}")),
            }
        })
    }
}
