package com.ruoyi.rocketmq.snmp;

import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.rocketmq.snmp.dto.CollectionResult;
import com.ruoyi.rocketmq.snmp.dto.SwitchOidDto;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 动态OID多交换机采集管理器 (SNMP v3版本)
 */
@Service
@Slf4j
public class DynamicOidCollector {

    // 全局SNMP实例
    private Snmp snmp;
    private boolean initialized = false;
    private final Object initLock = new Object();

    // 线程池
    private final ExecutorService switchExecutor = Executors.newFixedThreadPool(10);
    private final ExecutorService deviceTypeExecutor = Executors.newFixedThreadPool(20);

    /**
     * 初始化SNMP（线程安全）
     */
    public void init() throws IOException {
        synchronized (initLock) {
            if (!initialized) {
                TransportMapping<?> transport = new DefaultUdpTransportMapping();
                snmp = new Snmp(transport);

                // 添加SNMP v3支持
                USM usm = new USM(SecurityProtocols.getInstance(),
                        new OctetString(MPv3.createLocalEngineID()), 0);
                SecurityModels.getInstance().addSecurityModel(usm);

                transport.listen();
                initialized = true;
                System.out.println("SNMP服务初始化完成");
            }
        }
    }

    /**
     * 确保SNMP已初始化
     */
    private void ensureInitialized() throws IOException {
        if (!initialized) {
            init();
        }
    }

    /**
     * 关闭资源
     */
    public void shutdown() {
        try {
            if (snmp != null) {
                snmp.close();
            }
            switchExecutor.shutdown();
            deviceTypeExecutor.shutdown();
            System.out.println("采集器已关闭");
        } catch (IOException e) {
            System.err.println("关闭资源时出错: " + e.getMessage());
        }
    }

    /**
     * 根据设备类型获取信息（核心方法 - 动态OID版本）
     */
    public CollectionResult getInterfaceInfoByType(SwitchOidDto switchVo, String deviceType) {
        try {
            ensureInitialized();
            // 获取该设备类型的OID映射
            Map<String, String> oidParams = switchVo.getOidsByType(deviceType);
            if (oidParams == null || oidParams.isEmpty()) {
                return new CollectionResult(switchVo.getIp(), deviceType, "该设备类型未配置OID");
            }

            List<Map<String, String>> results = new ArrayList<>();
            // 创建目标
            Target target = createTarget(switchVo);
            // 判断是否需要索引处理
            boolean needIndex = needIndexProcessing(oidParams, deviceType);
            // 获取过滤条件
            String filterType = deviceType + "OID";
            List<String> filterValues = switchVo.getFilterByType(filterType);
            log.info("采集交换机 {} 的 {} 设备信息，筛选条件: {}",
                    switchVo.getIp(), deviceType, filterValues);

            if(needIndex){
                String indexOID = getIndexOidFromOidParams(oidParams);
                // 获取索引OID（使用第一个OID作为索引基础）
                if (StringUtils.equals(deviceType, "net")) {
                    indexOID = "1.3.6.1.2.1.2.2.1.1";
                }
                // 获取索引列表
                List<Integer> indexes = getIndexes(target, indexOID, filterValues);
                if (indexes.isEmpty()) {
                    log.info("交换机 {} 的 {} 设备索引为空", switchVo.getIp(), deviceType);
                    return new CollectionResult(switchVo.getIp(), deviceType, results, true);
                }

                // 获取每个索引的设备信息
                for (Integer index : indexes) {
                    Map<String, String> deviceInfo = getDeviceInfo(target, index, oidParams);
                    if (!deviceInfo.isEmpty()) {
//                        deviceInfo.put("index", String.valueOf(index));
                        results.add(deviceInfo);
                    }
                }

            }else {
                // 不需要索引处理的情况（标量类型OID），传入-1表示标量
                Map<String, String> deviceInfo = getDeviceInfo(target, -1, oidParams);
                if (!deviceInfo.isEmpty()) {
                    results.add(deviceInfo);
                }
            }
            return new CollectionResult(switchVo.getIp(), deviceType, results, true);
        } catch (Exception e) {
            String errorMsg = String.format("采集失败: {}", e.getMessage());
            log.error("采集交换机 {} 的 {} 信息失败: {}",
                    switchVo.getIp(), deviceType, errorMsg);
            return new CollectionResult(switchVo.getIp(), deviceType, errorMsg);
        }
    }

