use crate::{Error, Storage};
use nostr_rs_types::types::{Addr, Event, Filter, Kind};
use parking_lot::RwLock;
use std::{collections::HashMap, sync::atomic::AtomicUsize};
use tokio::sync::mpsc::Sender;

#[allow(dead_code)]
struct SubscriptionEntry {
    pub id: usize,
    pub filter: Filter,
    pub sender: Sender<(usize, Event)>,
}

#[allow(dead_code)]
enum SubscriptionListenerType {
    Id(Addr),
    Author(Addr),
    Kind(Kind),
    ReferenceToEvent(Addr),
    ReferenceToPublicKey(Addr),
}

/// Subscription
pub struct Subscription<T>
where
    T: Storage,
{
    db: T,
    subscriptions: RwLock<HashMap<usize, SubscriptionEntry>>,
    subscription_listener: RwLock<HashMap<SubscriptionListenerType, Vec<usize>>>,
    last_id: AtomicUsize,
}

pub struct SubscriptionResultFromDb<I: Iterator> {
    iterator: I,
}

impl<I> Iterator for SubscriptionResultFromDb<I>
where
    I: Iterator<Item = Result<Event, Error>>,
{
    type Item = Result<Event, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next()
    }
}

impl<T> Subscription<T>
where
    T: Storage,
{
    /// Wraps the storage layer to by pass the subscription/notification wrapper
    pub fn new(db: T) -> Self {
        Self {
            db,
            subscriptions: RwLock::new(HashMap::new()),
            subscription_listener: RwLock::new(HashMap::new()),
            last_id: AtomicUsize::new(0),
        }
    }

    /// Gets an event from the wrapped storage
    pub fn get_event<T1: AsRef<[u8]>>(&self, id: T1) -> Result<Option<Event>, Error> {
        self.db.get_event(id)
    }

    /// Removes a subscription from the listener
    pub fn unsubscribe(self, subscription_id: usize) -> Result<(), Error> {
        let mut subscribers = self.subscriptions.write();
        let _ = subscribers.remove(&subscription_id);
        Ok(())
    }

    /// Subscribes to a filter. The first streamed bytes will be reads from the
    /// database.
    pub fn subscribe(
        &self,
        filter: Filter,
        sender: Sender<(usize, Event)>,
    ) -> Result<(usize, SubscriptionResultFromDb<T::Iterator<'_>>), Error> {
        let mut subscribers = self.subscriptions.write();
        let mut _subscription_listener = self.subscription_listener.write();
        let id = self
            .last_id
            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        subscribers.insert(
            id,
            SubscriptionEntry {
                id,
                filter: filter.clone(),
                sender,
            },
        );
        Ok((
            id,
            SubscriptionResultFromDb {
                iterator: self.db.get_by_filter(filter)?,
            },
        ))
    }
}
