use crate::connection::ConnectionId;
use nostr_rs_storage_base::{EventFilter, Index, SingleIndex};
use nostr_rs_types::{
    client::Subscribe,
    types::{Event, SubscriptionId},
    Response,
};
use std::{
    collections::{BTreeMap, HashSet},
    sync::{
        atomic::{AtomicUsize, Ordering},
        Arc,
    },
};
use tokio::sync::{mpsc::Sender, RwLock};

type SubIdx = (SingleIndex, ConnectionId, SubscriptionId);

pub const MIN_PREFIX_MATCH_LEN: usize = 2;

/// Subscription for a connection
///
/// This object is responsible for keeping track of a subscription for a connection
///
/// When dropped their listener will be removed from the subscription manager automatically
#[derive(Clone, Debug)]
pub struct ActiveSubscription {
    conn_id: ConnectionId,
    name: SubscriptionId,
    indexes: Vec<SingleIndex>,
    manager: Arc<SubscriptionManager>,
}

impl ActiveSubscription {
    fn new(
        conn_id: ConnectionId,
        name: SubscriptionId,
        index: Index,
        manager: Arc<SubscriptionManager>,
    ) -> Self {
        Self {
            conn_id,
            name,
            indexes: index.split(),
            manager,
        }
    }
}

impl Drop for ActiveSubscription {
    /// When the subscription is dropped, it will remove the listener from the
    /// subscription manager
    fn drop(&mut self) {
        let indexes = self
            .indexes
            .drain(..)
            .map(|key| (key, self.conn_id, self.name.clone()))
            .collect::<Vec<_>>();

        let manager = self.manager.clone();

        tokio::spawn(async move {
            manager.unsubscribe(indexes).await;
        });
    }
}

type SubscriptionValue = (Arc<EventFilter>, Sender<Response>);

/// Subscription manager
///
/// This object is responsible for letting clients and processes subscribe to
/// events,
#[derive(Debug)]
pub struct SubscriptionManager {
    /// List of subscriptions with their filters and their index.
    ///
    /// A single request may be converted to multiple subscriptions entry as
    /// they are sorted by their index/key
    subscriptions: RwLock<BTreeMap<SubIdx, SubscriptionValue>>,
    /// Total number of subscribers
    /// A single REQ may have multiple subscriptions
    total_subscribers: AtomicUsize,
    /// Minimum prefix match length
    min_prefix_match_len: usize,
}

impl Default for SubscriptionManager {
    fn default() -> Self {
        Self {
            subscriptions: Default::default(),
            total_subscribers: Default::default(),
            min_prefix_match_len: MIN_PREFIX_MATCH_LEN,
        }
    }
}

impl SubscriptionManager {
    async fn unsubscribe(self: Arc<Self>, keys: Vec<SubIdx>) {
        let mut subscriptions = self.subscriptions.write().await;
        for sub in keys {
            subscriptions.remove(&sub);
        }
        self.total_subscribers.fetch_sub(1, Ordering::Relaxed);
    }

    fn get_keys_from_event(event: &Event, _min_prefix_match_len: usize) -> Vec<SingleIndex> {
        let mut subscriptions = vec![];

        subscriptions.push(SingleIndex::Author(event.author().to_owned()));
        subscriptions.push(SingleIndex::Id(event.id.to_owned()));

        for t in event.tags() {
            t.get_indexable_value()
                .map(|v| subscriptions.push(SingleIndex::Tag(t.get_identifier().to_owned(), v)));
        }

        subscriptions.push(SingleIndex::Kind(event.kind()));
        subscriptions.push(SingleIndex::AllUpdates);
        subscriptions
    }

    /// Get the number of subscribers
    pub fn total_subscribers(self: &Arc<Self>) -> usize {
        self.total_subscribers.load(Ordering::Relaxed)
    }

    /// Subscribe to a future events
    ///
    /// This will add a new subscription to the subscription manager with a
    /// given conn_id, sender and a vector of filters.
    pub async fn subscribe(
        self: &Arc<Self>,
        conn_id: ConnectionId,
        sender: Sender<Response>,
        request: Subscribe,
    ) -> Vec<ActiveSubscription> {
        let name = request.subscription_id;
        let mut subscriptions = self.subscriptions.write().await;
        let subscriptions = request
            .filters
            .into_iter()
            .map(|mut filter| {
                let index: Index = (&mut filter).into();
                let filter = Arc::new(EventFilter::from(filter));
                let subscription =
                    ActiveSubscription::new(conn_id, name.clone(), index, self.clone());

                for key in subscription.indexes.iter() {
                    subscriptions.insert(
                        (key.clone(), conn_id, name.clone()),
                        (filter.clone(), sender.clone()),
                    );
                }
                subscription
            })
            .collect::<Vec<_>>();
        self.total_subscribers
            .fetch_add(subscriptions.len(), Ordering::Relaxed);
        subscriptions
    }

    /// Publish an event to all subscribers
    pub fn broadcast(self: &Arc<Self>, event: Event) {
        let this = self.clone();
        tokio::spawn(async move {
            let subscriptions = this.subscriptions.read().await;
            let subs = Self::get_keys_from_event(&event, this.min_prefix_match_len);
            let mut deliverded = HashSet::new();

            for sub in subs {
                for ((sub_type, client, name), (filter, sender)) in subscriptions.range(
                    &(
                        sub.clone(),
                        ConnectionId::new_empty(),
                        SubscriptionId::empty(),
                    )..,
                ) {
                    if sub_type != &sub {
                        break;
                    }

                    if deliverded.contains(client) || !filter.check_event(&event) {
                        continue;
                    }

                    let _ = sender.try_send(Response::Event((name, &event).into()));
                    deliverded.insert(*client);
                }
            }
        });
    }
}
