//! Opaque session-handle bridge. The master key never leaves WASM linear
//! memory; JS receives only a u32 handle that it passes back on every
//! subsequent call.

use std::cell::RefCell;
use std::collections::HashMap;
use zeroize::Zeroizing;

pub struct SessionData {
    pub master_key: Zeroizing<[u8; 32]>,
    pub image_secret: Zeroizing<[u8; 32]>,
}

thread_local! {
    static SESSIONS: RefCell<HashMap<u32, SessionData>> = RefCell::new(HashMap::new());
    static NEXT_HANDLE: RefCell<u32> = const { RefCell::new(1) };
}

pub fn insert(master_key: Zeroizing<[u8; 32]>, image_secret: Zeroizing<[u8; 32]>) -> u32 {
    let handle = NEXT_HANDLE.with(|n| {
        let mut n = n.borrow_mut();
        let h = *n;
        *n = n.wrapping_add(1);
        if *n == 0 { *n = 1; } // avoid reserving 0 as a valid handle
        h
    });
    SESSIONS.with(|s| {
        s.borrow_mut().insert(handle, SessionData { master_key, image_secret });
    });
    handle
}

/// Access the master key for a handle. Preserves original `with` signature for all existing callers.
pub fn with<F, R>(handle: u32, f: F) -> Option<R>
where
    F: FnOnce(&Zeroizing<[u8; 32]>) -> R,
{
    SESSIONS.with(|s| s.borrow().get(&handle).map(|d| f(&d.master_key)))
}

/// Access the image_secret for a handle (used by recovery QR).
pub fn with_image_secret<F, R>(handle: u32, f: F) -> Option<R>
where
    F: FnOnce(&Zeroizing<[u8; 32]>) -> R,
{
    SESSIONS.with(|s| s.borrow().get(&handle).map(|d| f(&d.image_secret)))
}

/// Remove a session entry. Called by both `lock(handle)` (the explicit
/// path) and `impl Drop for SessionHandle` (the safety net). Returns
/// `true` if an entry was removed, `false` if the handle was already gone.
pub fn remove(handle: u32) -> bool {
    SESSIONS.with(|s| s.borrow_mut().remove(&handle).is_some())
}

/// For tests only — empty the table and wipe all sessions.
#[cfg(test)]
pub fn clear() {
    SESSIONS.with(|s| s.borrow_mut().clear());
}
