use std::{collections::{HashMap, HashSet}, sync::Arc, time::Duration};

use once_cell::sync::Lazy;
use tokio::{sync::Mutex, time::sleep};
use twitch_gql_rs::structs::{Channels, GameDirectory};

#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Channel {
    pub channel_id: String,
    pub channel_login: String
}

const MAX_ATTEMPTS: u32 = 3;

pub static DROP_CASH: Lazy<Arc<Mutex<HashSet<String>>>> = Lazy::new(|| Arc::new(Mutex::new(HashSet::new())));

pub static CHANNEL_IDS: Lazy<Arc<Mutex<HashSet<Channel>>>> = Lazy::new(|| Arc::new(Mutex::new(HashSet::new())));

pub static DEFAULT_CHANNELS: Lazy<Arc<Mutex<HashMap<String, HashSet<GameDirectory>>>>> = Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));

pub static ALLOW_CHANNELS: Lazy<Arc<Mutex<HashMap<String, HashSet<Channels>>>>> = Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));

#[macro_export]
macro_rules! retry {
    ($func:expr) => {
        retry_backup(|| $func).await.expect("Retry failed after MAX_ATTEMPTS attempts")
    };
}

pub async fn retry_backup<F, Fut, T, E> (mut f: F) -> Result<T, E> where F: FnMut() -> Fut, Fut: Future<Output = Result<T, E>> {
    let mut attempts = 0;
    loop {
        match f().await {
            Ok(t) => return Ok(t),
            Err(e) => {
                attempts += 1;
                if attempts > MAX_ATTEMPTS {
                    return Err(e);
                } else {
                    sleep(Duration::from_secs(5)).await
                }
            }
        }
    }
}