package com.zehong.system.modbus.util;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lenovo
 * @date 2025/9/25
 * @description TODO
 */
@Component
public class ModbusMasterPool {
    private static final Logger log = LoggerFactory.getLogger(ModbusMasterPool.class);

    // 连接池配置：可在application.yml中配置，这里先硬编码，后续可优化为配置类
    private static final int MAX_TOTAL = 10; // 每个池最大连接数（3个端口共30个，足够72个Job）
    private static final int MAX_IDLE = 5;   // 每个池最大空闲连接数
    private static final int MIN_IDLE = 2;   // 每个池最小空闲连接数
    private static final long MAX_WAIT_MS = 3000; // 借连接超时时间（3秒）
    private static final long TIME_BETWEEN_EVICTION_RUNS_MS = 60000; // 空闲连接检测间隔（1分钟）
    private static final boolean TEST_ON_BORROW = true; // 借连接时验证有效性
    private static final boolean TEST_ON_RETURN = false; // 还连接时不验证（减少开销）

    // 存储「ip:port -> 连接池」的映射，线程安全
    private final Map<String, GenericObjectPool<ModbusMaster>> poolMap = new ConcurrentHashMap<>();

    // 单例Modbus工厂（复用，避免重复创建）
    private static final com.serotonin.modbus4j.ModbusFactory MODBUS_FACTORY = new com.serotonin.modbus4j.ModbusFactory();

    /**
     * 借连接：根据ip和port获取连接池，再从池中借连接
     * @param ip 设备IP
     * @param port 端口（501/502/503）
     * @return 可用的ModbusMaster
     * @throws Exception 借连接超时或连接无效
     */
    public ModbusMaster borrowMaster(String ip, int port) throws Exception {
        String poolKey = getPoolKey(ip, port);
        // 懒加载创建连接池：第一次使用时才创建对应ip:port的池
        GenericObjectPool<ModbusMaster> pool = poolMap.computeIfAbsent(poolKey, this::createPool);

        // 借连接（超时抛异常）
        ModbusMaster master = pool.borrowObject(MAX_WAIT_MS);
        log.debug("借Modbus连接成功：{}，当前池空闲数={}，活跃数={}",
                poolKey, pool.getNumIdle(), pool.getNumActive());
        return master;
    }

    /**
     * 还连接：将连接归还给对应池（无论连接是否有效，池会自动处理）
     * @param ip 设备IP
     * @param port 端口
     * @param master 借到的ModbusMaster
     */
    public void returnMaster(String ip, int port, ModbusMaster master) {
        if (master == null) return;

        String poolKey = getPoolKey(ip, port);
        GenericObjectPool<ModbusMaster> pool = poolMap.get(poolKey);
        if (pool == null) {
            log.warn("还连接失败：连接池不存在（已被销毁），{}", poolKey);
            destroyMaster(master); // 池不存在，直接销毁连接
            return;
        }

        try {
            pool.returnObject(master);
            log.debug("还Modbus连接成功：{}，当前池空闲数={}，活跃数={}",
                    poolKey, pool.getNumIdle(), pool.getNumActive());
        } catch (Exception e) {
            log.error("还连接失败：{}", poolKey, e);
            destroyMaster(master); // 归还失败，销毁连接
        }
    }

    /**
     * 销毁连接：强制关闭连接（池内部也会调用此方法）
     */
    private void destroyMaster(ModbusMaster master) {
        if (master == null) return;

        try {
            // 1. 关闭底层Socket（反射）
            if (master instanceof TcpMaster) {
                TcpMaster tcpMaster = (TcpMaster) master;
                java.lang.reflect.Field socketField = TcpMaster.class.getDeclaredField("socket");
                socketField.setAccessible(true);
                java.net.Socket socket = (java.net.Socket) socketField.get(tcpMaster);
                if (socket != null && !socket.isClosed()) {
                    socket.close();
                }
            }
            // 2. 销毁Master
            master.destroy();
            log.debug("销毁Modbus连接成功");
        } catch (Exception e) {
            log.error("销毁Modbus连接失败", e);
        }
    }

