package com.usky.sas.common.onvif;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.sas.common.entity.SearchDeviceInfo;
import com.usky.sas.common.exception.BusinessException;
import com.usky.sas.common.util.GetIpUtils;
import com.usky.sas.domain.SasDevice;
import com.usky.sas.mapper.SasDeviceMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
public class OnvifService {
    private static final Logger log = LoggerFactory.getLogger(OnvifService.class);
    private static final String DISCOVERY_MULTICAST_ADDRESS = "239.255.255.250";
    private static final int DISCOVERY_PORT = 3702;

    public OnvifService() {
    }

    private static String buildProbeMessage() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n<e:Envelope xmlns:e=\"http://www.w3.org/2003/05/soap-envelope\" \n xmlns:w=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" \n xmlns:d=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" \n xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\"> \n <e:Header> \n  <w:MessageID>uuid:" + UUID.randomUUID() + "</w:MessageID> \n  <w:To e:mustUnderstand=\"true\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To> \n  <w:Action a:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</w:Action> \n </e:Header> \n <e:Body> \n  <d:Probe> \n   <d:Types>dn:NetworkVideoTransmitter</d:Types> \n  </d:Probe> \n </e:Body> \n</e:Envelope>  ";
    }

    public static List<SearchDeviceInfo> searchDeviceByOnvif(Integer second, SasDeviceMapper deviceMapper) {
        List<SearchDeviceInfo> deviceInfoList = new ArrayList();

        try (MulticastSocket socket = new MulticastSocket(0)) {
            InetAddress localAddr = InetAddress.getByName(GetIpUtils.getServerIP());
            NetworkInterface ni = NetworkInterface.getByInetAddress(localAddr);
            if (ni == null) {
                throw new BusinessException("未找到与 IP " + GetIpUtils.getServerIP() + " 关联的网络接口", -1);
            }

            log.info(" 使用网卡: " + ni.getDisplayName() + " (" + ni.getName() + ")");
            socket.setNetworkInterface(ni);
            socket.setTimeToLive(4);
            socket.setSoTimeout(second * 1000);
            socket.joinGroup(new InetSocketAddress(InetAddress.getByName("239.255.255.250"), 3702), ni);
            String probeMessage = buildProbeMessage();
            DatagramPacket packet = new DatagramPacket(probeMessage.getBytes(StandardCharsets.UTF_8), probeMessage.getBytes(StandardCharsets.UTF_8).length, InetAddress.getByName("239.255.255.250"), 3702);
            socket.send(packet);
            log.info("已通过 {} 发送 Probe 到 {}:{}", new Object[]{GetIpUtils.getServerIP(), "239.255.255.250", 3702});
            byte[] buffer = new byte[4096];
            DatagramPacket response = new DatagramPacket(buffer, buffer.length);

            for(long startTime = System.currentTimeMillis(); System.currentTimeMillis() - startTime < (long)(second * 1000); response = new DatagramPacket(buffer, buffer.length)) {
                try {
                    socket.receive(response);
                    String responseData = new String(response.getData(), 0, response.getLength(), StandardCharsets.UTF_8);
                    log.info("收到响应来自: " + response.getAddress().getHostAddress());
                    SearchDeviceInfo searchDeviceInfo = parseProbeMatchResponse(responseData, deviceMapper);
                    deviceInfoList.add(searchDeviceInfo);
                } catch (SocketTimeoutException var24) {
                    break;
                }
            }
        } catch (Exception e) {
            log.error("设备发现异常", e);
        }

        return deviceInfoList;
    }

    private static SearchDeviceInfo parseProbeMatchResponse(String responseData, SasDeviceMapper deviceMapper) {
        try {
            SearchDeviceInfo searchDeviceInfo = new SearchDeviceInfo();
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(responseData)));
            NodeList xAddrsList = doc.getElementsByTagNameNS("http://schemas.xmlsoap.org/ws/2005/04/discovery", "XAddrs");
            if (xAddrsList.getLength() > 0) {
                String xAddrs = xAddrsList.item(0).getTextContent().trim();
                log.info("原始 XAddrs: " + xAddrs);

                for(String addr : xAddrs.split("\\s+")) {
                    if (addr.startsWith("http://")) {
                        try {
                            URL url = new URL(addr);
                            InetAddress ip = InetAddress.getByName(url.getHost());
                            if (ip instanceof Inet4Address) {
                                searchDeviceInfo.setIpAddr(url.getHost());
                                searchDeviceInfo.setPort(80);
                            }
                        } catch (Exception e) {
                            log.error("url解析失败：{}", e.getMessage());
                        }
                    }
                }
            }

            NodeList scopesList = doc.getElementsByTagNameNS("http://schemas.xmlsoap.org/ws/2005/04/discovery", "Scopes");
            if (scopesList.getLength() > 0) {
                String scopesText = scopesList.item(0).getTextContent().trim();

                for(String scope : scopesText.split("\\s+")) {
                    String mac = extractScopeValue(scope, "onvif://www.onvif.org/MAC/");
                    String hardware = extractScopeValue(scope, "onvif://www.onvif.org/hardware/");
                    String name = extractScopeValue(scope, "onvif://www.onvif.org/name/");
                    if (mac != null) {
                        searchDeviceInfo.setMacAddr(mac);
                    }

                    if (hardware != null) {
                        searchDeviceInfo.setNote(hardware);
                    }

                    if (name != null) {
                        searchDeviceInfo.setDeviceId(URLDecoder.decode(name, "UTF-8"));
                    }
                }
            }

            LambdaQueryWrapper<SasDevice> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(SasDevice::getMacAddr, searchDeviceInfo.getMacAddr());
            SasDevice device = deviceMapper.selectOne(wrapper);
            searchDeviceInfo.setIsAdded(device != null);
            return searchDeviceInfo;
        } catch (Exception e) {
            log.error("解析响应失败: " + e.getMessage());
            return null;
        }
    }

    private static String extractScopeValue(String scope, String prefix) {
        return scope.startsWith(prefix) ? scope.substring(prefix.length()).trim() : null;
    }
}
