use futures::future::join_all;
use nostr_rs_client::Pool;
use nostr_rs_relayer::Relayer;
use nostr_rs_storage_base::Storage;
use nostr_rs_types::types::{Addr, Filter, Id};
use tokio::{net::TcpListener, task::JoinHandle};
use url::Url;

pub struct Stoppable(Option<Vec<JoinHandle<()>>>);

impl From<Vec<JoinHandle<()>>> for Stoppable {
    fn from(value: Vec<JoinHandle<()>>) -> Self {
        Self(Some(value))
    }
}

impl Drop for Stoppable {
    fn drop(&mut self) {
        if let Some(tasks) = self.0.take() {
            for join_handle in tasks.into_iter() {
                join_handle.abort();
            }
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Relayer: {0}")]
    Relayer(#[from] nostr_rs_relayer::Error),

    #[error("Client error: {0}")]
    Client(#[from] nostr_rs_client::Error),
}

pub struct PersonalRelayer<T: Storage + Send + Sync + 'static> {
    relayer: Relayer<T>,
    accounts: Vec<Id>,
}

impl<T: Storage + Send + Sync + 'static> PersonalRelayer<T> {
    pub async fn new(storage: T, accounts: Vec<Id>, client_urls: Vec<Url>) -> Result<Self, Error> {
        let pool = Pool::new_with_clients(client_urls);

        join_all(
            accounts
                .iter()
                .map(|account| {
                    pool.subscribe(
                        Filter {
                            authors: vec![account.clone()],
                            ..Default::default()
                        }
                        .into(),
                    )
                })
                .collect::<Vec<_>>(),
        )
        .await
        .into_iter()
        .collect::<Result<Vec<_>, _>>()?;

        Ok(Self {
            relayer: Relayer::new(Some(storage), Some(pool))?,
            accounts,
        })
    }

    pub fn main(self, server: TcpListener) -> Result<Stoppable, Error> {
        let tasks = vec![self.relayer.main(server)?, tokio::spawn(async move {})];
        Ok(tasks.into())
    }
}
