use crate::Memory;
use futures::Stream;
use nostr_rs_storage_base::{
    cursor::{check_future_call, FutureResult, FutureValue},
    Error, EventFilter, Storage,
};
use nostr_rs_types::types::Event;
use std::{
    collections::{BTreeMap, VecDeque},
    pin::Pin,
    task::{Context, Poll},
};
use tokio::sync::RwLockReadGuard;

pub struct Cursor<'a> {
    pub db: &'a Memory,
    pub filter: Option<EventFilter>,
    pub limit: Option<usize>,
    pub returned: usize,
    pub index: RwLockReadGuard<'a, BTreeMap<Vec<u8>, Vec<u8>>>,
    pub index_prefixes: VecDeque<Vec<u8>>,
    pub last_prefix: Option<Vec<u8>>,
    pub current_index_key: Vec<u8>,
    pub future_event: Option<FutureResult<'a>>,
}

impl<'a> Cursor<'a> {
    fn get_id_from_index(&mut self) -> Option<Vec<u8>> {
        if let Some(Some(Some((last_prefix, id)))) = self.last_prefix.take().map(|last_prefix| {
            // We are iterating over an index and we are half way through, we
            // know the last key, we start iterating from them and skip 1,
            // because we already have that key If the key starts with the
            // current index key, we return it otherwise we go to the else
            // brancha and the next key in the index_prefixes is used
            //
            // This approach is needed since holding a reference to the index Range has been problematic
            self.index
                .range(last_prefix..)
                .nth(1)
                .map(|(idx_id, value)| {
                    if idx_id.starts_with(&self.current_index_key) {
                        Some((idx_id.clone(), value.clone()))
                    } else {
                        None
                    }
                })
        }) {
            self.last_prefix = Some(last_prefix);
            Some(id)
        } else {
            loop {
                self.current_index_key = self.index_prefixes.pop_front()?;
                if let Some(Some((last_prefix, id))) = self
                    .index
                    .range(self.current_index_key.clone()..)
                    .next()
                    .map(|(idx_id, id)| {
                        if idx_id.starts_with(&self.current_index_key) {
                            Some((idx_id.clone(), id.clone()))
                        } else {
                            None
                        }
                    })
                {
                    self.last_prefix = Some(last_prefix);
                    return Some(id);
                }
            }
        }
    }
}

impl<'a> Stream for Cursor<'a> {
    type Item = Result<Event, Error>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, None)
    }

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        if Some(self.returned) == self.limit {
            return Poll::Ready(None);
        }

        let this = Pin::into_inner(self);

        match check_future_call(&mut this.future_event, &this.filter, cx) {
            FutureValue::Found(event) => {
                this.returned += 1;
                return Poll::Ready(Some(event));
            }
            FutureValue::Pending => return Poll::Pending,
            _ => {}
        }

        while let Some(id) = this.get_id_from_index() {
            this.future_event = Some(this.db.get_event(id));

            match check_future_call(&mut this.future_event, &this.filter, cx) {
                FutureValue::Found(event) => {
                    this.returned += 1;
                    return Poll::Ready(Some(event));
                }
                FutureValue::Pending => return Poll::Pending,
                _ => {}
            }
        }

        Poll::Ready(None)
    }
}
