use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
use twitch_irc::{login::StaticLoginCredentials, message::ServerMessage, SecureTCPTransport, TwitchIRCClient};
use dotenv::dotenv;
use std::{env, sync::Arc};

use super::bot_objects::listener::Listener;



/// Twitch chat bot
pub struct Bot 
{
    /// Prefix for commands
    _prefix: char,
    /// inbound chat msg stream
    incoming_msgs: Mutex<UnboundedReceiver<ServerMessage>>,
    /// outbound chat client msg stream
    pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
    /// joined channels
    botchannels: Vec<String>,
    /// listeners
    listeners: Vec<Listener>,
}


impl Bot
{
    /// Creates a new `Bot` using env variables
    /// 
    /// Be sure the following is defined in an `.env` file
    /// - login_name
    /// - access_token
    /// - bot_channels
    /// - prefix
    /// - bot_admins
    pub fn new() -> Bot {

        dotenv().ok();
        let bot_login_name = env::var("login_name").unwrap().to_owned();
        let oauth_token = env::var("access_token").unwrap().to_owned();
        let prefix = env::var("prefix")
            .unwrap()
            .to_owned()
            .chars()
            .next()
            .expect("ERROR : when defining prefix");

        let mut botchannels = Vec::new();

        for chnl in env::var("bot_channels").unwrap().split(',') {
                botchannels.push(chnl.to_owned());
        }

        Bot::new_from(bot_login_name, oauth_token, prefix, botchannels)
       

    }

    /// Creates a new `Bot` using bot information
    /// 
    /// Bot joined channels will include channels from `.env` and `botchannels` argument
    pub fn new_from(bot_login_name:String,oauth_token:String,prefix:char,botchannels:Vec<String>) -> Bot {

        dotenv().ok();
        let bot_login_name = bot_login_name;

        let config = twitch_irc::ClientConfig::new_simple(StaticLoginCredentials::new(
            bot_login_name.to_owned(),
            Some(oauth_token.to_owned()),
        ));

        let (incoming_messages, client) =
            TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config);

        let mut botchannels_all = Vec::new();
        botchannels_all.extend(botchannels);

        for chnl in env::var("bot_channels").unwrap().split(',') {
            botchannels_all.push(chnl.to_owned());
        }

        Bot {
             _prefix : prefix,
             incoming_msgs : Mutex::new(incoming_messages),
             client,
             botchannels : botchannels_all,
             listeners : vec![],
        }
    }

    /// Runs the bot 
    pub async fn run(self) {

        for chnl in &self.botchannels {
            self.client.join(chnl.to_owned()).unwrap();
        }
        

        let bot = Arc::new(self);

        let join_handle = tokio::spawn(async move {
            
            let mut in_msgs_lock = bot.incoming_msgs.lock().await;
            
            while let Some(message) = in_msgs_lock.recv().await {
                // dbg!("Received message: {:?}", message.clone());
                
                for listener in &(*bot).listeners {
                    
                    let a = listener.clone();
                    if a.cond_triggered(bot.clone(),message.clone()) {
                        
                        let _ = listener.execute_fn(bot.clone(),message.clone()).await;
                    }
                }
            }
            drop(in_msgs_lock);
        });
        
        join_handle.await.unwrap();
    }

    /// Loads a `Listener` into the bot
    pub fn load_listener(&mut self,l : Listener) {
        self.listeners.push(l);
    }
    
}