//! Bifrost Library - Core logic for the Zenoh-NATS bridge.

use async_nats::Client as NatsClient;
use futures::StreamExt;
use std::sync::Arc;
use tracing::{debug, error, info};
use zenoh::Session;

pub mod ragnarok;
pub use ragnarok::{EigenstateFrame, HilbertPulse};

/// Maps a Zenoh key expression to a NATS subject.
/// e.g. "mimirs/signal/agent1" -> "MIMIRS.SIGNAL.AGENT1"
pub fn zenoh_key_to_nats_subject(key: &str) -> String {
    key.replace('/', ".").to_uppercase()
}

/// Maps a NATS subject to a Zenoh key expression.
/// e.g. "MIMIRS.SIGNAL.AGENT1" -> "mimirs/signal/agent1"
pub fn nats_subject_to_zenoh_key(subject: &str) -> String {
    subject.replace('.', "/").to_lowercase()
}

/// Task that forwards Zenoh samples to NATS.
pub async fn bridge_zenoh_to_nats(zenoh: Arc<Session>, nats: Arc<NatsClient>, prefix: String) {
    info!("Starting Zenoh -> NATS bridge on {}", prefix);
    let subscriber = match zenoh.declare_subscriber(&prefix).await {
        Ok(sub) => sub,
        Err(e) => {
            error!("Failed to declare Zenoh subscriber: {}", e);
            return;
        }
    };

    while let Ok(sample) = subscriber.recv_async().await {
        let key = sample.key_expr().as_str();
        let subject = zenoh_key_to_nats_subject(key);

        debug!("Forwarding Zenoh [{}] -> NATS [{}]", key, subject);

        let payload = sample.payload().to_bytes();
        if let Err(e) = nats.publish(subject, payload.to_vec().into()).await {
            error!("Failed to forward to NATS: {}", e);
        }
    }
}

/// Task that forwards NATS messages to Zenoh.
pub async fn bridge_nats_to_zenoh(zenoh: Arc<Session>, nats: Arc<NatsClient>, subject: String) {
    info!("Starting NATS -> Zenoh bridge on {}", subject);
    let mut subscriber = match nats.subscribe(subject).await {
        Ok(sub) => sub,
        Err(e) => {
            error!("Failed to subscribe to NATS: {}", e);
            return;
        }
    };

    while let Some(message) = subscriber.next().await {
        let subject = message.subject.as_str();
        let key = nats_subject_to_zenoh_key(subject);

        debug!("Forwarding NATS [{}] -> Zenoh [{}]", subject, key);

        if let Err(e) = zenoh.put(&key, message.payload).await {
            error!("Failed to forward to Zenoh: {}", e);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_mapping() {
        assert_eq!(
            zenoh_key_to_nats_subject("mimirs/signal/agent1"),
            "MIMIRS.SIGNAL.AGENT1"
        );
        assert_eq!(
            nats_subject_to_zenoh_key("MIMIRS.SIGNAL.AGENT1"),
            "mimirs/signal/agent1"
        );
    }
}
