//! @docs ARCHITECTURE:Networking
//!
//! ### AI Assist Note
//! **Swarm Discovery Service**: Orchestrates the zero-configuration
//! coordination of Tadpole OS "Bunkers" across the Local Area Network
//! (LAN). Features **mDNS-SD (Multicast DNS Service Discovery)**:
//! utilizes the `mdns-sd` daemon to broadcast and resolve peer nodes
//! on UDP port `5353`. Implements **Dynamic Node Registry**:
//! automatically updates the `AppState` node list when a peer's
//! `_tadpole._tcp.local` service is resolved. Note: Discovery
//! requires non-loopback IP resolution and permissive firewall rules
//! for the mDNS port (DISCO-01).
//!
//! ### 🔍 Debugging & Observability
//! - **Failure Path**: UDP 5353 blocked by host firewall, mDNS
//!   daemon initialization failures (OS-level), or IP address
//!   resolution conflicts on multi-homed systems.
//! - **Telemetry Link**: Search for `📡 [Discovery]` or `[mDNS]` in
//!   `tracing` logs for peer arrival/departure events.
//! - **Trace Scope**: `server-rs::services::discovery`

use crate::agent::types::SwarmNode;
use crate::state::AppState;
use chrono::Utc;
use mdns_sd::{ServiceDaemon, ServiceEvent, ServiceInfo};
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;

pub struct SwarmDiscoveryManager {
    app_state: Arc<AppState>,
    daemon: ServiceDaemon,
}

impl SwarmDiscoveryManager {
    pub fn new(app_state: Arc<AppState>) -> anyhow::Result<Self> {
        let daemon = ServiceDaemon::new()?;
        Ok(Self { app_state, daemon })
    }

    pub fn start(&self, mut shutdown_rx: tokio::sync::watch::Receiver<bool>) -> anyhow::Result<()> {
        let name = std::env::var("CLUSTER_ID").unwrap_or_else(|_| "tadpole-node".to_string());
        let port = std::env::var("PORT")
            .unwrap_or_else(|_| "8000".to_string())
            .parse::<u16>()
            .unwrap_or(8000);

        // 1. Resolve local IP (non-loopback if possible for mDNS functionality)
        let my_ip = if let Ok(ips) = local_ip_address::list_afinet_netifas() {
            ips.iter()
                .find(|(_name, ip)| ip.is_ipv4() && !ip.is_loopback())
                .map(|(_name, ip)| ip.to_string())
                .unwrap_or_else(|| "127.0.0.1".to_string())
        } else {
            "127.0.0.1".to_string()
        };

        let service_type = "_tadpole._tcp.local.";
        let node_id = Uuid::new_v4().to_string();
        let instance_name = format!("{}-{}", name.replace(" ", "-"), &node_id[..8]);
        let host_name = format!("{}.local.", instance_name);

        let mut properties = HashMap::new();
        properties.insert("id".to_string(), node_id.clone());
        properties.insert("name".to_string(), name.clone());
        properties.insert("version".to_string(), env!("CARGO_PKG_VERSION").to_string());

        let my_service = ServiceInfo::new(
            service_type,
            &instance_name,
            &host_name,
            &my_ip,
            port,
            Some(properties),
        )?;

        if let Err(e) = self.daemon.register(my_service) {
            tracing::error!("📡 [Discovery] Failed to register mDNS service: {}. Swarm discovery will be limited.", e);
        } else {
            tracing::info!(
                "📡 [Discovery] mDNS Service Registered: {} on {} (port {})",
                instance_name,
                my_ip,
                port
            );
        }

        // 2. Browse for others
        let receiver = self.daemon.browse(service_type)?;
        let state = self.app_state.clone();

        tokio::spawn(async move {
            tracing::info!(
                "📡 [Discovery] Swarm Browser Active: Searching for _tadpole._tcp.local..."
            );
            loop {
                tokio::select! {
                    event_res = receiver.recv_async() => {
                        match event_res {
                            Ok(event) => {
                                match event {
                                    ServiceEvent::ServiceResolved(info) => {
                                        let id = info
                                            .get_property_val("id")
                                            .flatten()
                                            .map(|v| String::from_utf8_lossy(v).to_string())
                                            .unwrap_or_else(|| info.get_fullname().to_string());
                                        let name = info
                                            .get_property_val("name")
                                            .flatten()
                                            .map(|v| String::from_utf8_lossy(v).to_string())
                                            .unwrap_or_else(|| info.get_fullname().to_string());

                                        let address = info
                                            .get_addresses()
                                            .iter()
                                            .next()
                                            .map(|ip| format!("{}:{}", ip, info.get_port()))
                                            .unwrap_or_else(|| format!("127.0.0.1:{}", info.get_port()));

                                        let mut metadata: HashMap<String, String> = HashMap::new();
                                        for prop in info.get_properties().iter() {
                                            metadata.insert(prop.key().to_string(), prop.val_str().to_string());
                                        }
                                        // Optimization: Store the mDNS fullname for reliable cleanup on removal
                                        metadata.insert("mdns_name".to_string(), info.get_fullname().to_string());

                                        let node = SwarmNode {
                                            id: id.clone(),
                                            name,
                                            address,
                                            status: "online".to_string(),
                                            last_seen: Utc::now(),
                                            metadata,
                                        };

                                        state.registry.nodes.insert(id.clone(), node);
                                        tracing::info!(
                                            "📡 [Discovery] Found swarm node: {} at {}",
                                            id,
                                            info.get_fullname()
                                        );
                                    }
                                    ServiceEvent::ServiceRemoved(_type, name) => {
                                        tracing::info!("🔌 [Discovery] Node removed: {}", name);
                                        // Efficient Pruning: Find the node by its mDNS name and remove it
                                        let to_remove: Vec<String> = state
                                            .registry
                                            .nodes
                                            .iter()
                                            .filter(|entry| {
                                                entry
                                                    .value()
                                                    .metadata
                                                    .get("mdns_name")
                                                    .map(|n| n == &name)
                                                    .unwrap_or(false)
                                            })
                                            .map(|entry| entry.key().clone())
                                            .collect();

                                        for id in to_remove {
                                            state.registry.nodes.remove(&id);
                                            tracing::info!("🗑️ [Discovery] Pruned node from registry: {}", id);
                                        }
                                    }
                                    _ => {}
                                }
                            }
                            Err(_) => break,
                        }
                    }
                    _ = shutdown_rx.changed() => {
                        if *shutdown_rx.borrow() {
                            tracing::info!("📡 [Discovery] Swarm Browser shutting down gracefully.");
                            break;
                        }
                    }
                }
            }
        });

        Ok(())
    }
}

// Metadata: [discovery]
