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

pub async fn user_subscribe(user: &CaracalUser) -> Result<(), UserError> {
    let Ok(signer_pubk) = user.public_key().await else {
        log::warn!("Cannot get public key from signer");
        return Ok(());
    };

    let Ok(contacts) =
        user.client.get_contact_list(dur_parse("5s").unwrap()).await
    else {
        log::warn!("Failed to retrieve contact list");
        return Ok(());
    };

    let now = Timestamp::now();
    let sub_id_user = SubscriptionId::new("user");
    let sub_id_fpacks = SubscriptionId::new("follow_packs");

    user.client.connect().await;
    user.client.unsubscribe(&sub_id_user).await;
    user.client.unsubscribe(&sub_id_fpacks).await;

    let mut follow_pubkeys: Vec<PublicKey> =
        contacts.into_iter().map(|c| c.public_key).collect();
    follow_pubkeys.push(signer_pubk);

    let subs = vec![
        // Metadata and relay+inbox lists
        Filter::new()
            .kind(Kind::Metadata)
            .kind(Kind::RelayList)
            .kind(Kind::InboxRelays)
            .since(now - dur_parse("6mon").unwrap())
            .limit(30),
        // Contact and bookmarks
        Filter::new()
            .kind(Kind::ContactList)
            .kind(Kind::Bookmarks)
            // Payment targets
            .kind(Kind::Custom(10133))
            .since(now - dur_parse("90d").unwrap())
            .limit(10),
        // Notes, reactions and reposts
        Filter::new()
            .kind(Kind::TextNote)
            .kind(Kind::LongFormTextNote)
            .kind(Kind::Reaction)
            .kind(Kind::GenericRepost)
            .kind(Kind::Repost)
            .since(now - dur_parse("90d").unwrap())
            .limit(100),
    ];

    for pubkey in &follow_pubkeys {
        for sub in &subs {
            if let Err(err) = user
                .client
                .subscribe_with_id(
                    SubscriptionId::new(format!("{pubkey}")),
                    sub.clone().author(*pubkey),
                    None,
                )
                .await
            {
                eprintln!("Error subscribing: {err}")
            }
        }
    }

    // Follow packs
    user.client
        .subscribe_with_id(
            sub_id_fpacks,
            Filter::new().kind(Kind::Custom(39089)),
            None,
        )
        .await?;

    // Get reactions for notes we authored
    for event in user
        .client
        .fetch_combined_events(
            Filter::new()
                .kind(Kind::TextNote)
                .kind(Kind::LongFormTextNote)
                .author(signer_pubk),
            dur_parse("3s").unwrap(),
        )
        .await?
    {
        user.sub(
            Filter::new()
                .kind(Kind::Reaction)
                .kind(Kind::Repost)
                .kind(Kind::GenericRepost)
                .event(event.id),
        )
        .await?;
    }

    Ok(())
}

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
            {
                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;
                    };

                    /*
                    if let Some(RelayMetadata::Write) = metadata {
                        println!("{} {}", "=>".cyan(), url.to_string().bold().purple());
                    }
                    */
                }
            }

            Ok(false)
        })
        .await?;

    Ok(())
}
