use termichart_core::Candle;
use tokio::sync::mpsc;

/// A streaming data adapter backed by a tokio unbounded MPSC channel.
///
/// Unlike [`JsonAdapter`](crate::JsonAdapter) and [`CsvAdapter`](crate::CsvAdapter),
/// `StreamAdapter` does not implement the synchronous [`DataAdapter`](termichart_core::DataAdapter)
/// trait. Instead it exposes async and try-recv methods for consuming candles
/// one at a time from a live data feed.
pub struct StreamAdapter {
    rx: mpsc::UnboundedReceiver<Candle>,
}

impl StreamAdapter {
    /// Creates a new `StreamAdapter` together with the sender half that can
    /// be used to push candles into the stream.
    pub fn new() -> (Self, mpsc::UnboundedSender<Candle>) {
        let (tx, rx) = mpsc::unbounded_channel();
        (Self { rx }, tx)
    }

    /// Asynchronously receives the next candle from the stream.
    ///
    /// Returns `None` when the sender has been dropped and the channel is empty.
    pub async fn recv(&mut self) -> Option<Candle> {
        self.rx.recv().await
    }

    /// Attempts to receive a candle without blocking.
    ///
    /// Returns `None` if the channel is empty or closed.
    pub fn try_recv(&mut self) -> Option<Candle> {
        self.rx.try_recv().ok()
    }
}

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

    #[test]
    fn try_recv_returns_none_when_empty() {
        let (mut adapter, _tx) = StreamAdapter::new();
        assert!(adapter.try_recv().is_none());
    }

    #[test]
    fn try_recv_returns_sent_candle() {
        let (mut adapter, tx) = StreamAdapter::new();
        let candle = Candle {
            time: 1.0,
            open: 10.0,
            high: 15.0,
            low: 9.0,
            close: 14.0,
            volume: 100.0,
        };
        tx.send(candle).unwrap();
        let received = adapter.try_recv().unwrap();
        assert_eq!(received.close, 14.0);
    }

    #[tokio::test]
    async fn recv_returns_sent_candle() {
        let (mut adapter, tx) = StreamAdapter::new();
        let candle = Candle {
            time: 2.0,
            open: 20.0,
            high: 25.0,
            low: 19.0,
            close: 24.0,
            volume: 200.0,
        };
        tx.send(candle).unwrap();
        let received = adapter.recv().await.unwrap();
        assert_eq!(received.time, 2.0);
    }

    #[tokio::test]
    async fn recv_returns_none_when_sender_dropped() {
        let (mut adapter, tx) = StreamAdapter::new();
        drop(tx);
        assert!(adapter.recv().await.is_none());
    }
}
