//! Rocks DB implementation of the storage layer
use crate::{event_filter::EventFilter, RocksDb};
use nostr_rs_storage_base::{Error, Storage};
use nostr_rs_types::types::Event;
use rocksdb::{BoundColumnFamily, DBIteratorWithThreadMode, DB};
use std::{collections::VecDeque, sync::Arc};

pub struct WrapperIterator<'a> {
    /// Reference to the rocks db database. This is useful to load the event
    /// data, because in the secondary indexes we only store the event ID. It
    /// could be possible to avoid a reference and speed up things even more to
    /// duplicate the event, but that would mean it would use way more disk
    /// space.
    pub db: &'a RocksDb,
    /// List of filters to apply to the events, before returning. If no filter
    /// is given each events from the secondary index will be returned,
    /// otherwise the events will be filtered by the given filter, and only
    /// those events that comply will be returned
    pub filter: Option<EventFilter>,
    /// Reference to the namespace to use to query the secondary index. If none
    /// is given the secondary_index_iterator must be constructed outside this
    /// wrapper.
    pub namespace: Option<Arc<BoundColumnFamily<'a>>>,
    /// The current secondary index iterator. If none is given the iterator will
    /// try to create one using the namespace property and the first prefix from
    /// prefixes (it will also be copied to current_prefix)
    pub secondary_index_iterator: Option<DBIteratorWithThreadMode<'a, DB>>,
    pub current_prefix: Vec<u8>,
    pub prefixes: VecDeque<Vec<u8>>,
    pub limit: Option<usize>,
    pub returned: usize,
}

impl<'a> WrapperIterator<'a> {
    /// Selects the next prefix available and starts an iterator using the
    /// secondary index. If no prefix is available from prefixes the functions
    /// return None, signalling upstream the are no more results
    fn select_next_prefix_using_secondary_index(&mut self) -> Option<()> {
        let prefix = self.prefixes.pop_front()?;
        self.secondary_index_iterator = Some(
            self.db
                .db
                .prefix_iterator_cf(self.namespace.as_ref()?, prefix.clone()),
        );
        self.current_prefix = prefix;
        Some(())
    }
}

impl<'a> Iterator for WrapperIterator<'a> {
    type Item = Result<Event, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        if Some(self.returned) == self.limit {
            return None;
        }
        if self.secondary_index_iterator.is_none() {
            if self.namespace.is_some() {
                self.select_next_prefix_using_secondary_index()?;
            } else {
                // No secondary index is used to query, this means the query is
                // using the ID filter, so it is more efficient to use the
                // primary index to prefetch events that may satisfy the query
                loop {
                    let prefix = self.prefixes.pop_front()?;
                    if let Ok(Some(event)) = self.db.get_event(prefix) {
                        if let Some(filter) = &self.filter {
                            if filter.check_event(&event) {
                                self.returned += 1;
                                return Some(Ok(event));
                            }
                        } else {
                            self.returned += 1;
                            return Some(Ok(event));
                        }
                    }
                }
            }
        }

        loop {
            loop {
                let secondary_index = self.secondary_index_iterator.as_mut()?;
                let (key, value) = match secondary_index.next() {
                    Some(Ok((k, v))) => (k, v),
                    _ => {
                        // break this loop to select next available prefix
                        break;
                    }
                };
                if !key.starts_with(&self.current_prefix) {
                    break;
                }
                if let Ok(Some(event)) = self.db.get_event(value) {
                    if let Some(filter) = &self.filter {
                        if filter.check_event(&event) {
                            self.returned += 1;
                            return Some(Ok(event));
                        }
                    } else {
                        self.returned += 1;
                        return Some(Ok(event));
                    }
                }
            }
            // Select next prefix if available, or exists
            self.select_next_prefix_using_secondary_index()?;
        }
    }
}
