//! WASM backend for kvstore using IndexedDB (idb crate)
//!
//! # Platform
//!
//! This backend is only available when compiling for `wasm32` (browser/WebAssembly).
//! It uses the `idb` crate for async IndexedDB operations.
//!
//! # Usage
//!
//! This implementation is designed to run inside a browser environment and is not supported on native targets.
//!
//! # Example
//!


//! WASM backend for kvstore using IndexedDB (idb crate)
//! Only compiled for wasm32 targets

#[cfg(target_arch = "wasm32")]
use crate::traits::KVStore;
#[cfg(target_arch = "wasm32")]
use crate::error::{KVError, Result};
#[cfg(target_arch = "wasm32")]
use async_trait::async_trait;
#[cfg(target_arch = "wasm32")]
use idb::{Database, TransactionMode, Factory};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;
// use wasm-bindgen directly for Uint8Array if needed
#[cfg(target_arch = "wasm32")]
use std::rc::Rc;

#[cfg(target_arch = "wasm32")]
const STORE_NAME: &str = "kv";

#[cfg(target_arch = "wasm32")]
#[derive(Clone)]
pub struct WasmStore {
    db: Rc<Database>,
}

#[cfg(target_arch = "wasm32")]
impl WasmStore {
    pub async fn open(name: &str) -> Result<Self> {
        let factory = Factory::new().map_err(|e| KVError::Other(format!("IndexedDB factory error: {e:?}")))?;
        let mut open_req = factory.open(name, None)
            .map_err(|e| KVError::Other(format!("IndexedDB factory open error: {e:?}")))?;
        open_req.on_upgrade_needed(|event| {
            use idb::DatabaseEvent;
            let db = event.database().expect("Failed to get database in upgrade event");
            if !db.store_names().iter().any(|n| n == STORE_NAME) {
                db.create_object_store(STORE_NAME, Default::default()).unwrap();
            }
        });
        let db = open_req.await.map_err(|e| KVError::Other(format!("IndexedDB open error: {e:?}")))?;
        Ok(Self { db: Rc::new(db) })
    }
}

#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
impl KVStore for WasmStore {
    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
        let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadOnly)
            .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?;
        let store = tx.object_store(STORE_NAME)
            .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?;
        use idb::Query;
        let val = store.get(Query::from(JsValue::from_str(key)))?.await
            .map_err(|e| KVError::Other(format!("idb get error: {e:?}")))?;
        if let Some(jsval) = val {
            match jsval.into_serde::<Vec<u8>>() {
    Ok(bytes) => Ok(Some(bytes)),
    Err(_) => Ok(None),
}
        } else {
            Ok(None)
        }
    }
    async fn set(&self, key: &str, value: &[u8]) -> Result<()> {
        let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite)
            .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?;
        let store = tx.object_store(STORE_NAME)
            .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?;
        let js_value = JsValue::from_serde(&value).map_err(|e| KVError::Other(format!("serde error: {e:?}")))?;
store.put(&js_value, Some(&JsValue::from_str(key)))?.await
    .map_err(|e| KVError::Other(format!("idb put error: {e:?}")))?;
        Ok(())
    }
    async fn remove(&self, key: &str) -> Result<()> {
        let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite)
            .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?;
        let store = tx.object_store(STORE_NAME)
            .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?;
        use idb::Query;
        store.delete(Query::from(JsValue::from_str(key)))?.await
            .map_err(|e| KVError::Other(format!("idb delete error: {e:?}")))?;
        Ok(())
    }
    async fn contains_key(&self, key: &str) -> Result<bool> {
        Ok(self.get(key).await?.is_some())
    }

    async fn keys(&self) -> Result<Vec<String>> {
        let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadOnly)
            .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?;
        let store = tx.object_store(STORE_NAME)
            .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?;
        let js_keys = store.get_all_keys(None, None)?.await
            .map_err(|e| KVError::Other(format!("idb get_all_keys error: {e:?}")))?;
        let mut keys = Vec::new();
        for key in js_keys.iter() {
            if let Some(s) = key.as_string() {
                keys.push(s);
            }
        }
        Ok(keys)
    }

    async fn clear(&self) -> Result<()> {
        let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite)
            .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?;
        let store = tx.object_store(STORE_NAME)
            .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?;
        store.clear()?.await
            .map_err(|e| KVError::Other(format!("idb clear error: {e:?}")))?;
        Ok(())
    }
}

#[cfg(not(target_arch = "wasm32"))]
pub struct WasmStore;
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl KVStore for WasmStore {
    async fn get(&self, _key: &str) -> Result<Option<Vec<u8>>> {
        Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string()))
    }
    async fn set(&self, _key: &str, _value: &[u8]) -> Result<()> {
        Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string()))
    }
    async fn delete(&self, _key: &str) -> Result<()> {
        Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string()))
    }
    async fn exists(&self, _key: &str) -> Result<bool> {
        Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string()))
    }
}
