use nostr_rs_types::types::{Event, Filter, Tag};

#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Subscription {
    author: Option<Vec<u8>>,
    ref_public_key: Option<Vec<u8>>,
    ref_id: Option<Vec<u8>>,
    id: Option<Vec<u8>>,
    kind: Option<u32>,
}

impl Subscription {
    #[inline]
    pub fn from_filters(filters: &[Filter]) -> Vec<Subscription> {
        let mut subs = vec![];
        filters.iter().for_each(|filter| {
            let authors: Vec<Option<Vec<u8>>> = if filter.authors.is_empty() {
                vec![None]
            } else {
                filter
                    .authors
                    .iter()
                    .map(|author| Some(author.to_vec()))
                    .collect()
            };
            let ref_public_keys = if filter.references_to_public_key.is_empty() {
                vec![None]
            } else {
                filter
                    .references_to_public_key
                    .iter()
                    .map(|public_key| Some((*public_key).to_vec()))
                    .collect()
            };
            let ref_ids = if filter.references_to_event.is_empty() {
                vec![None]
            } else {
                filter
                    .references_to_event
                    .iter()
                    .map(|id| Some((*id).to_vec()))
                    .collect()
            };
            let kind = if filter.kinds.is_empty() {
                vec![None]
            } else {
                filter
                    .kinds
                    .iter()
                    .map(|kind| Some((*kind).into()))
                    .collect()
            };

            let ids = if filter.ids.is_empty() {
                vec![None]
            } else {
                filter.ids.iter().map(|id| Some((*id).to_vec())).collect()
            };

            for author in authors.iter() {
                for id in ids.iter() {
                    for ref_public_key in ref_public_keys.iter() {
                        for ref_id in ref_ids.iter() {
                            for kind in kind.iter() {
                                subs.push(Subscription {
                                    id: id.clone(),
                                    ref_public_key: ref_public_key.clone(),
                                    author: author.clone(),
                                    ref_id: ref_id.clone(),
                                    kind: *kind,
                                });
                            }
                        }
                    }
                }
            }
        });

        subs
    }

    #[inline]
    pub fn from_event(event: &Event) -> Vec<Subscription> {
        let kind = event.kind().into();
        let public_keys = vec![None, Some(event.author().as_ref().to_vec())];
        let id = vec![None, Some(event.id.as_ref().to_vec())];
        let kind = [None, Some(kind)];
        let mut ref_public_keys = vec![None];
        let mut ref_ids = vec![None];

        event.tags().iter().for_each(|tag| match tag {
            Tag::Event(x) => {
                ref_ids.push(Some(x.id.as_ref().to_vec()));
            }
            Tag::PubKey(x) => {
                ref_public_keys.push(Some(x.id.as_ref().to_vec()));
            }
            _ => {}
        });

        let mut subs = vec![];

        for ref_public_key in ref_public_keys.iter() {
            for ref_id in ref_ids.iter() {
                for public_key in public_keys.iter() {
                    for id in id.iter() {
                        for kind in kind.iter() {
                            subs.push(Subscription {
                                ref_id: ref_id.clone(),
                                ref_public_key: ref_public_key.clone(),
                                author: public_key.clone(),
                                id: id.clone(),
                                kind: *kind,
                            });
                        }
                    }
                }
            }
        }

        subs
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use nostr_rs_types::{types::Addr, Request};

    #[test]
    fn test_no_listen_to_all() {
        let request: Request = serde_json::from_str("[\"REQ\",\"6440d5279e350\",{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"since\":1681967101},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[1,3,6,7,9735],\"since\":1681967101},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"#e\":[\"2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a\",\"a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1\"],\"kinds\":[1,6,7,9735]}]").expect("valid");
        let mut subscriptions =
            Subscription::from_filters(&request.as_request().expect("req").filters);
        subscriptions.sort();

        assert!(subscriptions
            .binary_search(&Subscription::default())
            .is_err())
    }

    #[test]
    fn from_filters() {
        let request: Request = serde_json::from_str("[\"REQ\",\"1298169700973717\",{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"since\":1681939304},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[1,3,6,7,9735],\"since\":1681939304},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"#e\":[\"2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a\",\"a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1\"],\"kinds\":[1,6,7,9735]}]").expect("valid object");
        let mut subscriptions =
            Subscription::from_filters(&request.as_request().expect("req").filters);
        subscriptions.sort();

        assert!(subscriptions
            .binary_search(&Subscription::default())
            .is_err())
    }

    #[test]
    fn from_event() {
        let new_event: Request = serde_json::from_str(r#"["EVENT", {"kind":1,"content":"Pong","tags":[["e","9508850d7ddc8ef58c8b392236c49d472dc23fa11f4e73eb5475dfb099ddff42","","root"],["e","2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a","","reply"],["p","39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],["p","ee7202ad91459e013bfef263c59e47deb0163a5e7651b026673765488bfaf102"]],"created_at":1681938616,"pubkey":"a42007e33cfa25673b26f46f39df039aa6003258a68dc88f1f1e0447607aedb3","id":"e862fe23daf52ab09b36a37fa91ca3743e0c323e630e8627891212ca147c2da9","sig":"9036150a6c8a32933cffcc42aec4d2109a22e9f10d1c3860c0435a925e6386babb7df5c95fcf68c8ed6a9726a1f07225af663d0b068eb555014130aad21674fc","meta":{"revision":0,"created":1681939266488,"version":0},"$loki":108}]"#).expect("value");
        let mut subscriptions = Subscription::from_event(&new_event.as_event().expect("event"));
        subscriptions.sort();

        let pk: Addr = "39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"
            .try_into()
            .expect("id");

        let id: Addr = "e862fe23daf52ab09b36a37fa91ca3743e0c323e630e8627891212ca147c2da9"
            .try_into()
            .expect("id");

        let ref_id: Addr = "2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a"
            .try_into()
            .expect("id");

        let author: Addr = "a42007e33cfa25673b26f46f39df039aa6003258a68dc88f1f1e0447607aedb3"
            .try_into()
            .expect("id");

        let expected = vec![
            Subscription {
                ref_public_key: Some(pk.as_ref().to_vec()),
                ..Subscription::default()
            },
            Subscription {
                id: Some(id.as_ref().to_vec()),
                ref_public_key: Some(pk.as_ref().to_vec()),
                ..Subscription::default()
            },
            Subscription {
                id: Some(id.as_ref().to_vec()),
                ref_id: Some(ref_id.as_ref().to_vec()),
                ref_public_key: Some(pk.as_ref().to_vec()),
                ..Subscription::default()
            },
            Subscription {
                author: Some(author.as_ref().to_vec()),
                kind: Some(1),
                ..Subscription::default()
            },
            Subscription {
                author: Some(author.as_ref().to_vec()),
                ..Subscription::default()
            },
            Subscription {
                id: Some(id.as_ref().to_vec()),
                ref_id: Some(ref_id.as_ref().to_vec()),
                author: Some(author.as_ref().to_vec()),
                ..Subscription::default()
            },
        ];

        assert_eq!(subscriptions.len(), 72);
        expected.iter().enumerate().for_each(|(i, sub)| {
            assert!(
                subscriptions.binary_search(sub).is_ok(),
                "{} -> {:?}",
                i,
                sub
            );
        });
        assert!(subscriptions
            .binary_search(&Subscription::default())
            .is_ok());
    }
}