    /**
     * 判断是否需要索引处理
     * 针对特定的系统信息OID，不需要索引处理
     */
    private boolean needIndexProcessing(Map<String, String> oidParams, String deviceType) {
        // 定义不需要索引处理的系统信息OID列表
        Set<String> noIndexOids = new HashSet<>(Arrays.asList(
                "1.3.6.1.2.1.1.1.0",  // sysDescr
                "1.3.6.1.2.1.1.2.0",  // sysObjectID
                "1.3.6.1.2.1.1.3.0",  // sysUpTime
                "1.3.6.1.2.1.1.4.0",  // sysContact
                "1.3.6.1.2.1.1.5.0",  // sysName
                "1.3.6.1.2.1.1.6.0",  // sysLocation
                "1.3.6.1.4.1.2011.5.25.183.1.4.0",  // hwStackSystemMac
                "1.3.6.1.4.1.2011.6.157.1.3.0",  // hwAveragePower
                "1.3.6.1.4.1.2011.6.157.1.6.0"  // hwCurrentPower
        ));

        // 检查所有配置的OID
        for (String oid : oidParams.keySet()) {
            // 如果OID在不需要索引的列表中，返回false
            if (noIndexOids.contains(oid)) {
                return false;
            }
        }

        // 默认情况下，其他OID需要索引处理
        return true;
    }

    /**
     * 从OID参数中提取索引OID
     */
    private String getIndexOidFromOidParams(Map<String, String> oidParams) {
        if (oidParams.isEmpty()) {
            return null;
        }
        // 获取第一个OID作为索引基础
        String firstOid = "1.3.6.1.2.1.47.1.1.1.1.5";
        return firstOid;
    }

