use actix::*;
use rand::{self, rngs::ThreadRng, Rng};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

pub type ID = i64;

#[derive(Debug, Clone)]
pub struct Session {
    pub addr: Recipient<Event>,
}

#[derive(Message)]
#[rtype(result = "ID")]
pub struct Connect {
    pub addr: Recipient<Event>,
}

#[derive(Message)]
#[rtype(result = "()")]
pub struct Disconnect {
    pub id: ID,
}

#[derive(Message, Clone, Debug, Serialize, Deserialize)]
#[rtype(result = "()")]
pub enum Event {
    HealthcheckChange { app_id: i32, alive: bool },
}

#[derive(Default)]
pub struct EventBroker {
    rng: ThreadRng,
    sessions: HashMap<ID, Session>,
}

impl Supervised for EventBroker {}

impl SystemService for EventBroker {}

impl Actor for EventBroker {
    type Context = Context<Self>;

    fn started(&mut self, _: &mut Self::Context) {
        tracing::info!("EventBroker - Started");
    }

    fn stopped(&mut self, _: &mut Self::Context) {
        tracing::error!("EventBroker - Shutdown");
    }
}

impl Handler<Connect> for EventBroker {
    type Result = ID;
    fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
        tracing::info!("Session connected");
        let id = self.rng.gen::<ID>();
        self.sessions.insert(id, Session { addr: msg.addr });
        id
    }
}

impl Handler<Disconnect> for EventBroker {
    type Result = ();

    fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
        tracing::info!("Session disconnected");
        self.sessions.remove(&msg.id);
    }
}

impl Handler<Event> for EventBroker {
    type Result = ();

    fn handle(&mut self, msg: Event, _ctx: &mut Self::Context) -> Self::Result {
        tracing::info!("Event received");
        for (_, ses) in self.sessions.iter() {
            let _ = ses.addr.do_send(msg.clone());
        }
    }
}
