//! Platform-aware shared pointer type aliases.
//!
//! Provides unified type aliases for smart pointers and locks that work on both
//! native (multi-threaded) and WASM (single-threaded) platforms.
//!
//! # Native Platform
//!
//! Uses thread-safe primitives from `parking_lot`:
//! - `SharedPtr<T>` = `Arc<T>` (atomic reference counting)
//! - `SharedLock<T>` = `parking_lot::RwLock<T>` (read-write lock without
//!   poisoning)
//!
//! # WASM Platform
//!
//! Uses single-threaded primitives:
//! - `SharedPtr<T>` = `Rc<T>` (non-atomic reference counting)
//! - `SharedLock<T>` = `RefCell<T>` (runtime borrow checking)
//!
//! # Lock Poisoning
//!
//! This module uses `parking_lot::RwLock` instead of `std::sync::RwLock` on
//! native platforms. The key difference is that `parking_lot` locks **do not
//! support poisoning**. When a thread panics while holding a lock, the lock is
//! automatically released and remains usable by other threads.
//!
//! This design choice is intentional for an identity/auth SDK where:
//! - Reliability is critical - a panic in one operation should not render the
//!   entire client unusable
//! - Recovery is preferred over propagating failures - if a thread panics
//!   during a cache update, the next thread should still be able to acquire the
//!   lock
//! - The data protected by these locks (tokens, nonces) can be safely
//!   re-acquired from the server if corrupted
//!
//! # Example
//!
//! ```rust,ignore
//! use lastid_sdk::shared::{SharedPtr, SharedLock};
//!
//! // Works on both native and WASM
//! let shared_data = SharedPtr::new(SharedLock::new(42));
//! ```

// Native: Use thread-safe primitives with parking_lot (no lock poisoning)
#[cfg(not(target_arch = "wasm32"))]
pub type SharedPtr<T> = std::sync::Arc<T>;

#[cfg(not(target_arch = "wasm32"))]
pub type SharedLock<T> = parking_lot::RwLock<T>;

// WASM: Use single-threaded primitives
#[cfg(target_arch = "wasm32")]
pub type SharedPtr<T> = std::rc::Rc<T>;

#[cfg(target_arch = "wasm32")]
pub type SharedLock<T> = std::cell::RefCell<T>;

/// Helper trait for unified lock access across platforms.
///
/// Provides a common interface for reading from `SharedLock<T>`.
pub trait SharedLockRead<T> {
    /// Get a read reference to the inner value.
    ///
    /// # Native Platform
    ///
    /// Acquires a read lock. This will never panic due to lock poisoning since
    /// `parking_lot::RwLock` does not support poisoning. However, it may block
    /// if a writer holds the lock.
    ///
    /// # WASM Platform
    ///
    /// Borrows the inner value. May panic at runtime if the value is already
    /// mutably borrowed (this indicates a bug in single-threaded code).
    fn read_inner(&self) -> impl std::ops::Deref<Target = T>;
}

/// Helper trait for unified mutable lock access across platforms.
pub trait SharedLockWrite<T> {
    /// Get a mutable reference to the inner value.
    ///
    /// # Native Platform
    ///
    /// Acquires a write lock. This will never panic due to lock poisoning since
    /// `parking_lot::RwLock` does not support poisoning. However, it may block
    /// if any readers or another writer hold the lock.
    ///
    /// # WASM Platform
    ///
    /// Mutably borrows the inner value. May panic at runtime if the value is
    /// already borrowed (this indicates a bug in single-threaded code).
    fn write_inner(&self) -> impl std::ops::DerefMut<Target = T>;
}

#[cfg(not(target_arch = "wasm32"))]
impl<T> SharedLockRead<T> for SharedLock<T> {
    fn read_inner(&self) -> impl std::ops::Deref<Target = T> {
        // parking_lot::RwLock::read() never panics due to poisoning.
        // It returns the guard directly, not a Result.
        self.read()
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl<T> SharedLockWrite<T> for SharedLock<T> {
    fn write_inner(&self) -> impl std::ops::DerefMut<Target = T> {
        // parking_lot::RwLock::write() never panics due to poisoning.
        // It returns the guard directly, not a Result.
        self.write()
    }
}

#[cfg(target_arch = "wasm32")]
impl<T> SharedLockRead<T> for SharedLock<T> {
    fn read_inner(&self) -> impl std::ops::Deref<Target = T> {
        self.borrow()
    }
}

#[cfg(target_arch = "wasm32")]
impl<T> SharedLockWrite<T> for SharedLock<T> {
    fn write_inner(&self) -> impl std::ops::DerefMut<Target = T> {
        self.borrow_mut()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_shared_ptr_clone() {
        let ptr = SharedPtr::new(42);
        let cloned = SharedPtr::clone(&ptr);
        assert_eq!(*ptr, *cloned);
    }

    #[test]
    fn test_shared_lock_read_write() {
        let lock = SharedLock::new(42);
        {
            let value = lock.read_inner();
            assert_eq!(*value, 42);
        }
        {
            let mut value = lock.write_inner();
            *value = 100;
        }
        {
            let value = lock.read_inner();
            assert_eq!(*value, 100);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[test]
    fn test_lock_survives_panic_in_another_thread() {
        use std::sync::Arc;
        use std::thread;

        let lock = Arc::new(SharedLock::new(42));
        let lock_clone = Arc::clone(&lock);

        // Spawn a thread that will panic while holding the write lock
        let handle = thread::spawn(move || {
            let mut guard = lock_clone.write();
            *guard = 100;
            panic!("intentional panic while holding lock");
        });

        // Wait for the thread to panic
        let _ = handle.join();

        // The lock should still be usable - this is the key benefit of parking_lot
        // With std::sync::RwLock, this would return a PoisonError
        let value = lock.read_inner();
        // The value may be 42 or 100 depending on when the panic occurred,
        // but the important thing is that we can still acquire the lock
        assert!(*value == 42 || *value == 100);
    }
}