    /**
     * 获取设备索引列表
     */
    private List<Integer> getIndexes(Target target, String indexOID, List<String> filterValues)
            throws IOException {
        List<Integer> indexes = new ArrayList<>();

        PDU pdu = new PDU();
        pdu.add(new VariableBinding(new OID(indexOID)));
        pdu.setType(PDU.GETBULK);
        pdu.setMaxRepetitions(100);
        pdu.setNonRepeaters(0);

        OID baseOid = new OID(indexOID);
        boolean finished = false;

        while (!finished) {
            ResponseEvent response = snmp.send(pdu, target);
            PDU responsePdu = response.getResponse();

            if (responsePdu == null || responsePdu.getErrorStatus() != 0) {
                break;
            }

            for (VariableBinding vb : responsePdu.getVariableBindings()) {
                OID returnedOid = vb.getOid();
                if (!returnedOid.startsWith(baseOid)) {
                    finished = true;
                    break;
                }

                int index = returnedOid.get(returnedOid.size() - 1);
                String value = vb.getVariable().toString();

                // 应用过滤条件
                if (filterValues == null || filterValues.isEmpty()) {
                    indexes.add(index);
                } else if (filterValues.contains(value)) {
                    indexes.add(index);
                }
            }

            if (!finished && !responsePdu.getVariableBindings().isEmpty()) {
                pdu.set(0, new VariableBinding(responsePdu.getVariableBindings().get(0).getOid()));
            }
        }

        return indexes.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 获取设备信息（支持标量和表类型OID）
     * @param target SNMP目标
     * @param index 索引号，-1表示标量OID（不需要加索引后缀）
     * @param oidParams OID参数映射
     * @return 设备信息
     */
    private Map<String, String> getDeviceInfo(Target target, int index,
                                              Map<String, String> oidParams)
            throws IOException {
        Map<String, String> deviceInfo = new HashMap<>();

        PDU pdu = new PDU();
        for (String oid : oidParams.keySet()) {
            String finalOid = oid;
            // 如果index不为-1，为表类型OID添加索引后缀
            if (index != -1) {
                finalOid = oid + "." + index;
            }
            pdu.add(new VariableBinding(new OID(finalOid)));
        }
        pdu.setType(PDU.GET);

        ResponseEvent event = snmp.send(pdu, target);
        if (event != null && event.getResponse() != null) {
            PDU responsePdu = event.getResponse();
            if (responsePdu.getErrorStatus() == PDU.noError) {
                int i = 0;
                for (String paramName : oidParams.values()) {
                    if (i < responsePdu.size()) {
                        VariableBinding vb = responsePdu.get(i);
                        deviceInfo.put(paramName, vb.getVariable().toString());
                    }
                    i++;
                }
            }
        }

        return deviceInfo;
    }

    /**
     * 创建SNMP目标 (支持v2c和v3)
     */
    private Target createTarget(SwitchOidDto switchVo) {
        Address targetAddress = new UdpAddress(switchVo.getIp() + "/" + switchVo.getPort());

        // 根据版本选择创建不同的Target
        if (switchVo.isV3()) {
            return createV3Target(switchVo, targetAddress);
        } else {
            return createV2cTarget(switchVo, targetAddress);
        }
    }

    /**
     * 创建SNMP v2c目标
     */
    private CommunityTarget createV2cTarget(SwitchOidDto switchVo, Address targetAddress) {
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(switchVo.getCommunity()));
        target.setAddress(targetAddress);
        target.setRetries(switchVo.getRetries());
        target.setTimeout(switchVo.getTimeout());
        target.setVersion(SnmpConstants.version2c);
        return target;
    }

    /**
     * 创建SNMP v3目标
     */
    private UserTarget createV3Target(SwitchOidDto switchVo, Address targetAddress) {
        UserTarget target = new UserTarget();
        target.setAddress(targetAddress);
        target.setRetries(switchVo.getRetries());
        target.setTimeout(switchVo.getTimeout());
        target.setVersion(SnmpConstants.version3);

        // 设置安全名称
        target.setSecurityName(new OctetString(switchVo.getSecurityName()));

        // 设置安全级别
        target.setSecurityLevel(switchVo.getSecurityLevel());

        return target;
    }

    /**
     * 添加SNMP v3用户到SNMP引擎
     */
    public void addV3User(SwitchOidDto switchVo) {
        if (!switchVo.isV3()) {
            return;
        }

        OctetString securityName = new OctetString(switchVo.getSecurityName());
        OID authProtocol = getAuthProtocol(switchVo.getAuthProtocol());
        OID privProtocol = getPrivProtocol(switchVo.getPrivProtocol());

        UsmUser user = new UsmUser(
                securityName,
                authProtocol,
                new OctetString(switchVo.getAuthPassword()),
                privProtocol,
                new OctetString(switchVo.getPrivPassword())
        );

        snmp.getUSM().addUser(securityName, user);
    }

    /**
     * 获取认证协议OID
     */
    private OID getAuthProtocol(String authProtocol) {
        if (authProtocol == null) {
            return AuthMD5.ID;
        }

        switch (authProtocol.toLowerCase()) {
            case "md5":
                return AuthMD5.ID;
            case "sha":
                return AuthSHA.ID;
            case "sha1":
                return AuthSHA.ID;
            default:
                return AuthMD5.ID;
        }
    }

