use std::marker::PhantomData;
use std::rc::Rc;

use crate::context::{Compute, SlotId};
use crate::{Computed, Context, Source};

#[cfg(feature = "thread-safe")]
use crate::ThreadSafeContext;
#[cfg(feature = "thread-safe")]
use crate::{Read, Write};

/// A single-threaded lazily context tagged with a schema/context-family type.
///
/// The tag has no runtime cost. It prevents mixing slot and cell handles from
/// different schemas while reusing the existing [`Context`] graph.
pub struct TypedContext<Schema> {
    inner: Context,
    _schema: PhantomData<fn() -> Schema>,
}

/// Read-only typed context view passed into typed slot computations.
///
/// It threads the recomputing node id **as a value** (`#lzcellkernel`): a ref
/// built from a [`Compute`] carries `track = Some(slot_id)` so reads through it
/// register a dependency edge against the recomputing node; a ref built from a
/// bare [`Context`] (via [`TypedContext::as_ref`]) carries `track = None` and
/// reads untracked. Each read mints a throwaway [`Compute`] view over the shared
/// graph — there is no ambient thread-local.
pub struct TypedContextRef<'a, Schema> {
    inner: &'a Context,
    track: Option<SlotId>,
    _schema: PhantomData<fn() -> Schema>,
}

/// A slot handle bound to a typed context schema.
pub struct TypedSlotHandle<Schema, T> {
    raw: Computed<T>,
    _schema: PhantomData<fn() -> Schema>,
}

/// A cell handle bound to a typed context schema.
pub struct TypedCellHandle<Schema, T> {
    raw: Source<T>,
    _schema: PhantomData<fn() -> Schema>,
}

/// Values that can be read through a [`TypedContext`] with [`TypedContext::get`].
///
/// Implemented for typed slot handles, typed cell handles, and decorator-style
/// factory functions generated by `#[lazily::computed]` / `#[lazily::source]`.
pub trait TypedGet<Schema, Kind> {
    type Output;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output;
}

/// Values that can be read through a [`TypedContextRef`] with
/// [`TypedContextRef::get`].
///
/// This is the read-only companion to [`TypedGet`] used inside slot
/// computations so reads are tracked as graph dependencies.
pub trait TypedGetRef<Schema, Kind> {
    type Output;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output;
}

/// Values that can be written through a [`TypedContext`] with
/// [`TypedContext::set`].
///
/// Implemented for typed cell handles and decorator-style cell factory
/// functions. Slots are intentionally not writable and therefore do not
/// implement this trait.
pub trait TypedSet<Schema, T, Kind> {
    fn set_typed(self, ctx: &TypedContext<Schema>, value: T);
}

/// Extracts the schema marker from a typed context family.
///
/// Decorator macros use this to enforce that a factory declared for
/// `TypedContext<MySchema>` can be called with any compatible context view for
/// `MySchema`, while rejecting unrelated schemas.
pub trait TypedContextFamily {
    type Schema: 'static;
}

pub struct TypedSlotHandleSource;
pub struct TypedCellHandleSource;
pub struct TypedSlotFactorySource;
pub struct TypedCellFactorySource;

/// Context-like typed views that can memoize decorator-style slot/cell
/// factories.
///
/// Implemented for both [`TypedContext`] and [`TypedContextRef`], so a factory
/// can be called from ordinary code and from inside another slot callback while
/// returning the same context-local handle.
pub trait TypedFactoryContext {
    type Schema: 'static;

    fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
    where
        K: 'static,
        T: 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static;

    fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
    where
        K: 'static,
        T: PartialEq + 'static,
        F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T;
}

#[cfg(feature = "thread-safe")]
/// A thread-safe lazily context tagged with a schema/context-family type.
pub struct TypedThreadSafeContext<Schema> {
    inner: ThreadSafeContext,
    _schema: PhantomData<fn() -> Schema>,
}

#[cfg(feature = "thread-safe")]
/// Read-only typed thread-safe context view passed into typed slot computations.
pub struct TypedThreadSafeContextRef<'a, Schema> {
    inner: &'a ThreadSafeContext,
    _schema: PhantomData<fn() -> Schema>,
}

