use std::{
    future::Future,
    ops::Deref,
    pin::Pin,
    thread::{self, ThreadId},
};

use futures::{FutureExt, Stream, StreamExt};

#[cfg(feature = "source_indexeddb")]
pub mod indexeddb;

/// Wraps any type and forcefully implements [Send] for it
///
/// WASM (browser) targets are not multi-threaded, and `wasm-bindgen` only provides
/// [!Send](Send) types. To make them work in Taganak's [Send] streams, we need this
/// wrapper.
///
/// # Safety
///
/// This wrapper is only safe on targets that guarantee to never send the wrapped data
/// to a different thread.
///
/// # Panics
///
/// All implementations panic if the wrapper is sent to another thread.
#[derive(Debug)]
pub(super) struct FakeSend<F> {
    inner: Pin<Box<F>>,
    t: ThreadId,
}

impl<F> FakeSend<F> {
    pub(super) fn new(inner: F) -> Self {
        Self {
            inner: Box::pin(inner),
            t: thread::current().id(),
        }
    }

    pub(super) fn guard(&self) {
        if self.t != thread::current().id() {
            panic!("was sent to another thread when we expected a single-threaded environment")
        }
    }
}

impl<F> Deref for FakeSend<F> {
    type Target = F;

    fn deref(&self) -> &Self::Target {
        self.guard();
        self.inner.deref()
    }
}

impl<F: Future> Future for FakeSend<F> {
    type Output = F::Output;

    fn poll(
        mut self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        self.guard();
        self.inner.poll_unpin(cx)
    }
}

impl<F: Stream> Stream for FakeSend<F> {
    type Item = F::Item;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Option<Self::Item>> {
        self.guard();
        self.inner.poll_next_unpin(cx)
    }
}

unsafe impl<F> Send for FakeSend<F> {}
