package com.zehong.system.service.impl;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.common.utils.DateUtils;
import com.zehong.common.utils.StringUtils;
import com.zehong.system.domain.TEquipmentInfo;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.TEquipmentInfoMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.business.DeviceStatusReaderAndTimeSetter;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.IRobotArmCommandService;
import com.zehong.system.service.ITStoreyInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 老化层信息Service业务层处理
 * 
 * @author zehong
 * @date 2025-05-28
 */
@Service
public class TStoreyInfoServiceImpl implements ITStoreyInfoService 
{
    public static final Logger log = LoggerFactory.getLogger(TStoreyInfoServiceImpl.class);
    @Resource
    private TStoreyInfoMapper tStoreyInfoMapper;

    @Resource
    private TEquipmentInfoMapper equipmentInfoMapper;

    @Resource
    private IRobotArmCommandService robotArmCommandService;

    @Resource
    private DeviceStatusReaderAndTimeSetter deviceStatusReaderAndTimeSetter;

    @Autowired
    private ModbusResultHandler modbusResultHandler;

    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    /**
     * 查询老化层信息
     * 
     * @param fStoreyId 老化层信息ID
     * @return 老化层信息
     */
    @Override
    public TStoreyInfo selectTStoreyInfoById(Long fStoreyId)
    {
        return tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
    }

    /**
     * 更新南边指令
     */
    private void updateNCommand() {
        List<Long> equipmentIds = Arrays.asList(18L, 19L, 20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L);
        List<TStoreyInfo> tStoreyInfos = tStoreyInfoMapper.selectByEquipmentIds(equipmentIds);
        if(tStoreyInfos.size() > 0) {
            for (TStoreyInfo tStoreyInfo : tStoreyInfos) {
                String s = tStoreyInfo.getfStoreyCode();
                Long aLong = tStoreyInfo.getfEquipmentId();
                String[] split = s.split("-");
                String feedingCommand = "NSL" + (aLong - 17L) + split[1];
                String blankingCommand = "NXL" + (aLong - 17L) + split[1];
                tStoreyInfo.setFeedingCommand(feedingCommand);
                tStoreyInfo.setBlankingCommand(blankingCommand);
            }
            tStoreyInfoMapper.updateBatch(tStoreyInfos);
        }
    }
    /**
     * 更新北边指令
     */
    private void updateBCommand(){
        List<Long> equipmentIds = Arrays.asList(3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 17L);
        List<TStoreyInfo> tStoreyInfos = tStoreyInfoMapper.selectByEquipmentIds(equipmentIds);
        if(tStoreyInfos.size() > 0) {
            for (TStoreyInfo tStoreyInfo : tStoreyInfos) {
                String s = tStoreyInfo.getfStoreyCode();
                String[] split = s.split("-");
                String feedingCommand = "BSL" + split[0] + split[1];
                String blankingCommand = "BXL" + split[0] + split[1];
                tStoreyInfo.setFeedingCommand(feedingCommand);
                tStoreyInfo.setBlankingCommand(blankingCommand);
            }
            tStoreyInfoMapper.updateBatch(tStoreyInfos);
        }
    }

    private void initStoreyInfo() {
        List<TEquipmentInfo> equipmentInfos1 = equipmentInfoMapper.selectTEquipmentInfoList(new TEquipmentInfo());
        List<TStoreyInfo> tStoreyInfos = tStoreyInfoMapper.selectTStoreyInfoList(new TStoreyInfo());

        if (tStoreyInfos.size() < 16) {
            TStoreyInfo storeyInfo;
            List<TStoreyInfo> storeyInfoList = new ArrayList<>();

            for (TEquipmentInfo tEquipmentInfo : equipmentInfos1) {
                String ip = tEquipmentInfo.getfIp();
                if (ip == null || !ip.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) {
                    continue;
                }

                String[] split = ip.split("\\.");
                if (split.length != 4) {
                    continue;
                }

                try {
                    int start = Integer.parseInt(split[3]) + 9; // 起始值（如IP最后一段为81，则start=90）
                    String baseIp = split[0] + "." + split[1] + "." + split[2] + ".";
                    int code = 1;

                    // 核心修改：循环10次（i从start递减到start-9，共10个值）
                    for (int i = start; i >= start - 9; i--) {
                        storeyInfo = new TStoreyInfo();
                        storeyInfo.setfEquipmentId(tEquipmentInfo.getfEquipmentId());
                        storeyInfo.setfStoreyCode(tEquipmentInfo.getfEquipmentCode() + "-" + code);
                        storeyInfo.setfIp(baseIp + i); // 生成IP：如192.168.2.90、192.168.2.89...192.168.2.81
                        storeyInfo.setfPort("501,502,503");
                        storeyInfo.setfStatus("0");
                        storeyInfo.setfCreateTime(new Date());
                        storeyInfoList.add(storeyInfo);
                        code++;
                    }
                } catch (NumberFormatException e) {
                    continue;
                }
            }

            if (!storeyInfoList.isEmpty()) {
                tStoreyInfoMapper.insertBatch(storeyInfoList);
            }
        }
    }