    /**
     * 创建单个连接池（按ip:port）
     */
    private GenericObjectPool<ModbusMaster> createPool(String poolKey) {
        // 1. 配置池参数
        GenericObjectPoolConfig<ModbusMaster> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(MAX_TOTAL);
        poolConfig.setMaxIdle(MAX_IDLE);
        poolConfig.setMinIdle(MIN_IDLE);
        poolConfig.setTimeBetweenEvictionRunsMillis(TIME_BETWEEN_EVICTION_RUNS_MS);
        poolConfig.setTestOnBorrow(TEST_ON_BORROW);
        poolConfig.setTestOnReturn(TEST_ON_RETURN);
        poolConfig.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");

        // 2. 创建池（传入连接工厂）
        GenericObjectPool<ModbusMaster> pool = new GenericObjectPool<>(
                new ModbusMasterFactory(poolKey), poolConfig);
        log.info("创建Modbus连接池成功：{}，配置={}", poolKey, poolConfig);
        return pool;
    }

    /**
     * 生成连接池Key：ip:port（如192.168.2.1:501）
     */
    private String getPoolKey(String ip, int port) {
        return ip + ":" + port;
    }

    /**
     * 连接工厂：负责创建、验证、销毁ModbusMaster（Pool2核心接口）
     */
    private class ModbusMasterFactory extends BasePooledObjectFactory<ModbusMaster> {
        private final String poolKey;
        private final String ip;
        private final int port;

        public ModbusMasterFactory(String poolKey) {
            this.poolKey = poolKey;
            String[] parts = poolKey.split(":");
            this.ip = parts[0];
            this.port = Integer.parseInt(parts[1]);
        }

        /**
         * 创建新连接（池无空闲连接时调用）
         */
        @Override
        public ModbusMaster create() throws Exception {
            IpParameters params = new IpParameters();
            params.setHost(ip);
            params.setPort(port);

            TcpMaster master = (TcpMaster) MODBUS_FACTORY.createTcpMaster(params, true);
            master.setTimeout(3000); // 连接超时3秒
            master.setRetries(0);    // 禁用内置重试（业务层控制重试）
            master.init(); // 初始化连接（关键：不初始化会导致后续操作失败）
            log.debug("创建新Modbus连接：{}", poolKey);
            return master;
        }

        /**
         * 包装连接为池化对象（Pool2要求）
         */
        @Override
        public PooledObject<ModbusMaster> wrap(ModbusMaster master) {
            return new DefaultPooledObject<>(master);
        }

        /**
         * 验证连接有效性（借连接时调用，TEST_ON_BORROW=true）
         */
        @Override
        public boolean validateObject(PooledObject<ModbusMaster> p) {
            try {
                ModbusMaster master = p.getObject();
                // 简单验证：检查连接是否初始化（master.init()后isInitialized为true）
                // 复杂验证：可发送一个空请求（如读0个寄存器），但会增加开销，这里用简单验证
                if (master instanceof TcpMaster) {
                    TcpMaster tcpMaster = (TcpMaster) master;
                    java.lang.reflect.Field initializedField = TcpMaster.class.getDeclaredField("initialized");
                    initializedField.setAccessible(true);
                    return (boolean) initializedField.get(tcpMaster);
                }
                return false;
            } catch (Exception e) {
                log.warn("验证Modbus连接无效：{}", poolKey, e);
                return false;
            }
        }

        /**
         * 销毁无效连接（验证失败或池清理时调用）
         */
        @Override
        public void destroyObject(PooledObject<ModbusMaster> p) {
            destroyMaster(p.getObject());
        }
    }

    /**
     * 容器关闭时销毁所有连接池（避免资源泄漏）
     */
    @PostConstruct
    public void init() {
        log.info("ModbusMasterPool初始化成功");
        // JVM关闭钩子：确保容器关闭时销毁所有池
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.info("开始销毁所有Modbus连接池");
            poolMap.forEach((key, pool) -> {
                try {
                    pool.close();
                    log.info("销毁连接池成功：{}", key);
                } catch (Exception e) {
                    log.error("销毁连接池失败：{}", key, e);
                }
            });
            poolMap.clear();
        }));
    }
}
