//! UDP 广播服务实现

use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use std::time::Duration;
use socket2::{Domain, Protocol, Socket, Type};
use tokio::net::UdpSocket;
use tokio::sync::broadcast;

use crate::models::{DeviceInfo, DiscoveryPacket, DiscoveryPacketType};
use crate::utils::{AppError, DISCOVERY_INTERVAL_MS, DEFAULT_UDP_PORT};

/// 广播地址
const BROADCAST_ADDR: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 255);

/// 发现服务状态
#[derive(Debug, Clone)]
pub enum DiscoveryEvent {
    /// 发现新设备
    DeviceFound(DeviceInfo),
    /// 设备离线
    DeviceLost(String),
    /// 服务启动
    Started,
    /// 服务停止
    Stopped,
}

/// UDP 发现服务
pub struct DiscoveryService {
    /// 本机设备信息
    local_device: DeviceInfo,
    /// UDP 端口
    port: u16,
    /// 事件发送器
    event_tx: broadcast::Sender<DiscoveryEvent>,
    /// 停止信号
    stop_tx: Option<tokio::sync::oneshot::Sender<()>>,
}

impl DiscoveryService {
    /// 创建新的发现服务
    pub fn new(local_device: DeviceInfo) -> Self {
        let (event_tx, _) = broadcast::channel(100);
        Self {
            // UDP 广播端口必须固定，所有实例使用同一端口才能互相发现
            port: DEFAULT_UDP_PORT,
            local_device,
            event_tx,
            stop_tx: None,
        }
    }

    /// 获取事件接收器
    pub fn subscribe(&self) -> broadcast::Receiver<DiscoveryEvent> {
        self.event_tx.subscribe()
    }

    /// 启动发现服务
    pub async fn start(&mut self) -> Result<(), AppError> {
        let port = self.port;
        let local_device = self.local_device.clone();
        let event_tx = self.event_tx.clone();

        // 创建停止信号
        let (stop_tx, stop_rx) = tokio::sync::oneshot::channel();
        self.stop_tx = Some(stop_tx);

        // 使用 socket2 创建支持端口复用的 UDP 套接字
        let bind_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port);
        let socket2_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
        socket2_socket.set_reuse_address(true)?;
        #[cfg(unix)]
        socket2_socket.set_reuse_port(true)?;
        socket2_socket.set_broadcast(true)?;
        socket2_socket.set_nonblocking(true)?;
        socket2_socket.bind(&bind_addr.into())?;
        
        let std_socket: std::net::UdpSocket = socket2_socket.into();
        let socket = UdpSocket::from_std(std_socket)?;

        log::info!("Discovery service started on port {}", port);
        let _ = event_tx.send(DiscoveryEvent::Started);

        let socket = Arc::new(socket);

        // 启动接收任务
        let recv_socket = socket.clone();
        let recv_event_tx = event_tx.clone();
        let local_id = local_device.device_id.clone();
        
        let recv_handle = tokio::spawn(async move {
            Self::receive_loop(recv_socket, recv_event_tx, local_id).await;
        });

        // 启动广播任务
        let broadcast_socket = socket.clone();
        let broadcast_device = local_device.clone();
        
        let broadcast_handle = tokio::spawn(async move {
            Self::broadcast_loop(broadcast_socket, broadcast_device, port).await;
        });

        // 等待停止信号
        tokio::spawn(async move {
            let _ = stop_rx.await;
            recv_handle.abort();
            broadcast_handle.abort();
            log::info!("Discovery service stopped");
        });

        Ok(())
    }

    /// 停止发现服务
    pub fn stop(&mut self) {
        if let Some(stop_tx) = self.stop_tx.take() {
            let _ = stop_tx.send(());
            let _ = self.event_tx.send(DiscoveryEvent::Stopped);
        }
    }

    /// 接收循环
    async fn receive_loop(
        socket: Arc<UdpSocket>,
        event_tx: broadcast::Sender<DiscoveryEvent>,
        local_device_id: String,
    ) {
        let mut buf = [0u8; 4096];
        
        loop {
            match socket.recv_from(&mut buf).await {
                Ok((len, src)) => {
                    if let Ok(packet) = serde_json::from_slice::<DiscoveryPacket>(&buf[..len]) {
                        // 忽略自己的广播
                        if packet.device.device_id == local_device_id {
                            continue;
                        }

                        match packet.packet_type {
                            DiscoveryPacketType::Announce | DiscoveryPacketType::Response => {
                                log::debug!("Discovered device: {} at {}", packet.device.device_name, src);
                                let _ = event_tx.send(DiscoveryEvent::DeviceFound(packet.device));
                            }
                            DiscoveryPacketType::Goodbye => {
                                log::debug!("Device offline: {}", packet.device.device_id);
                                let _ = event_tx.send(DiscoveryEvent::DeviceLost(packet.device.device_id));
                            }
                            DiscoveryPacketType::Query => {
                                // 收到查询请求，不在这里处理 (由 broadcast_loop 定期广播)
                            }
                        }
                    }
                }
                Err(e) => {
                    log::error!("UDP receive error: {}", e);
                    tokio::time::sleep(Duration::from_secs(1)).await;
                }
            }
        }
    }

    /// 广播循环
    async fn broadcast_loop(socket: Arc<UdpSocket>, device: DeviceInfo, port: u16) {
        let broadcast_addr = SocketAddr::V4(SocketAddrV4::new(BROADCAST_ADDR, port));
        let interval = Duration::from_millis(DISCOVERY_INTERVAL_MS);

        loop {
            let packet = DiscoveryPacket::announce(device.clone());
            if let Ok(data) = serde_json::to_vec(&packet) {
                if let Err(e) = socket.send_to(&data, broadcast_addr).await {
                    log::error!("Failed to broadcast: {}", e);
                }
            }
            tokio::time::sleep(interval).await;
        }
    }

    /// 发送离线通知
    pub async fn send_goodbye(&self) -> Result<(), AppError> {
        let bind_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
        let socket = UdpSocket::bind(bind_addr).await?;
        socket.set_broadcast(true)?;

        let broadcast_addr = SocketAddr::V4(SocketAddrV4::new(BROADCAST_ADDR, self.port));
        let packet = DiscoveryPacket::goodbye(self.local_device.clone());
        let data = serde_json::to_vec(&packet)?;
        
        socket.send_to(&data, broadcast_addr).await?;
        Ok(())
    }
}
