use log::debug;
use once_cell::sync::Lazy;
use tokio::sync::broadcast;

use crate::modules::application::ApplicationFeatureStatus;

use super::{
    interpret::{InterpretError, Outcome},
    topology::TopologyStatus,
};

#[derive(Debug, Clone)]
pub enum HarmonyEvent {
    HarmonyStarted,
    HarmonyFinished,
    InterpretExecutionStarted {
        execution_id: String,
        topology: String,
        interpret: String,
        score: String,
        message: String,
    },
    InterpretExecutionFinished {
        execution_id: String,
        topology: String,
        interpret: String,
        score: String,
        outcome: Result<Outcome, InterpretError>,
    },
    TopologyStateChanged {
        topology: String,
        status: TopologyStatus,
        message: Option<String>,
    },
    ApplicationFeatureStateChanged {
        topology: String,
        application: String,
        feature: String,
        status: ApplicationFeatureStatus,
    },
}

static HARMONY_EVENT_BUS: Lazy<broadcast::Sender<HarmonyEvent>> = Lazy::new(|| {
    // TODO: Adjust channel capacity
    let (tx, _rx) = broadcast::channel(100);
    tx
});

pub fn instrument(event: HarmonyEvent) -> Result<(), &'static str> {
    if cfg!(any(test, feature = "testing")) {
        let _ = event; // Suppress the "unused variable" warning for `event`
        Ok(())
    } else {
        match HARMONY_EVENT_BUS.send(event) {
            Ok(_) => Ok(()),
            Err(_) => Err("send error: no subscribers"),
        }
    }
}

pub async fn subscribe<F, Fut>(name: &str, mut handler: F)
where
    F: FnMut(HarmonyEvent) -> Fut + Send + 'static,
    Fut: Future<Output = bool> + Send,
{
    let mut rx = HARMONY_EVENT_BUS.subscribe();
    debug!("[{name}] Service started. Listening for events...");
    loop {
        match rx.recv().await {
            Ok(event) => {
                if !handler(event).await {
                    debug!("[{name}] Handler requested exit.");
                    break;
                }
            }
            Err(broadcast::error::RecvError::Lagged(n)) => {
                debug!("[{name}] Lagged behind by {n} messages.");
            }
            Err(_) => break,
        }
    }
}