#[cfg(feature = "thread-safe")]
/// A thread-safe slot handle bound to a typed context schema.
pub struct TypedThreadSafeSlotHandle<Schema, T> {
    raw: Computed<T>,
    _schema: PhantomData<fn() -> Schema>,
}

#[cfg(feature = "thread-safe")]
/// A thread-safe cell handle bound to a typed context schema.
pub struct TypedThreadSafeCellHandle<Schema, T> {
    raw: Source<T>,
    _schema: PhantomData<fn() -> Schema>,
}

impl<Schema> TypedContext<Schema> {
    pub fn new() -> Self {
        Self {
            inner: Context::new(),
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> &Context {
        &self.inner
    }

    pub fn as_ref(&self) -> TypedContextRef<'_, Schema> {
        TypedContextRef::detached(&self.inner)
    }

    pub fn cell<T>(&self, value: T) -> TypedCellHandle<Schema, T>
    where
        T: PartialEq + 'static,
    {
        TypedCellHandle::new(self.inner.cell(value))
    }

    pub fn slot<T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
    where
        T: 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
    {
        let raw = self.inner.slot(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            compute(&typed)
        });
        TypedSlotHandle::new(raw)
    }

    pub fn computed<T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
    where
        T: PartialEq + 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
    {
        let raw = self.inner.computed(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            compute(&typed)
        });
        TypedSlotHandle::new(raw)
    }

    pub fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
    where
        K: 'static,
        T: 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
        Schema: 'static,
    {
        <Self as TypedFactoryContext>::memoized_slot::<K, T, F>(self, compute)
    }

    pub fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Schema, T>
    where
        K: 'static,
        T: PartialEq + 'static,
        F: for<'a> FnOnce(&TypedContextRef<'a, Schema>) -> T,
        Schema: 'static,
    {
        <Self as TypedFactoryContext>::memoized_cell::<K, T, F>(self, init)
    }

    pub fn get<S, Kind>(&self, source: S) -> S::Output
    where
        S: TypedGet<Schema, Kind>,
    {
        source.get_typed(self)
    }

    pub fn get_rc<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> Rc<T>
    where
        T: 'static,
    {
        self.inner.get_rc(&handle.raw)
    }

    pub fn get_cell<T>(&self, handle: &TypedCellHandle<Schema, T>) -> T
    where
        T: Clone + 'static,
    {
        self.inner.get(&handle.raw)
    }

    pub fn get_cell_rc<T>(&self, handle: &TypedCellHandle<Schema, T>) -> Rc<T>
    where
        T: 'static,
    {
        self.inner.get_rc(&handle.raw)
    }

    pub fn set_cell<T>(&self, handle: &TypedCellHandle<Schema, T>, value: T)
    where
        T: PartialEq + 'static,
    {
        self.inner.set(&handle.raw, value);
    }

    pub fn set<S, T, Kind>(&self, source: S, value: T)
    where
        S: TypedSet<Schema, T, Kind>,
    {
        source.set_typed(self, value);
    }

    pub fn is_set<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> bool
    where
        T: 'static,
    {
        self.inner.is_set(&handle.raw)
    }
}

impl<Schema> Default for TypedContext<Schema> {
    fn default() -> Self {
        Self::new()
    }
}

impl<Schema: 'static> TypedContextFamily for TypedContext<Schema> {
    type Schema = Schema;
}

impl<Schema: 'static> TypedFactoryContext for TypedContext<Schema> {
    type Schema = Schema;

    fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
    where
        K: 'static,
        T: 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static,
    {
        let raw = self.inner.memoized_slot::<K, T, _>(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            compute(&typed)
        });
        TypedSlotHandle::new(raw)
    }

    fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
    where
        K: 'static,
        T: PartialEq + 'static,
        F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T,
    {
        let raw = self.inner.memoized_cell::<K, T, _>(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            init(&typed)
        });
        TypedCellHandle::new(raw)
    }
}

impl<'a, Schema> TypedContextRef<'a, Schema> {
    /// Build a **tracked** ref from the recompute's [`Compute`] view — reads
    /// register against the recomputing node.
    fn from_compute(cx: &Compute<'a>) -> Self {
        Self {
            inner: cx.untracked(),
            track: Some(cx.slot_id()),
            _schema: PhantomData,
        }
    }

    /// Build an **untracked** ref from a bare context (outside any recompute).
    fn detached(inner: &'a Context) -> Self {
        Self {
            inner,
            track: None,
            _schema: PhantomData,
        }
    }

