package com.njcn.gather.detection.sntp;

import com.alibaba.fastjson.JSON;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.time.Instant;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
@RequiredArgsConstructor
public class SntpServerManager {

    private final SntpServerProperties sntpServerProperties;
    private final SntpPacketService sntpPacketService;

    private final AtomicBoolean running = new AtomicBoolean(false);
    private final Object lifecycleMonitor = new Object();

    private volatile DatagramSocket datagramSocket;
    private volatile ExecutorService executorService;

    public void start() {
        // 使用同步锁和原子变量防止重复启动
        synchronized (lifecycleMonitor) {
            if (running.get()) {
                return;
            }
            int port = resolvePort();
            DatagramSocket socket = createSocket(port);
            ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {
                Thread thread = new Thread(runnable, "sntp-server");
                thread.setDaemon(true);
                return thread;
            });

            datagramSocket = socket;
            executorService = executor;
            running.set(true);
            executor.submit(this::receiveLoop);
            log.info("SNTP服务已启动，监听端口: {}", port);
        }
    }

    public void stop() {
        synchronized (lifecycleMonitor) {
            if (!running.get()) {
                return;
            }
            running.set(false);
            closeSocketQuietly(datagramSocket);
            datagramSocket = null;
            if (executorService != null) {
                executorService.shutdownNow();
                executorService = null;
            }
            log.info("SNTP服务已停止");
        }
    }

    public boolean isRunning() {
        return running.get();
    }

    @PreDestroy
    public void destroy() {
        stop();
    }

    private void receiveLoop() {
        byte[] buffer = new byte[512];
        while (running.get()) {
            DatagramSocket socket = datagramSocket;
            if (socket == null || socket.isClosed()) {
                break;
            }

            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            try {
                socket.receive(packet);
                handlePacket(socket, packet);
                log.info("SNTP服务接收报文: {}", Arrays.toString(packet.getData()));
            } catch (SocketException e) {
                if (running.get()) {
                    log.error("SNTP服务接收报文失败", e);
                }
                break;
            } catch (Exception e) {
                log.error("SNTP服务处理报文失败", e);
            }
        }

        synchronized (lifecycleMonitor) {
            if (running.get()) {
                running.set(false);
                closeSocketQuietly(datagramSocket);
                datagramSocket = null;
                if (executorService != null) {
                    executorService.shutdownNow();
                    executorService = null;
                }
            }
        }
    }

    private void handlePacket(DatagramSocket socket, DatagramPacket packet) throws IOException {
        Instant receiveInstant = Instant.now(); //T2：服务器接收请求时间
        byte[] request = Arrays.copyOf(packet.getData(), packet.getLength());
        InetSocketAddress clientAddress = new InetSocketAddress(packet.getAddress(), packet.getPort());
        SntpExchange exchange = sntpPacketService.parseRequest(request, clientAddress);
        if (exchange == null) {
            return;
        }

        Instant transmitInstant = Instant.now(); //T3：服务器发送响应时间
        byte[] response = sntpPacketService.buildResponse(exchange, receiveInstant, transmitInstant);
        DatagramPacket responsePacket = new DatagramPacket(response, response.length, packet.getAddress(), packet.getPort());
        socket.send(responsePacket);

        String deviceIp = clientAddress.getAddress().getHostAddress();
//        SntpPushMessage pushMessage = sntpPacketService.toPushMessage(deviceIp, transmitInstant, exchange.getDeviceInstant());
        SntpPushMessage pushMessage = sntpPacketService.toPushMessage(deviceIp, receiveInstant, exchange.getDeviceInstant());
        WebServiceManager.broadcast(JSON.toJSONString(pushMessage));
    }

    private DatagramSocket createSocket(int port) {
        try {
            DatagramSocket socket = new DatagramSocket(port);
            socket.setReuseAddress(true);
            return socket;
        } catch (SocketException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "SNTP服务启动失败，端口绑定异常");
        }
    }

    private int resolvePort() {
        Integer port = sntpServerProperties.getPort();
        if (port == null || port < 1 || port > 65535) {
            throw new BusinessException(CommonResponseEnum.FAIL, "SNTP服务启动失败，端口配置无效");
        }
        return port;
    }

    /**
     * 关闭UDP Socket
     *
     * @param socket
     */
    private void closeSocketQuietly(DatagramSocket socket) {
        if (socket != null && !socket.isClosed()) {
            socket.close();
        }
    }
}
