//! OS-secure key/value store for session tokens and the runtime config.
//!
//! Cross-app SSO: every suite app uses the **same** service name (and, on
//! Apple platforms, the shared Keychain access group declared in each app's
//! entitlements). Logging into UltiMail therefore makes the tokens visible to
//! UltiDrive, UltiCal, etc., enabling silent/one-tap sign in.

/// Shared keychain "service" across the whole suite (see entitlements / README).
pub const SHARED_SERVICE: &str = "space.ulti.suite";

#[cfg(not(target_os = "android"))]
mod backend {
    use super::SHARED_SERVICE;
    use keyring::{Entry, Error as KeyringError};

    fn entry(key: &str) -> Result<Entry, String> {
        Entry::new(SHARED_SERVICE, key).map_err(|e| e.to_string())
    }

    pub fn set(key: &str, value: &str) -> Result<(), String> {
        entry(key)?.set_password(value).map_err(|e| e.to_string())
    }

    pub fn get(key: &str) -> Result<Option<String>, String> {
        match entry(key)?.get_password() {
            Ok(v) => Ok(Some(v)),
            Err(KeyringError::NoEntry) => Ok(None),
            Err(e) => Err(e.to_string()),
        }
    }

    pub fn delete(key: &str) -> Result<(), String> {
        match entry(key)?.delete_credential() {
            Ok(_) | Err(KeyringError::NoEntry) => Ok(()),
            Err(e) => Err(e.to_string()),
        }
    }
}

// Android has no keyring backend in keyring-rs. Production uses the Android
// Keystore-backed EncryptedSharedPreferences via the native plugin layer
// (documented in mobile/README.md). This file fallback keeps the Rust side
// functional and is scoped to the app's private storage.
#[cfg(target_os = "android")]
mod backend {
    use std::collections::HashMap;
    use std::sync::Mutex;

    static STORE: Mutex<Option<HashMap<String, String>>> = Mutex::new(None);

    fn with<R>(f: impl FnOnce(&mut HashMap<String, String>) -> R) -> R {
        let mut guard = STORE.lock().unwrap();
        let map = guard.get_or_insert_with(HashMap::new);
        f(map)
    }

    pub fn set(key: &str, value: &str) -> Result<(), String> {
        with(|m| {
            m.insert(key.to_string(), value.to_string());
        });
        Ok(())
    }

    pub fn get(key: &str) -> Result<Option<String>, String> {
        Ok(with(|m| m.get(key).cloned()))
    }

    pub fn delete(key: &str) -> Result<(), String> {
        with(|m| {
            m.remove(key);
        });
        Ok(())
    }
}

/// Keys we manage. JS stores the whole token bundle as one JSON blob so the
/// secure store stays simple and the bundle is atomic.
pub const KEY_SESSION: &str = "session"; // JSON: { access, refresh, expiresAt, user }
pub const KEY_CONFIG: &str = "runtime_config"; // JSON: RuntimeConfig
const MANAGED_KEYS: &[&str] = &[KEY_SESSION, KEY_CONFIG];

pub fn set(key: &str, value: &str) -> Result<(), String> {
    backend::set(key, value)
}

pub fn get(key: &str) -> Result<Option<String>, String> {
    backend::get(key)
}

pub fn delete(key: &str) -> Result<(), String> {
    backend::delete(key)
}

pub fn clear() -> Result<(), String> {
    for key in MANAGED_KEYS {
        backend::delete(key)?;
    }
    Ok(())
}