    /// The per-read [`Compute`] view carrying this ref's tracking discipline: a
    /// tracked ref registers against its node, an untracked one against the
    /// no-op [`SlotId::DETACHED`] sentinel.
    fn ops(&self) -> Compute<'_> {
        Compute::new(self.inner, self.track.unwrap_or(SlotId::DETACHED), 0)
    }

    pub fn raw(&self) -> &'a Context {
        self.inner
    }

    pub fn get<S, Kind>(&self, source: S) -> S::Output
    where
        S: TypedGetRef<Schema, Kind>,
    {
        source.get_typed_ref(self)
    }

    pub fn get_rc<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> Rc<T>
    where
        T: 'static,
    {
        self.ops().get_rc(&handle.raw)
    }

    pub fn get_cell<T>(&self, handle: &TypedCellHandle<Schema, T>) -> T
    where
        T: Clone + 'static,
    {
        handle.raw.get(&self.ops())
    }

    pub fn get_cell_rc<T>(&self, handle: &TypedCellHandle<Schema, T>) -> Rc<T>
    where
        T: 'static,
    {
        // Rc read of a source cell: register the edge (if tracked), then read.
        // A source has no tracked Rc read, so register the edge explicitly.
        if let Some(node) = self.track {
            self.inner.register_dependency(handle.raw.id, node);
        }
        self.inner.get_rc(&handle.raw)
    }

    pub fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
    where
        K: 'static,
        T: 'static,
        F: for<'b> Fn(&TypedContextRef<'b, Schema>) -> T + 'static,
        Schema: 'static,
    {
        <Self as TypedFactoryContext>::memoized_slot::<K, T, F>(self, compute)
    }

    pub fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Schema, T>
    where
        K: 'static,
        T: PartialEq + 'static,
        F: for<'b> FnOnce(&TypedContextRef<'b, Schema>) -> T,
        Schema: 'static,
    {
        <Self as TypedFactoryContext>::memoized_cell::<K, T, F>(self, init)
    }
}

impl<Schema: 'static> TypedContextFamily for TypedContextRef<'_, Schema> {
    type Schema = Schema;
}

impl<Schema: 'static> TypedFactoryContext for TypedContextRef<'_, Schema> {
    type Schema = Schema;

    fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
    where
        K: 'static,
        T: 'static,
        F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static,
    {
        let raw = self.inner.memoized_slot::<K, T, _>(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            compute(&typed)
        });
        TypedSlotHandle::new(raw)
    }

    fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
    where
        K: 'static,
        T: PartialEq + 'static,
        F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T,
    {
        let raw = self.inner.memoized_cell::<K, T, _>(move |ctx| {
            let typed = TypedContextRef::from_compute(ctx);
            init(&typed)
        });
        TypedCellHandle::new(raw)
    }
}

impl<Schema, T> TypedGet<Schema, TypedSlotHandleSource> for &TypedSlotHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        ctx.inner.get(&self.raw)
    }
}

impl<Schema, T> TypedGet<Schema, TypedSlotHandleSource> for TypedSlotHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        ctx.inner.get(&self.raw)
    }
}

impl<Schema, T> TypedGetRef<Schema, TypedSlotHandleSource> for &TypedSlotHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        // Tracked read: route through the ref's per-read `Compute` view so the
        // edge attributes to the recomputing node (`#lzcellkernel`).
        self.raw.get(&ctx.ops())
    }
}

impl<Schema, T> TypedGetRef<Schema, TypedSlotHandleSource> for TypedSlotHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        // Tracked read: route through the ref's per-read `Compute` view so the
        // edge attributes to the recomputing node (`#lzcellkernel`).
        self.raw.get(&ctx.ops())
    }
}

impl<Schema, T> TypedGet<Schema, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        ctx.inner.get(&self.raw)
    }
}

impl<Schema, T> TypedGet<Schema, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        ctx.inner.get(&self.raw)
    }
}

impl<Schema, T> TypedGetRef<Schema, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        // Tracked read: route through the ref's per-read `Compute` view so the
        // edge attributes to the recomputing node (`#lzcellkernel`).
        self.raw.get(&ctx.ops())
    }
}

