use anyhow::{bail, Result};
use tokio_retry::{
    strategy::{jitter, FibonacciBackoff},
    Retry,
};

use acter::api::SubscriptionStatus;

use crate::utils::random_users_with_random_space_under_template;

const TMPL: &str = r#"
version = "0.1"
name = "News Notifications Setup Template"

[inputs]
main = { type = "user", is-default = true, required = true, description = "The starting user" }
space = { type = "space", is-default = true, required = true, description = "The main user" }

[objects.example-news-one-image]
type = "news-entry"
slides = [
  { body = "This is the news section. Swipe down for more.", info = { size = 3264047, mimetype = "image/jpeg", thumbnail_info = { w = 400, h = 600, mimetype = "image/jpeg", size = 130511 }, w = 3840, h = 5760, "xyz.amorgan.blurhash" = "TQF=,g?uIo},={X5$c#+V@t2sRjF", thumbnail_url = "mxc://acter.global/aJhqfXrJRWXsFgWFRNlBlpnD" }, msgtype = "m.image", url = "mxc://acter.global/tVLtaQaErMyoXmcCroPZdfNG" }
]

"#;

#[tokio::test]
async fn like_on_news() -> Result<()> {
    let (users, _sync_states, space_id, _engine) =
        random_users_with_random_space_under_template("likeOnboost", 1, TMPL).await?;

    let first = users.first().expect("exists");
    let second_user = &users[1];

    // wait for sync to catch up
    let retry_strategy = FibonacciBackoff::from_millis(100).map(jitter).take(30);
    let news_entry = Retry::spawn(retry_strategy.clone(), || async {
        let news_entries = second_user.latest_news_entries(1).await?;
        if news_entries.len() != 1 {
            bail!("news entries not found found");
        }
        Ok(news_entries[0].clone())
    })
    .await?;

    // ensure we are expected to see these notifications
    let notif_settings = first.notification_settings().await?;
    let obj_id = news_entry.event_id().to_string();

    notif_settings
        .subscribe_object_push(obj_id.clone(), None)
        .await
        .expect("setting notifications subscription works");
    // ensure this has been locally synced
    Retry::spawn(retry_strategy, || async {
        let status = notif_settings
            .object_push_subscription_status(obj_id.clone(), None)
            .await?;
        if status != SubscriptionStatus::Subscribed {
            bail!("not yet subscribed");
        }
        Ok(())
    })
    .await?;

    let reactions = news_entry.reactions().await?;
    let notification_ev = reactions.send_like().await?;

    let notification_item = first
        .get_notification_item(space_id.to_string(), notification_ev.to_string())
        .await?;
    assert_eq!(notification_item.push_style(), "reaction");
    assert_eq!(
        notification_item
            .parent_id_str()
            .expect("parent is in like"),
        news_entry.event_id()
    );
    assert!(notification_item.body().is_none());
    assert_eq!(notification_item.reaction_key().as_deref(), Some("❤️"));
    let parent = notification_item.parent().expect("parent was found");
    assert_eq!(parent.title(), None);
    assert_eq!(parent.emoji(), "🚀"); // rocket
    assert_eq!(parent.object_id_str(), news_entry.event_id());

    Ok(())
}
