//! Zenoh Queryable handler for responding to peer memory queries.
//!
//! This module provides `ZenohQueryable`, a handler that listens for
//! memory queries from peer nodes via Zenoh and responds with results
//! from the local well (Urdarbrunnr).

use std::sync::Arc;

use mimirs_core::{Memory, MimirError, RecallQuery};
use mimirs_store::Urdarbrunnr;

/// A Zenoh queryable handler that responds to memory queries from peer nodes.
///
/// Listens on the Zenoh queryable topic `mimirs/query/memory` and responds
/// with matching memories from the local well.
///
/// # Example
///
/// ```no_run
/// use std::sync::Arc;
/// use mimirs_store::Urdarbrunnr;
/// use mimirs_agent::queryable::ZenohQueryable;
///
/// # async fn example(well: Arc<Urdarbrunnr>, session: zenoh::Session) {
/// let handler = ZenohQueryable::new(well);
/// handler.spawn(session).await.unwrap();
/// # }
/// ```
pub struct ZenohQueryable {
    well: Arc<Urdarbrunnr>,
}

impl ZenohQueryable {
    /// Creates a new ZenohQueryable handler with the given well reference.
    pub fn new(well: Arc<Urdarbrunnr>) -> Self {
        Self { well }
    }

    /// Spawns the Zenoh queryable handler.
    ///
    /// This declares a Zenoh queryable on `mimirs/query/memory/>` that
    /// receives queries from peer nodes and responds with matching memories
    /// from the local well.
    pub async fn spawn(&self, session: zenoh::Session) -> Result<(), MimirError> {
        let well = self.well.clone();

        let queryable = session
            .declare_queryable("mimirs/query/memory")
            .await
            .map_err(|e| MimirError::Internal(format!("Failed to declare Zenoh queryable: {}", e)))?;

        tracing::info!("ZenohQueryable listening on mimirs/query/memory");

        // Spawn a task to handle incoming queries
        tokio::spawn(async move {
            loop {
                match queryable.recv_async().await {
                    Ok(query) => {
                        let query_str = query.key_expr().as_str().to_string();
                        // Extract the actual query from the key expression
                        // Key format: mimirs/query/memory/{query_text}
                        let query_text = query_str
                            .strip_prefix("mimirs/query/memory/")
                            .unwrap_or("");

                        tracing::debug!(query = %query_text, "Received peer memory query");

                        let recall_query = RecallQuery::new(query_text).with_limit(10);
                        match well.minni(recall_query).await {
                            Ok(results) => {
                                // Convert results to Memory structs for serialization
                                let mut memories = Vec::new();
                                for result in &results {
                                    match well.get_memory(result.id) {
                                        Ok(mem) => memories.push(mem),
                                        Err(e) => {
                                            tracing::warn!(memory_id = %result.id, error = %e, "Failed to fetch memory for peer response");
                                        }
                                    }
                                }

                                // Serialize and send response
                                match bincode::serialize(&memories) {
                                    Ok(payload) => {
                                        use zenoh::Wait;
                                        if let Err(e) = query.reply(query.key_expr(), payload).wait() {
                                            tracing::warn!(error = %e, "Failed to send query reply");
                                        } else {
                                            tracing::debug!(count = memories.len(), "Sent peer query reply");
                                        }
                                    }
                                    Err(e) => {
                                        tracing::warn!(error = %e, "Failed to serialize memories for peer reply");
                                    }
                                }
                            }
                            Err(e) => {
                                tracing::warn!(error = %e, "Local recall failed for peer query");
                                // Send empty response
                                let empty: Vec<Memory> = Vec::new();
                                if let Ok(payload) = bincode::serialize(&empty) {
                                    use zenoh::Wait;
                                    let _ = query.reply(query.key_expr(), payload).wait();
                                }
                            }
                        }
                    }
                    Err(e) => {
                        tracing::error!(error = %e, "Zenoh queryable recv error");
                        break;
                    }
                }
            }
        });

        Ok(())
    }
}

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

    #[test]
    fn test_queryable_new() {
        // The well needs a temp directory to construct.
        // This is a basic smoke test; full integration requires a running Zenoh session.
        let dir = tempfile::tempdir().unwrap();
        let well = Arc::new(Urdarbrunnr::opna(dir.path(), 384).unwrap());
        let _handler = ZenohQueryable::new(well);
    }
}
