//! # Nostr Storage - Test suite
//!
//! This crate will storage events into a database. It will also build index to
//! find events by their tags, kind and references.
use super::*;
use futures::{StreamExt, TryStreamExt};
use nostr_rs_types::types::{Addr, Event, Filter, Id, Kind};
use serde_json::json;
use std::{
    collections::HashMap,
    fs::File,
    io::{BufRead, BufReader},
};

async fn setup_db<T>(db: &T)
where
    T: Storage,
{
    let events = include_str!("../tests/events.json")
        .lines()
        .map(|line| serde_json::from_str(&line).expect("valid"))
        .collect::<Vec<Event>>();

    for event in events {
        assert!(db.store(&event).await.expect("valid"));
    }

    while db.is_flushing() {
        tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
    }
}

pub async fn store_and_get<T>(db: &T)
where
    T: Storage,
{
    let json = "{\"content\":\"{\\\"lud06\\\":\\\"lnbc1p3a4wxvpp5x0pa6gr55fq5s9d3dxs0vz77mqxgdw63hhtgtlfz5zvm65847vnqdqqcqpjsp5402c8rtqxd4j97rnvuejuwl4sg473g6wg08d67fvn7qc4gtpkfks9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqmqz9gxqyjw5qrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glclleasn65surjcsqqqqlgqqqqqeqqjqyxj968tem9ps6ttm9ukv6ag4yc6qmgj2svrccfgp4n83fpktr3dsx6fq7grfzlqt982aaemahg9q29vzl9f627kh4j8h8xc2z2mtpdqqjlekah\\\",\\\"website\\\":\\\"\\\",\\\"nip05\\\":\\\"cesar@cesar.com.py\\\",\\\"picture\\\":\\\"https://pbs.twimg.com/profile_images/1175432935337537536/_Peu9vuJ_400x400.jpg\\\",\\\"display_name\\\":\\\"C\\\",\\\"about\\\":\\\"Rust and PHP\\\",\\\"name\\\":\\\"c\\\"}\",\"created_at\":1678476588,\"id\":\"3800c787a23288641c0b96cbcc87c26cbd3ea7bee53b7748422fdb100fb7b9f0\",\"kind\":0,\"pubkey\":\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\",\"sig\":\"c8a12ce96833e4cd67bce0e9e50f831262ef0f0c0cff5e56c38a0c90867ed1a6621e9692948ef5e85a7ca3726c3f0f43fa7e1992536bc457317123bca8784f5f\",\"tags\":[]}";

    let event: Event = serde_json::from_str(json).expect("valid");
    assert_eq!(true, db.store(&event).await.expect("valid"));
    assert_eq!(false, db.store(&event).await.expect("valid"));

    let event1 = db.get_event(&event.id).await.expect("something");
    assert_eq!(event1, Some(event));
}

pub async fn store_and_get_by_partial_key<T>(db: &T)
where
    T: Storage,
{
    let json = "{\"content\":\"{\\\"lud06\\\":\\\"lnbc1p3a4wxvpp5x0pa6gr55fq5s9d3dxs0vz77mqxgdw63hhtgtlfz5zvm65847vnqdqqcqpjsp5402c8rtqxd4j97rnvuejuwl4sg473g6wg08d67fvn7qc4gtpkfks9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqmqz9gxqyjw5qrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glclleasn65surjcsqqqqlgqqqqqeqqjqyxj968tem9ps6ttm9ukv6ag4yc6qmgj2svrccfgp4n83fpktr3dsx6fq7grfzlqt982aaemahg9q29vzl9f627kh4j8h8xc2z2mtpdqqjlekah\\\",\\\"website\\\":\\\"\\\",\\\"nip05\\\":\\\"cesar@cesar.com.py\\\",\\\"picture\\\":\\\"https://pbs.twimg.com/profile_images/1175432935337537536/_Peu9vuJ_400x400.jpg\\\",\\\"display_name\\\":\\\"C\\\",\\\"about\\\":\\\"Rust and PHP\\\",\\\"name\\\":\\\"c\\\"}\",\"created_at\":1678476588,\"id\":\"3800c787a23288641c0b96cbcc87c26cbd3ea7bee53b7748422fdb100fb7b9f0\",\"kind\":0,\"pubkey\":\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\",\"sig\":\"c8a12ce96833e4cd67bce0e9e50f831262ef0f0c0cff5e56c38a0c90867ed1a6621e9692948ef5e85a7ca3726c3f0f43fa7e1992536bc457317123bca8784f5f\",\"tags\":[]}";

    let event: Event = serde_json::from_str(json).expect("valid");
    assert_eq!(true, db.store(&event).await.expect("valid"));
    assert_eq!(false, db.store(&event).await.expect("valid"));

    let event1 = db.get_event(&event.id[0..2]).await.expect("something");
    assert_eq!(event1, Some(event));
}

pub async fn records_are_sorted_by_date_desc_by_default<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let pk: Addr = "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"
        .try_into()
        .expect("pk");

    let vec: Vec<Event> = db
        .get_by_filter(Filter::default())
        .await
        .expect("set of results")
        .try_collect()
        .await
        .expect("valid");

    let dates = vec.iter().map(|e| e.created_at()).collect::<Vec<_>>();
    let mut sorted_dates = dates.clone();
    sorted_dates.sort_by(|a, b| b.cmp(a));

    assert_eq!(vec.len(), 10002);
    assert_eq!(dates, sorted_dates);
}