    /**
     * 查询老化层信息
     *
     * @param fStoreyCode 老化层信息Code
     * @return 老化层信息
     */
    @Override
    public TStoreyInfo selectTStoreyInfoByCode(String fStoreyCode)
    {
        return tStoreyInfoMapper.selectTStoreyInfoByCode(fStoreyCode);
    }

    /**
     * 查询老化层信息列表
     * 
     * @param tStoreyInfo 老化层信息
     * @return 老化层信息
     */
    @Override
    public List<TStoreyInfo> selectTStoreyInfoList(TStoreyInfo tStoreyInfo)
    {
        return tStoreyInfoMapper.selectTStoreyInfoList(tStoreyInfo);
    }

    /**
     * 根据设备id 查询层信息
     * @param fEquipmentId f
     * @return r
     */
    @Override
    public List<TStoreyInfo> queryByDepartmentId(Long fEquipmentId) {
        return tStoreyInfoMapper.queryByDepartmentId(fEquipmentId);
    }

    public AjaxResult powerOffAllStore() {

        List<String> types = Arrays.asList("1", "2");
        List<TEquipmentInfo> equipmentInfos = equipmentInfoMapper.selectTEquipmentList(types);

        if (equipmentInfos.size() == 0) {
            return AjaxResult.error("无设备信息！！！");
        }

// 收集所有异步任务
        List<CompletableFuture<TEquipmentInfo>> futures = equipmentInfos.stream()
                .map(equipmentInfo -> CompletableFuture.supplyAsync(() -> {
                    ModbusMaster master = null;
                    List<Integer> registerOffsets = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
                    try {
                        master = Modbus4jUtils.getMaster(equipmentInfo.getfPowerOutageIp(), equipmentInfo.getfPowerOutagePort());

                        for (Integer registerOffset : registerOffsets) {
                            Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffset);
                            if (aBoolean) {
                                Modbus4jUtils.writeCoil(master, 1, registerOffset, false);
                            }
                        }
                    } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
                        log.info("equipmentinfo -> ip = {} port = {}" + equipmentInfo.getfPowerOutageIp(), equipmentInfo.getfPowerOutagePort());
                        throw new RuntimeException(e);
                    } finally {
                        // 建议在这里关闭master连接，避免资源泄露
                        if (master != null) {
                            try {
                                master.destroy();
                            } catch (Exception e) {
                                // 处理关闭异常
                            }
                        }
                    }
                    return equipmentInfo;
                }, executorService))
                .collect(Collectors.toList());

// 等待所有异步任务完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

