//! UDP 广播服务实现
//! 
//! 支持同机器多实例运行：
//! - 每个实例监听不同端口 (DEFAULT_UDP_PORT + offset)
//! - 所有实例都向 DEFAULT_UDP_PORT 广播
//! - 同时向 localhost 发送确保同机器实例能收到

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 监听端口 (可能因端口偏移而不同)
    listen_port: u16,
    /// UDP 广播目标端口 (始终是 DEFAULT_UDP_PORT)
    broadcast_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);
        
        // 从环境变量读取端口偏移量 (用于同机器多实例)
        let port_offset: u16 = std::env::var("FLASH_SEND_PORT_OFFSET")
            .ok()
            .and_then(|s| s.parse().ok())
            .unwrap_or(0);
        
        Self {
            // 监听端口 = 默认端口 + 偏移量 (避免同机器绑定冲突)
            listen_port: DEFAULT_UDP_PORT + port_offset,
            // 广播端口 = 始终是默认端口 (确保所有实例都向同一端口广播)
            broadcast_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 listen_port = self.listen_port;
        let broadcast_port = self.broadcast_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);

        // 创建主监听 socket
        let socket = Self::create_udp_socket(listen_port)?;
        log::info!("Discovery service: listening on {}, broadcasting to {}", listen_port, broadcast_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, broadcast_port).await;
        });

        // 如果监听端口与广播端口不同，需要额外监听广播端口以接收其他实例的消息
        let extra_recv_handle = if listen_port != broadcast_port {
            let extra_event_tx = event_tx.clone();
            let extra_local_id = local_device.device_id.clone();
            
            Some(tokio::spawn(async move {
                // 尝试绑定广播端口（可能被主实例占用）
                match Self::create_udp_socket(broadcast_port) {
                    Ok(extra_socket) => {
                        log::info!("Discovery service: also listening on broadcast port {}", broadcast_port);
                        Self::receive_loop(Arc::new(extra_socket), extra_event_tx, extra_local_id).await;
                    }
                    Err(_) => {
                        log::warn!("Could not bind broadcast port {} (occupied by main instance)", broadcast_port);
                    }
                }
            }))
        } else {
            None
        };

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

        Ok(())
    }

    /// 创建 UDP socket
    fn create_udp_socket(port: u16) -> Result<UdpSocket, AppError> {
        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)?;
        Ok(socket)
    }

    /// 停止发现服务
    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;
                }
            }
        }
    }

    /// 广播循环 - 向广播地址和 localhost 发送
    async fn broadcast_loop(socket: Arc<UdpSocket>, device: DeviceInfo, broadcast_port: u16) {
        let broadcast_addr = SocketAddr::V4(SocketAddrV4::new(BROADCAST_ADDR, broadcast_port));
        let localhost_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), broadcast_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);
                }
                // 发送到 localhost (同机器其他实例)
                let _ = socket.send_to(&data, localhost_addr).await;
            }
            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.broadcast_port));
        let localhost_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), self.broadcast_port));
        
        let packet = DiscoveryPacket::goodbye(self.local_device.clone());
        let data = serde_json::to_vec(&packet)?;
        
        socket.send_to(&data, broadcast_addr).await?;
        let _ = socket.send_to(&data, localhost_addr).await;
        Ok(())
    }
}
