use crate::user::CaracalUser;
use duration_str::parse as dur_parse;
use nostr_sdk::prelude::*;
use std::sync::Arc;

pub async fn user_subscribe(user: &CaracalUser) {
    let Ok(contacts) =
        user.client.get_contact_list(dur_parse("5s").unwrap()).await
    else {
        eprintln!("Failed to retrieve contact list");
        return;
    };

    let now = Timestamp::now();
    let sub_id = SubscriptionId::new("user");

    let Ok(signer_pubk) = user.signer.get_public_key().await else {
        eprintln!("Cannot get public key from signer");
        return;
    };

    user.client.connect().await;
    user.client.unsubscribe(&sub_id).await;

    let subscription = Filter::new()
        .kind(Kind::RelayList)
        .kind(Kind::InboxRelays)
        .kind(Kind::Metadata)
        .kind(Kind::ContactList)
        .kind(Kind::TextNote)
        .kind(Kind::LongFormTextNote)
        .kind(Kind::Reaction)
        // Payment targets
        .kind(Kind::Custom(10133))
        .since(now - dur_parse("90d").unwrap())
        .author(signer_pubk)
        .authors(contacts.into_iter().map(|c| c.public_key));

    if let Err(e) = user
        .client
        .subscribe_with_id(sub_id, subscription, None)
        .await
    {
        eprintln!("Error subscribing: {e}")
    }
}

pub async fn prepare_nostr_client(client: Arc<Client>) {
    client.connect().await;

    let _ = client
        .subscribe(
            Filter::new()
                .kind(Kind::Metadata)
                .limit(1000)
                .since(Timestamp::now() - dur_parse("90d").unwrap()),
            None,
        )
        .await;
}

pub async fn handle_nostr_notifications(
    client: Arc<Client>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    client
        .handle_notifications(|notification| async {
            if let RelayPoolNotification::Event {
                relay_url: _,
                subscription_id: _,
                event,
            } = notification
            {
                match event.kind {
                    Kind::TextNote => {
                        if let Err(err) = client
                            .subscribe(
                                Filter::new()
                                    .kind(Kind::Metadata)
                                    .author(event.pubkey)
                                    .limit(1),
                                None,
                            )
                            .await
                        {
                            eprintln!("Metadata sub failed: {err}");
                        }
                    }
                    Kind::RelayList => {
                        for (url, _metadata) in
                            nip65::extract_relay_list(&event)
                        {
                            let Ok(_) = client.add_discovery_relay(url).await
                            else {
                                continue;
                            };

                            let Ok(_) = client.add_read_relay(url).await else {
                                continue;
                            };
                        }
                    }
                    _ => {}
                }
            } else if let RelayPoolNotification::Message {
                relay_url: _,
                message,
            } = notification
                && let RelayMessage::Event {
                    subscription_id: _,
                    ref event,
                } = message
                && let Kind::RelayList = event.kind
            {
                let urls: Vec<_> = nip65::extract_relay_list(event)
                    .map(|(url, _)| url)
                    .collect();

                if !urls.is_empty() {
                    let _ = client
                        .subscribe_to(
                            urls,
                            Filter::new()
                                .kind(Kind::Metadata)
                                .since(
                                    Timestamp::now()
                                        - dur_parse("90d").unwrap(),
                                )
                                .limit(200),
                            None,
                        )
                        .await;
                }

                for (url, _metadata) in nip65::extract_relay_list(event) {
                    let Ok(_) = client.add_discovery_relay(url).await else {
                        continue;
                    };

                    let Ok(_) = client.add_read_relay(url).await else {
                        continue;
                    };
                }
            }

            Ok(false)
        })
        .await?;

    Ok(())
}