// 所有任务完成后执行数据库更新
        allFutures.thenRun(() -> {
            TStoreyInfo tStoreyInfo = new TStoreyInfo();
            tStoreyInfo.setfStatus("0");
            tStoreyInfo.setfAgingStartTime(null);
            tStoreyInfoMapper.updateAllStatusAndAgingStartTime(tStoreyInfo);
        }).join(); // 阻塞等待所有操作完成

        return AjaxResult.success();
    }

    /**
     * 批量处理
     * @param command c
     * @return r
     */
    @Override
    public AjaxResult handleFeeding(String command) {

        // 发送UDP指令
        robotArmCommandService.sendCommand(command);
        return AjaxResult.success();
    }

    /**
     * 批量关灯
     * @return r
     */
    @Override
    public AjaxResult handleBlanking(String command) {
        // 发送UDP指令
        robotArmCommandService.sendCommand(command);
        return AjaxResult.success();
    }

    /**
     * 批量开灯
     * @return r
     */
    @Override
    public AjaxResult powerOnAllStore() {
        List<String> types = Arrays.asList("1", "2");
        List<TEquipmentInfo> equipmentInfos = equipmentInfoMapper.selectTEquipmentList(types);

        if (equipmentInfos.size() == 0) {
            return AjaxResult.error("无设备信息！！！");
        }

// 收集所有异步任务
        List<CompletableFuture<TEquipmentInfo>> futures = equipmentInfos.stream()
                .map(equipmentInfo -> CompletableFuture.supplyAsync(() -> {
                    ModbusMaster master = null;
                    List<Integer> registerOffsets = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
                    try {
                        master = Modbus4jUtils.getMaster(equipmentInfo.getfPowerOutageIp(), equipmentInfo.getfPowerOutagePort());

                        for (Integer registerOffset : registerOffsets) {
                            Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffset);
                            if (!aBoolean) {
                                Modbus4jUtils.writeCoil(master, 1, registerOffset, true);
                            }
                        }
                    } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
                        log.info("equipmentinfo -> ip = {} port = {}" + equipmentInfo.getfPowerOutageIp(), equipmentInfo.getfPowerOutagePort());
                        throw new RuntimeException(e);
                    } finally {
                        // 建议在这里关闭master连接，避免资源泄露
                        if (master != null) {
                            try {
                                master.destroy();
                            } catch (Exception e) {
                                // 处理关闭异常
                            }
                        }
                    }
                    return equipmentInfo;
                }, executorService))
                .collect(Collectors.toList());

// 等待所有异步任务完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