pub async fn records_are_sorted_by_date_desc<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let pk: Id = "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"
        .try_into()
        .expect("pk");

    let vec: Vec<Event> = db
        .get_by_filter(Filter {
            authors: vec![pk],
            limit: 10,
            ..Default::default()
        })
        .await
        .expect("set of results")
        .try_collect()
        .await
        .expect("valid");

    let dates = vec.iter().map(|e| e.created_at()).collect::<Vec<_>>();
    let mut sorted_dates = dates.clone();
    sorted_dates.sort_by(|a, b| b.cmp(a));

    assert_eq!(vec.len(), 10);
    assert_eq!(dates, sorted_dates);
}

pub async fn filter_by_references<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let filter: Filter = serde_json::from_value(json!({
        "#e": [
            "f513f1422ee5dbf30f57118b6cc34e788746e589a9b07be767664a164c57b9b1"
        ],
        "#p": [
            "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
        ]
    }))
    .expect("valid filter");

    let related_events: Vec<Event> = db
        .get_by_filter(filter)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(related_events.len(), 1);
}

pub async fn filter_by_references_zero_match<T>(db: &T)
where
    T: Storage,
{
    setup_db(db);

    let filter: Filter = serde_json::from_value(json!({
        "#e": [
            "42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5"
        ],
        "#p": [
            "36ce9f55828b06f4f45c7f7292ae58362f4abe746938888f82e56fe6fb7ffb2c"
        ]
    }))
    .expect("valid filter");

    let related_events: Vec<Event> = db
        .get_by_filter(filter)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(related_events.len(), 0);
}

pub async fn filter_by_references_and_kind<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let filter: Filter = serde_json::from_value(json!({
        "kinds": [7, 1],
        "#e": [
            "42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5"
        ],
    }))
    .expect("valid filter");

    let related_events: Vec<Event> = db
        .get_by_filter(filter)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(related_events.len(), 3);
}

pub async fn get_event_and_related_events<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let id: Id = "42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5"
        .try_into()
        .expect("pk");

    let events: Vec<Event> = db
        .get_by_filter(Filter {
            ids: vec![id],
            ..Default::default()
        })
        .await
        .expect("events")
        .try_collect()
        .await
        .expect("valid");

    assert_eq!(events.len(), 1);

    let filter: Filter = serde_json::from_value(json!({
        "#e": [
            "42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5"
        ],
    }))
    .expect("valid filter");

    let related_events: Vec<Event> = db
        .get_by_filter(filter)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(related_events.len(), 2_538);

    let mut kinds = related_events.iter().map(|x| x.kind()).collect::<Vec<_>>();
    kinds.sort();
    kinds.dedup();

    assert_eq!(Kind::Reaction, kinds[0]);
    assert_eq!(Kind::Unknown(42), kinds[1]);
}

pub async fn filter_by_authors<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;
    let query = Filter {
        authors: vec![
            "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
                .try_into()
                .expect("pk"),
            "38fb689f2fb92d932d457b1ea56715292bdf2140b2c7d282e8b8e8d644483ad6"
                .try_into()
                .expect("pk"),
        ],
        ..Default::default()
    };
    let records: Vec<Event> = db
        .get_by_filter(query)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(records.len(), 27);
}

pub async fn filter_by_author<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;
    let query = Filter {
        authors: vec![
            "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
                .try_into()
                .expect("pk"),
        ],
        ..Default::default()
    };
    let records: Vec<Event> = db
        .get_by_filter(query)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(records.len(), 3);
}

pub async fn filter_by_author_and_kinds<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;
    let query = Filter {
        authors: vec![
            "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
                .try_into()
                .expect("pk"),
        ],
        kinds: vec![Kind::ShortTextNote, Kind::Reaction],
        ..Default::default()
    };
    let records: Vec<Event> = db
        .get_by_filter(query)
        .await
        .expect("iterator")
        .try_collect()
        .await
        .expect("valid");
    assert_eq!(records.len(), 2);
}

pub async fn filter_by_kind<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;
    let query = Filter {
        kinds: vec![Kind::ShortTextNote],
        ..Default::default()
    };
    let records: Vec<Event> = db
        .get_by_filter(query)
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");

    assert_eq!(records.len(), 1_511);
    records
        .iter()
        .map(|x| x.kind())
        .for_each(|x| assert_eq!(x, Kind::ShortTextNote));
}

pub async fn get_local_events<T>(db: &T)
where
    T: Storage,
{
    setup_db(db).await;

    let events_from_filter: Vec<Event> = db
        .get_by_filter(Filter {
            limit: 10,
            ..Default::default()
        })
        .await
        .expect("valid")
        .try_collect()
        .await
        .expect("valid");

    for event in events_from_filter.iter() {
        db.set_local_event(event).await.expect("valid");
    }

    assert_eq!(10, events_from_filter.len());

    let records: Vec<Event> = db
        .get_local_events(None)
        .await
        .expect("valid iterator")
        .try_collect()
        .await
        .expect("valid");

    assert_eq!(10, records.len())
}