impl<Schema, T> TypedGetRef<Schema, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
    T: Clone + 'static,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        // Tracked read: route through the ref's per-read `Compute` view so the
        // edge attributes to the recomputing node (`#lzcellkernel`).
        self.raw.get(&ctx.ops())
    }
}

impl<Schema: 'static, T, F> TypedGet<Schema, TypedSlotFactorySource> for F
where
    T: Clone + 'static,
    F: Fn(&TypedContext<Schema>) -> TypedSlotHandle<Schema, T>,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        let handle = self(ctx);
        ctx.inner.get(&handle.raw)
    }
}

impl<Schema: 'static, T, F> TypedGetRef<Schema, TypedSlotFactorySource> for F
where
    T: Clone + 'static,
    F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> TypedSlotHandle<Schema, T>,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        let handle = self(ctx);
        // Tracked read (`#lzcellkernel`): attribute to the recomputing node.
        handle.raw.get(&ctx.ops())
    }
}

impl<Schema: 'static, T, F> TypedGet<Schema, TypedCellFactorySource> for F
where
    T: Clone + 'static,
    F: Fn(&TypedContext<Schema>) -> TypedCellHandle<Schema, T>,
{
    type Output = T;

    fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
        let handle = self(ctx);
        ctx.inner.get(&handle.raw)
    }
}

impl<Schema: 'static, T, F> TypedGetRef<Schema, TypedCellFactorySource> for F
where
    T: Clone + 'static,
    F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> TypedCellHandle<Schema, T>,
{
    type Output = T;

    fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
        let handle = self(ctx);
        // Tracked read (`#lzcellkernel`): attribute to the recomputing node.
        handle.raw.get(&ctx.ops())
    }
}

impl<Schema, T> TypedSet<Schema, T, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
    T: PartialEq + 'static,
{
    fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
        ctx.inner.set(&self.raw, value);
    }
}

impl<Schema, T> TypedSet<Schema, T, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
    T: PartialEq + 'static,
{
    fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
        ctx.inner.set(&self.raw, value);
    }
}

impl<Schema: 'static, T, F> TypedSet<Schema, T, TypedCellFactorySource> for F
where
    T: PartialEq + 'static,
    F: Fn(&TypedContext<Schema>) -> TypedCellHandle<Schema, T>,
{
    fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
        let handle = self(ctx);
        ctx.inner.set(&handle.raw, value);
    }
}