    /**
     * 获取加密协议OID
     */
    private OID getPrivProtocol(String privProtocol) {
        if (privProtocol == null) {
            return PrivDES.ID;
        }

        switch (privProtocol.toLowerCase()) {
            case "des":
                return PrivDES.ID;
            case "3des":
                return Priv3DES.ID;
            case "aes128":
                return PrivAES128.ID;
            case "aes192":
                return PrivAES192.ID;
            case "aes256":
                return PrivAES256.ID;
            default:
                return PrivDES.ID;
        }
    }

    /**
     * 采集单个交换机的所有设备类型
     */
    public Map<String, CollectionResult> collectSwitch(SwitchOidDto switchVo) {
        Map<String, CollectionResult> results = new ConcurrentHashMap<>();

        // 获取交换机支持的所有设备类型
        List<String> deviceTypes = switchVo.getSupportedDeviceTypes();
        if (deviceTypes.isEmpty()) {
            log.info("交换机 {} 未配置任何设备类型的OID", switchVo.getIp());
            return results;
        }

        // 如果是v3版本，先添加用户
        if (switchVo.isV3()) {
            addV3User(switchVo);
        }

        // 并发采集所有设备类型
        List<CompletableFuture<Void>> futures = deviceTypes.stream()
                .map(deviceType -> CompletableFuture.runAsync(() -> {
                    CollectionResult result = getInterfaceInfoByType(switchVo, deviceType);
                    results.put(deviceType, result);

                    if (result.isSuccess()) {
                        log.info("交换机 {} 的 {} 采集完成: %d 个设备",
                                switchVo.getIp(), deviceType, result.getData().size());
                    } else {
                        log.info("交换机 {} 的 {} 采集失败: {}",
                                switchVo.getIp(), deviceType, result.getErrorMessage());
                    }
                }, deviceTypeExecutor))
                .collect(Collectors.toList());

        // 等待所有设备类型采集完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        return results;
    }

    /**
     * 并发采集多个交换机
     */
    public Map<String, Map<String, CollectionResult>> collectAllSwitches(List<SwitchOidDto> switches) {
        Map<String, Map<String, CollectionResult>> allResults = new ConcurrentHashMap<>();

        List<CompletableFuture<Void>> futures = switches.stream()
                .map(switchVo -> CompletableFuture.runAsync(() -> {
                    log.info("开始采集交换机: {}", switchVo.getIp());

                    Map<String, CollectionResult> switchResults = collectSwitch(switchVo);
                    allResults.put(switchVo.getIp(), switchResults);

                    log.info("完成采集交换机: {}, 采集了 %d 种设备类型",
                            switchVo.getIp(), switchResults.size());
                }, switchExecutor))
                .collect(Collectors.toList());

        // 等待所有交换机采集完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        return allResults;
    }

    /**
     * 创建示例交换机配置 (支持v2c和v3)
     */
    public static SwitchOidDto createExampleSwitch(String ip, String community) {
        SwitchOidDto switchVo = new SwitchOidDto(ip, community);
        switchVo.setVersion(SnmpConstants.version2c); // 默认v2c

        // 配置系统信息OID
        Map<String, String> otherOids = new LinkedHashMap<>();
        otherOids.put("1.3.6.1.2.1.1.1.0", "ifDescr");
        otherOids.put("1.3.6.1.2.1.1.2.0", "sysObjectID");
        otherOids.put("1.3.6.1.2.1.1.3.0", "sysUpTime");
        otherOids.put("1.3.6.1.2.1.1.4.0", "sysContact");
        otherOids.put("1.3.6.1.2.1.1.5.0", "sysName");
        otherOids.put("1.3.6.1.2.1.1.6.0", "sysLocation");
        otherOids.put("1.3.6.1.4.1.2011.5.25.183.1.4.0", "hwStackSystemMac");
        switchVo.setOtherOID(otherOids);

        // 配置网络接口OID
        Map<String, String> netOids = new LinkedHashMap<>();
        netOids.put("1.3.6.1.2.1.2.2.1.2", "ifDescr");
        netOids.put("1.3.6.1.2.1.2.2.1.3", "ifType");
        netOids.put("1.3.6.1.2.1.2.2.1.8", "ifOperStatus");
        switchVo.setNetOID(netOids);

        return switchVo;
    }