// 所有任务完成后执行数据库更新
        allFutures.thenRun(() -> {
            TStoreyInfo tStoreyInfo = new TStoreyInfo();
            tStoreyInfo.setfStatus("1");
            tStoreyInfo.setfAgingStartTime(DateUtils.getNowDate());
            tStoreyInfoMapper.updateAllStatusAndAgingStartTime(tStoreyInfo);
        }).join(); // 阻塞等待所有操作完成

        return AjaxResult.success();
    }

    @Override
    public AjaxResult handleReadingHoldingRegister(String ipAndPort) {

        if(StringUtils.isBlank(ipAndPort) || ipAndPort.split("-").length != 2) {
            return AjaxResult.error("ip和端口格式错误！！！");
        }

        String[] split = ipAndPort.split("-");
        List<Integer> registerOffset ;
        if("501".equals(split[1])) {
            registerOffset = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27);
        } else if("502".equals(split[1])) {
            registerOffset = Arrays.asList(28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,49, 50, 51, 52, 53,54);
        } else {
            registerOffset = Arrays.asList(55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72);
        }
        deviceStatusReaderAndTimeSetter.startMultiDeviceMonitoring(split[0], Integer.parseInt(split[1]),registerOffset, modbusResultHandler, ModbusResultHandler.createDefaultStopCondition());


        return null;
    }

    /**
     * 批量读取设备状态
     * @param fEquipmentCode f
     * @return r
     */
    @Override
    public AjaxResult batchReadingCabinetStatus(String fEquipmentCode) {

        TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(fEquipmentCode);
        if(tEquipmentInfo == null) {
            return AjaxResult.error("设备不存在！！！");
        }
        List<Integer> integers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

        try {
            ModbusMaster master = Modbus4jUtils.getMaster(tEquipmentInfo.getfIp(), tEquipmentInfo.getfPort());

            Map<Integer, Object> integerObjectMap = Modbus4jUtils.batchReadAgingCabinetStatus(master, integers);

            return AjaxResult.success(integerObjectMap);

        } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 给设备断电
     * @param tStoreyInfo t
     * @return r
     */
    @Override
    public AjaxResult powerOutage(TStoreyInfo tStoreyInfo) {

        if(tStoreyInfo.getfEquipmentId() == null || tStoreyInfo.getfStoreyCode() == null) {
            return AjaxResult.error("参数异常！！！");
        }
        TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoById(tStoreyInfo.getfEquipmentId());
        String registerOffset = tStoreyInfo.getfStoreyCode().split("-")[1];

        try {
            ModbusMaster master = Modbus4jUtils.getMaster(tEquipmentInfo.getfPowerOutageIp(), tEquipmentInfo.getfPowerOutagePort());

            int i = Integer.parseInt(registerOffset);
            int registerOffsetInt = i - 1;
            Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffsetInt);

            if(aBoolean) {
                Modbus4jUtils.writeCoil(master, 1, registerOffsetInt, false);

                tStoreyInfo.setfStatus("0");
                tStoreyInfo.setfAgingStartTime(null);
                tStoreyInfoMapper.updateTStoreyInfo(tStoreyInfo);
            }
            return AjaxResult.success();

        } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 给设备上电
     * @param tStoreyInfo t
     * @return r
     */
    @Override
    public AjaxResult powerOn(TStoreyInfo tStoreyInfo) {

        if(tStoreyInfo.getfEquipmentId() == null || tStoreyInfo.getfStoreyCode() == null) {
            return AjaxResult.error("参数异常！！！");
        }

        TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoById(tStoreyInfo.getfEquipmentId());
        String registerOffset = tStoreyInfo.getfStoreyCode().split("-")[1];

        try {
            ModbusMaster master = Modbus4jUtils.getMaster(tEquipmentInfo.getfPowerOutageIp(), tEquipmentInfo.getfPowerOutagePort());

            int i = Integer.parseInt(registerOffset);
            int registerOffsetInt = i - 1;
            Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffsetInt);

            if(!aBoolean) {
                Modbus4jUtils.writeCoil(master, 1, registerOffsetInt, true);

                tStoreyInfo.setfStatus("1");
                tStoreyInfo.setfAgingStartTime(new Date());
                tStoreyInfoMapper.updateTStoreyInfo(tStoreyInfo);
            }
            return AjaxResult.success();

        } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 新增老化层信息
     * 
     * @param tStoreyInfo 老化层信息
     * @return 结果
     */
    @Override
    public int insertTStoreyInfo(TStoreyInfo tStoreyInfo)
    {

        check(tStoreyInfo);
        tStoreyInfo.setfCreateTime(DateUtils.getNowDate());

        return tStoreyInfoMapper.insertTStoreyInfo(tStoreyInfo);
    }

    private void check(TStoreyInfo tStoreyInfo) {

        if(StringUtils.isBlank(tStoreyInfo.getfStoreyCode())){
            throw new RuntimeException("层编号不能为空");
        }
        if(tStoreyInfo.getfStoreyCode() != null && tStoreyInfo.getfStoreyId() ==  null) {
            TStoreyInfo storeyInfo = tStoreyInfoMapper.selectTStoreyInfoByCode(tStoreyInfo.getfStoreyCode());
            if(storeyInfo != null) {
                throw new RuntimeException("层编号已存在");
            }
        }
        if(StringUtils.isBlank(tStoreyInfo.getfEquipmentCode())){
            throw new RuntimeException("柜编号不许为空");
        }

        TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(tStoreyInfo.getfEquipmentCode());
        if(tEquipmentInfo == null) {
            throw new RuntimeException("柜编号不存在");
        }
        tStoreyInfo.setfEquipmentId(tEquipmentInfo.getfEquipmentId());
    }

    /**
     * 修改老化层信息
     * 
     * @param tStoreyInfo 老化层信息
     * @return 结果
     */
    @Override
    public int updateTStoreyInfo(TStoreyInfo tStoreyInfo)
    {
        check(tStoreyInfo);
        tStoreyInfo.setUpdateTime(DateUtils.getNowDate());
        return tStoreyInfoMapper.updateTStoreyInfo(tStoreyInfo);
    }

    /**
     * 批量删除老化层信息
     * 
     * @param fStoreyIds 需要删除的老化层信息ID
     * @return 结果
     */
    @Override
    public int deleteTStoreyInfoByIds(Long[] fStoreyIds)
    {
        return tStoreyInfoMapper.deleteTStoreyInfoByIds(fStoreyIds);
    }

    /**
     * 删除老化层信息信息
     * 
     * @param fStoreyId 老化层信息ID
     * @return 结果
     */
    @Override
    public int deleteTStoreyInfoById(Long fStoreyId)
    {
        return tStoreyInfoMapper.deleteTStoreyInfoById(fStoreyId);
    }
}