impl<Schema, T> TypedSlotHandle<Schema, T> {
    fn new(raw: Computed<T>) -> Self {
        Self {
            raw,
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> Computed<T> {
        self.raw
    }

    pub fn get(&self, ctx: &TypedContext<Schema>) -> T
    where
        T: Clone + 'static,
    {
        ctx.get(self)
    }

    pub fn get_ref(&self, ctx: &TypedContextRef<'_, Schema>) -> T
    where
        T: Clone + 'static,
    {
        ctx.get(self)
    }

    pub fn clear(&self, ctx: &TypedContext<Schema>) {
        ctx.inner.clear_slot(self.raw.id);
        ctx.inner.flush_effects_after_invalidation();
    }
}

impl<Schema, T> Clone for TypedSlotHandle<Schema, T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<Schema, T> Copy for TypedSlotHandle<Schema, T> {}

impl<Schema, T> TypedCellHandle<Schema, T> {
    fn new(raw: Source<T>) -> Self {
        Self {
            raw,
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> Source<T> {
        self.raw
    }

    pub fn get(&self, ctx: &TypedContext<Schema>) -> T
    where
        T: Clone + 'static,
    {
        ctx.get(self)
    }

    pub fn get_ref(&self, ctx: &TypedContextRef<'_, Schema>) -> T
    where
        T: Clone + 'static,
    {
        ctx.get(self)
    }

    pub fn set(&self, ctx: &TypedContext<Schema>, value: T)
    where
        T: PartialEq + 'static,
    {
        ctx.set(self, value);
    }

    pub fn clear_dependents(&self, ctx: &TypedContext<Schema>) {
        ctx.inner.clear_cell_dependents(self.raw.id);
    }
}

impl<Schema, T> Clone for TypedCellHandle<Schema, T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<Schema, T> Copy for TypedCellHandle<Schema, T> {}

#[cfg(feature = "thread-safe")]
impl<Schema> TypedThreadSafeContext<Schema> {
    pub fn new() -> Self {
        Self {
            inner: ThreadSafeContext::new(),
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> &ThreadSafeContext {
        &self.inner
    }

    pub fn as_ref(&self) -> TypedThreadSafeContextRef<'_, Schema> {
        TypedThreadSafeContextRef::new(&self.inner)
    }

    pub fn cell<T>(&self, value: T) -> TypedThreadSafeCellHandle<Schema, T>
    where
        T: PartialEq + Send + Sync + 'static,
    {
        TypedThreadSafeCellHandle::new(self.inner.source(value))
    }

    pub fn slot<T, F>(&self, compute: F) -> TypedThreadSafeSlotHandle<Schema, T>
    where
        T: Send + Sync + 'static,
        F: for<'a> Fn(&TypedThreadSafeContextRef<'a, Schema>) -> T + Send + Sync + 'static,
    {
        let raw = self.inner.slot(move |ctx| {
            let typed = TypedThreadSafeContextRef::new(ctx);
            compute(&typed)
        });
        TypedThreadSafeSlotHandle::new(raw)
    }

    pub fn computed<T, F>(&self, compute: F) -> TypedThreadSafeSlotHandle<Schema, T>
    where
        T: PartialEq + Send + Sync + 'static,
        F: for<'a> Fn(&TypedThreadSafeContextRef<'a, Schema>) -> T + Send + Sync + 'static,
    {
        let raw = self.inner.computed(move |ctx| {
            let typed = TypedThreadSafeContextRef::new(ctx);
            compute(&typed)
        });
        TypedThreadSafeSlotHandle::new(raw)
    }

    /// Read a typed slot or cell handle (`#lzcellkernel`). Unified read
    /// superseding the deprecated `get_cell`.
    pub fn get<H: Read<Self> + ?Sized>(&self, handle: &H) -> <H as Read<Self>>::Output {
        handle.read(self)
    }

    /// Write a typed cell handle (`#lzcellkernel`). Unified write superseding
    /// the deprecated `set_cell`.
    pub fn set<H: Write<Self> + ?Sized>(&self, handle: &H, value: <H as Write<Self>>::Value) {
        handle.write(self, value)
    }

    #[deprecated(note = "use `TypedThreadSafeContext::get` (#lzcellkernel)")]
    pub fn get_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        self.get(handle)
    }

    #[deprecated(note = "use `TypedThreadSafeContext::set` (#lzcellkernel)")]
    pub fn set_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>, value: T)
    where
        T: PartialEq + Send + Sync + 'static,
    {
        self.set(handle, value);
    }

    pub fn is_set<T>(&self, handle: &TypedThreadSafeSlotHandle<Schema, T>) -> bool
    where
        T: Send + Sync + 'static,
    {
        self.inner.is_set(&handle.raw)
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T: Clone + Send + Sync + 'static> Read<TypedThreadSafeContext<Schema>>
    for TypedThreadSafeSlotHandle<Schema, T>
{
    type Output = T;
    fn read(&self, ctx: &TypedThreadSafeContext<Schema>) -> T {
        ctx.inner.get(&self.raw)
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T: Clone + Send + Sync + 'static> Read<TypedThreadSafeContext<Schema>>
    for TypedThreadSafeCellHandle<Schema, T>
{
    type Output = T;
    fn read(&self, ctx: &TypedThreadSafeContext<Schema>) -> T {
        ctx.inner.get(&self.raw)
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T: PartialEq + Send + Sync + 'static> Write<TypedThreadSafeContext<Schema>>
    for TypedThreadSafeCellHandle<Schema, T>
{
    type Value = T;
    fn write(&self, ctx: &TypedThreadSafeContext<Schema>, value: T) {
        ctx.inner.set(&self.raw, value);
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema> Clone for TypedThreadSafeContext<Schema> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            _schema: PhantomData,
        }
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema> Default for TypedThreadSafeContext<Schema> {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "thread-safe")]
impl<'a, Schema> TypedThreadSafeContextRef<'a, Schema> {
    fn new(inner: &'a ThreadSafeContext) -> Self {
        Self {
            inner,
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> &'a ThreadSafeContext {
        self.inner
    }

    /// Read a typed slot or cell handle inside a slot computation
    /// (`#lzcellkernel`). Unified read superseding the deprecated `get_cell`.
    pub fn get<H: Read<Self> + ?Sized>(&self, handle: &H) -> <H as Read<Self>>::Output {
        handle.read(self)
    }

    #[deprecated(note = "use `TypedThreadSafeContextRef::get` (#lzcellkernel)")]
    pub fn get_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        self.get(handle)
    }
}

#[cfg(feature = "thread-safe")]
impl<'a, Schema, T: Clone + Send + Sync + 'static> Read<TypedThreadSafeContextRef<'a, Schema>>
    for TypedThreadSafeSlotHandle<Schema, T>
{
    type Output = T;
    fn read(&self, ctx: &TypedThreadSafeContextRef<'a, Schema>) -> T {
        ctx.inner.get(&self.raw)
    }
}

#[cfg(feature = "thread-safe")]
impl<'a, Schema, T: Clone + Send + Sync + 'static> Read<TypedThreadSafeContextRef<'a, Schema>>
    for TypedThreadSafeCellHandle<Schema, T>
{
    type Output = T;
    fn read(&self, ctx: &TypedThreadSafeContextRef<'a, Schema>) -> T {
        ctx.inner.get(&self.raw)
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T> TypedThreadSafeSlotHandle<Schema, T> {
    fn new(raw: Computed<T>) -> Self {
        Self {
            raw,
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> Computed<T> {
        self.raw
    }

    pub fn get(&self, ctx: &TypedThreadSafeContext<Schema>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        ctx.get(self)
    }

    pub fn get_ref(&self, ctx: &TypedThreadSafeContextRef<'_, Schema>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        ctx.get(self)
    }

    pub fn clear(&self, ctx: &TypedThreadSafeContext<Schema>) {
        ctx.inner.clear(&self.raw);
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T> Clone for TypedThreadSafeSlotHandle<Schema, T> {
    fn clone(&self) -> Self {
        *self
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T> Copy for TypedThreadSafeSlotHandle<Schema, T> {}

#[cfg(feature = "thread-safe")]
impl<Schema, T> TypedThreadSafeCellHandle<Schema, T> {
    fn new(raw: Source<T>) -> Self {
        Self {
            raw,
            _schema: PhantomData,
        }
    }

    pub fn raw(&self) -> Source<T> {
        self.raw
    }

    pub fn get(&self, ctx: &TypedThreadSafeContext<Schema>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        ctx.raw().get(&self.raw)
    }

    pub fn get_ref(&self, ctx: &TypedThreadSafeContextRef<'_, Schema>) -> T
    where
        T: Clone + Send + Sync + 'static,
    {
        ctx.raw().get(&self.raw)
    }

    pub fn set(&self, ctx: &TypedThreadSafeContext<Schema>, value: T)
    where
        T: PartialEq + Send + Sync + 'static,
    {
        ctx.raw().set(&self.raw, value);
    }

    pub fn clear_dependents(&self, ctx: &TypedThreadSafeContext<Schema>) {
        ctx.inner.clear_cell_dependents(&self.raw);
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T> Clone for TypedThreadSafeCellHandle<Schema, T> {
    fn clone(&self) -> Self {
        *self
    }
}

#[cfg(feature = "thread-safe")]
impl<Schema, T> Copy for TypedThreadSafeCellHandle<Schema, T> {}

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

    enum CycleSchema {}

    #[test]
    fn typed_context_binds_slot_and_cell_handles_to_schema() {
        let ctx = TypedContext::<CycleSchema>::new();
        let input = ctx.cell(2);
        let doubled = ctx.slot(move |ctx| ctx.get(&input) * 2);

        assert_eq!(ctx.get(&doubled), 4);
        ctx.set(input, 3);
        assert_eq!(doubled.get(&ctx), 6);
    }

    #[cfg(feature = "thread-safe")]
    #[test]
    fn typed_thread_safe_context_binds_slot_and_cell_handles_to_schema() {
        let ctx = TypedThreadSafeContext::<CycleSchema>::new();
        let input = ctx.cell(2);
        let doubled = ctx.slot(move |ctx| ctx.get(&input) * 2);

        assert_eq!(ctx.get(&doubled), 4);
        ctx.set(&input, 3);
        assert_eq!(doubled.get(&ctx), 6);
    }
}