    /**
     * 创建SNMP v3示例交换机配置
     */
    public static SwitchOidDto createV3ExampleSwitch(String ip, String securityName,
                                                     String authProtocol, String authPassword,
                                                     String privProtocol, String privPassword) {
        SwitchOidDto switchVo = new SwitchOidDto(ip, null);
        switchVo.setVersion(SnmpConstants.version3);
        switchVo.setSecurityName(securityName);
        switchVo.setAuthProtocol(authProtocol);
        switchVo.setAuthPassword(authPassword);
        switchVo.setPrivProtocol(privProtocol);
        switchVo.setPrivPassword(privPassword);
        switchVo.setSecurityLevel(org.snmp4j.security.SecurityLevel.AUTH_PRIV); // 认证+加密

        // 配置系统信息OID
        Map<String, String> otherOids = new LinkedHashMap<>();
        otherOids.put("1.3.6.1.2.1.1.1.0", "ifDescr");
        otherOids.put("1.3.6.1.2.1.1.2.0", "sysObjectID");
        otherOids.put("1.3.6.1.2.1.1.3.0", "sysUpTime");
        otherOids.put("1.3.6.1.2.1.1.4.0", "sysContact");
        otherOids.put("1.3.6.1.2.1.1.5.0", "sysName");
        otherOids.put("1.3.6.1.2.1.1.6.0", "sysLocation");
        switchVo.setOtherOID(otherOids);

        // 配置网络接口OID
        Map<String, String> netOids = new LinkedHashMap<>();
        netOids.put("1.3.6.1.2.1.2.2.1.2", "ifDescr");
        netOids.put("1.3.6.1.2.1.2.2.1.3", "ifType");
        netOids.put("1.3.6.1.2.1.2.2.1.8", "ifOperStatus");
        switchVo.setNetOID(netOids);

        return switchVo;
    }

    /**
     * 主程序入口 (支持v2c和v3测试)
     */
//    public static void main(String[] args) {
//        DynamicOidCollector collector = new DynamicOidCollector();
//
//        try {
//            // 初始化
//            collector.init();
//
//            // 创建动态配置的交换机列表 (支持v2c和v3混合)
//            List<SwitchOidDto> switches = Arrays.asList(
//                    createExampleSwitch("123.182.89.17", "chengde_200G"), // v2c
//                    createV3ExampleSwitch("1.194.193.81", "v3user", "SHA", "auth123", "AES128", "priv123") // v3
//            );
//
//            // 并发采集所有交换机
//            log.info("开始并发采集 {} 台交换机...", switches.size());
//            long startTime = System.currentTimeMillis();
//
//            Map<String, Map<String, CollectionResult>> results = collector.collectAllSwitches(switches);
//
//            long endTime = System.currentTimeMillis();
//
//            // 显示统计信息
//            int totalDevices = 0;
//            for (Map.Entry<String, Map<String, CollectionResult>> entry : results.entrySet()) {
//                String ip = entry.getKey();
//                Map<String, CollectionResult> deviceResults = entry.getValue();
//
//                int switchDevices = deviceResults.values().stream()
//                        .filter(CollectionResult::isSuccess)
//                        .mapToInt(r -> r.getData().size())
//                        .sum();
//
//                totalDevices += switchDevices;
//            }
//
//            log.info("总计: %d 台交换机, %d 个设备", results.size(), totalDevices);
//            log.info("总采集时间: %.2f 秒", (endTime - startTime) / 1000.0);
//
//        } catch (Exception e) {
//            log.error("采集程序出错: {}", e.getMessage());
//            e.printStackTrace();
//        } finally {
//            collector.shutdown();
//        }
//    }
}