package com.yida.data.attendance.service.impl;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yida.data.attendance.dto.AddAttendanceRecordDTO;
import com.yida.data.attendance.mapper.EduAttendanceDeviceMapper;
import com.yida.data.attendance.service.AttendanceRecordService;
import com.yida.data.attendance.service.EduAttendanceStrategyService;
import com.yida.data.attendance.service.EduUserStrategyService;
import com.yida.data.attendance.service.ErrorRecordMsgService;
import com.yida.data.attendance.utils.MqttUtil;
import com.yida.data.common.core.entity.ErrorRecordMsg;
import com.yida.data.common.core.entity.ReceiveMsg;
import com.yida.data.common.core.entity.attendance.EduAttendanceDevice;
import com.yida.data.common.core.entity.attendance.EduUserStrategy;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.constant.LockPrefixConstant;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.entity.constant.hq.HaiqingCallbackCodeConstant;
import com.yida.data.common.core.entity.constant.hq.HaiqingConstant;
import com.yida.data.common.core.entity.user.EduFaceGroupStrategy;
import com.yida.data.common.core.entity.user.EduFaceGroupUser;
import com.yida.data.common.core.entity.user.EduUserDevice;
import com.yida.data.common.core.entity.user.EduUserFace;
import com.yida.data.common.core.enums.DeviceType;
import com.yida.data.common.core.enums.MsgChannel;
import com.yida.data.common.core.enums.RecordTypeEnum;
import com.yida.data.common.core.utils.FileUtil;
import com.yida.data.user.dto.UpdateFaceGroupUserStatusDTO;
import com.yida.data.user.feign.RemoteFaceGroupStrategyService;
import com.yida.data.user.feign.RemoteUserDeviceService;
import com.yida.data.user.feign.RemoteUserFaceService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
@RefreshScope
public class HaiqingCallbackService implements MqttCallbackExtended {

    private static MqttUtil mqttUtil;
    @Lazy
    @Resource
    private AttendanceRecordService attendanceRecordService;
    @Resource
    private EduAttendanceDeviceMapper eduAttendanceDeviceMapper;
    @Resource
    private RemoteUserFaceService remoteUserFaceService;
    @Resource
    private RemoteUserDeviceService remoteUserDeviceService;
    @Resource
    private ErrorRecordMsgService errorRecordMsgService;
    @Resource
    private RemoteFaceGroupStrategyService remoteFaceGroupStrategyService;

    @Resource
    private RedisService redisService;

    @Resource
    private ApplicationContext applicationContext;

    @Lazy
    @Resource
    private EduUserStrategyService eduUserStrategyService;

    private static final Snowflake SNOWFLAKE;

    private static final ExecutorService MQTT_HEARTBEAT_POOL = new ThreadPoolExecutor(5, 10, 1,
        TimeUnit.MINUTES, new LinkedBlockingQueue<>());

    @Value("${febs.uploadUrl}")
    private String uploadUrl;

    static {
        SNOWFLAKE = new Snowflake(1L, 1L);
    }

    public void setMqttUtil(MqttUtil util) {
        mqttUtil = util;
    }

    @Override
    public void connectComplete(boolean b, String s) {
        //if (b) {
        // 断线重连后重新添加订阅
        log.info("----------------");
        log.info("连接成功");
        log.info("----------------");
        // 全部设备消息订阅
        List<EduAttendanceDevice> deviceList =
            eduAttendanceDeviceMapper.selectList(Wrappers.lambdaQuery(new EduAttendanceDevice())
                .eq(EduAttendanceDevice::getDeviceType, DeviceType.HAIQING));
        if (CollUtil.isNotEmpty(deviceList)) {
            List<String> snList = deviceList.stream()
                .map(x -> StrUtil.utf8Str("$queue/mqtt/face/" + x.getSn() + "/Ack")).collect(Collectors.toList());
            snList.addAll(deviceList.stream().map(x -> StrUtil.utf8Str("$queue/mqtt/face/" + x.getSn() +
                "/Rec")).collect(Collectors.toList()));
            mqttUtil.addTopic(snList.toArray(new String[0]));
        }
        // 心跳订阅
        mqttUtil.addTopic(StrUtil.utf8Str("$queue/mqtt/face/heartbeat"));
        // 上线订阅
        mqttUtil.addTopic(StrUtil.utf8Str("$queue/mqtt/face/basic"));
        //}
    }

    @Override
    public void connectionLost(Throwable throwable) {
        log.info("----------------");
        log.info("丢失连接", throwable);
        log.info("----------------");
    }

    /**
     * 接受消息
     */
    @Override
    public void messageArrived(String s, MqttMessage mqttMessage) {
        checkReceiveMsg(s, mqttMessage);
    }

    @Async
    public void checkReceiveMsg(String s, MqttMessage mqttMessage) {
        String msg = new String(mqttMessage.getPayload());
        JSONObject msgJson = JSONUtil.parseObj(msg);
        // 处理数据
        String type = msgJson.getStr("operator");
        JSONObject info = msgJson.getJSONObject("info");
        // 保存接收消息到es
        if (!HaiqingConstant.HEARTBEAT.equals(type)) {
            ReceiveMsg receiveMsg = new ReceiveMsg();
            receiveMsg.setTopic(s);
            receiveMsg.setMsg(msg);
            receiveMsg.setId(SNOWFLAKE.nextId());
            receiveMsg.setChannel(MsgChannel.MQTT.getValue());
            receiveMsg.setCreateDate(LocalDateTime.now());
            redisService.lSet(CachePrefixConstant.RECEIVE_MSG, receiveMsg);
//            log.info("接收的消息值, type: {}, info: {}", type, info);
        }
        try {
            switch (type) {
                // 编辑单个人员
                case HaiqingConstant.EDIT_PERSON_CALL:
                    log.info("增改人员消息接收的消息值, type: {}, info: {}", type, info);
                    editUser(info);
                    break;
                // 批量增加与修改 处理逻辑相同
                case HaiqingConstant.ADD_PERSON_BATCH_CALL:
                    log.info("批量增加人员接收的消息值, type: {}, info: {}", type, info);
                    editUserBatch(msgJson, info, 1);
                    break;
                case HaiqingConstant.UPDATE_PERSON_BATCH_CALL:
                    log.info("批量修改人员接收的消息值, type: {}, info: {}", type, info);
                    editUserBatch(msgJson, info, 0);
                    break;
                // 删除单个人员
                case HaiqingConstant.DELETE_PERSON_CALL:
                    log.info("删除人员接收的消息值, type: {}, info: {}", type, info);
                    deleteUser(info);
                    break;
                // 批量删除人员
                case HaiqingConstant.DELETE_PERSON_BATCH_CALL:
                    log.info("批量删除人员接收的消息值, type: {}, info: {}", type, info);
                    deleteUserBatch(info);
                    break;
                // 认证信息推送
                case HaiqingConstant.REC_PERSON:
                    recPerson(s, info);
                    break;
                // 心跳保活
                case HaiqingConstant.HEARTBEAT:
                    heartbeat(info);
                    break;
                case HaiqingConstant.UNLOCK_ACK:
                    openDoor(info);
                    break;
                case HaiqingConstant.SET_SYS_TIME_ACK:
                    setSysTime(info);
                    break;
                // 设备上线
                case HaiqingConstant.ONLINE:
                    online(info);
                    break;
                // 回复接收到认证记录信息返回
                case HaiqingConstant.PUSH_MESSAGE_ACK:
                    log.info("接收确认返回: {}", info);
                    pushMessageReturn(info);
                    break;
                default:
            }
        } catch (Exception e) {
            info.remove("pic");
            log.error("haiqing callback fail, info: {}", JSONUtil.toJsonStr(info), e);
        }
    }

    /**
     * 回复接收到认证记录信息返回
     *
     * @param info 接收的返回信息
     * @author ZYJ
     * @date 2023/10/27 11:35
     */
    private void pushMessageReturn(JSONObject info) {
        try {
            // 设备号
            String deviceSn = info.getStr("facesluiceId");
            // 记录id
            Integer recordId = info.getInt("SnapOrRecordID");
            // 操作结果,成功:“ok”/失败:“fail”
            String result = info.getStr("result");
            // 操作成功删除缓存信息
            if (HaiqingCallbackCodeConstant.PUSH_RETURN_SUCCESS_RESULT.equals(result)) {
                redisService.hdel(CachePrefixConstant.HQ_RECEIVE_INFO + deviceSn, String.valueOf(recordId));
            }
        } catch (Exception e) {
            log.error("回复接收到认证记录信息返回处理失败", e);
        }
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

    }


    private void editUser(JSONObject info) {
        if ("fail".equals(info.get("result").toString())) {
            // 删除本地对应关系，重置用户状态
            Integer type = Integer
                .valueOf(info.get("customId").toString().substring(info.get("customId").toString().length() - 1));
            Long userId = Long
                .valueOf(info.get("customId").toString().substring(0, info.get("customId").toString().length() - 1));
            EduUserFace userFace = remoteUserFaceService.getFaceByUser(userId, type).getData();
            // 发送重新采集通知
            if (userFace != null) {
//                noticeService.sendCollectFailNotice(userFace.getPicPath(), userId, type);
                remoteUserFaceService.delFaceByUser(userId, type);
            }
            log.error("mqtt编辑用户失败,customId:{},msg:{}", info.get("customId"), info.get("detail").toString());
        }
    }

    /**
     * 批量增加人员回调
     *
     * @param msgJson 接收设备的所有信息
     * @param info    接收的设备info信息
     * @param type    0-修改，1-增加
     */
    private void editUserBatch(JSONObject msgJson, JSONObject info, Integer type) {
        // 设备号
        String deviceSn = info.getStr("facesluiceId");

        // 添加设备锁
        String lockKey = LockPrefixConstant.ISSUE_FACE_DEVICE_LOCK + deviceSn;
        // 加锁
        redisService.falseLock(lockKey);
        try {
            // 人脸组id
            Object o = redisService.hget(CachePrefixConstant.ISSUE_FACE_GROUP_DEVICE, deviceSn);
            Long faceGroupId = Objects.nonNull(o) ? Long.valueOf(o.toString()) : null;
            // 设备回调后删除当前设备下发的人脸信息
            redisService.del(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn);
            // 当前设备下发的人脸信息
            Map<Object, Object> faceList;
            // 删除海清人脸下发信息. 0-修改，1-增加
            if (type == 0) {
                // 编辑信息
                faceList = redisService.hmget(CachePrefixConstant.HQ_BATCH_EDIT + deviceSn);
                redisService.del(CachePrefixConstant.HQ_BATCH_EDIT + deviceSn);
            } else {
                // 新增信息
                faceList = redisService.hmget(CachePrefixConstant.HQ_BATCH_ADD + deviceSn);
                redisService.del(CachePrefixConstant.HQ_BATCH_ADD + deviceSn);
            }
            // 释放批量编辑锁
            redisService.falseUnLock(LockPrefixConstant.HQ_DEVICE_USER_EDIT_LOCK + deviceSn);

            // 判断返回状态码
            String code = msgJson.getStr("code");
            // 当前设备全部失败
            if (HaiqingCallbackCodeConstant.DEVICE_BUSY.equals(code)
                || HaiqingCallbackCodeConstant.DATA_OUT_OF_RANGE.equals(code)) {
                // 添加失败信息并返回
                redisService.incr(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId, (long) faceList.size());
                redisService.incr(CachePrefixConstant.ISSUE_FACE_ERROR + faceGroupId, (long) faceList.size());
                // 设置当前设备错误信息
                redisService.lSet(CachePrefixConstant.ISSUE_FACE_ERROR_MSG + faceGroupId + StringConstant.DOT + deviceSn,
                    faceList.values());
                // 解除设备锁
                redisService.falseUnLock(lockKey);

                // 修改人脸状态
                if (CollUtil.isNotEmpty(faceList)) {
                    // 失败的用户  修改其下发状态  删除下发记录
                    UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                    dto.setUserList(faceList.values().stream().map(value -> {
                        EduUserFace userFace = (EduUserFace) value;
                        EduFaceGroupUser user = new EduFaceGroupUser();
                        user.setUserId(userFace.getUserId());
                        user.setUserType(userFace.getUserType());
                        user.setFaceGroupId(userFace.getFaceGroupUserId());
                        return user;
                    }).collect(Collectors.toList()));
                    dto.setStatus(EduFaceGroupUser.ISSUE_FAIL);
                    remoteUserFaceService.updateFaceGroupUserStatus(dto);
                }
                return;
            }

            // 设备信息
            EduAttendanceDevice device = eduAttendanceDeviceMapper.selectOne(
                Wrappers.lambdaQuery(new EduAttendanceDevice()).eq(EduAttendanceDevice::getSn, deviceSn)
                    .eq(EduAttendanceDevice::getDeviceType, DeviceType.HAIQING.getValue()));
//            // 策略下发信息
//            JSONObject userParams = JSONUtil.createObj();
//            // 设置人员绑定策略
//            userParams.set("operator", HaiqingConstant.PERSONS_BIND_STRATEGY)
//                    .set("messageId", StrUtil.uuid().replace("-", ""));
//            JSONArray personsInfo = JSONUtil.createArray();
            // 下发的人脸信息
            List<EduUserFace> userFaceList = new ArrayList<>();

            // 判断设备数量
            Object onlineNumberObject = redisService.get(CachePrefixConstant.ISSUE_FACE_GROUP_DEVICE_NUMBER + device.getSn());
            int onlineNumber = Objects.nonNull(onlineNumberObject) ? (Integer) onlineNumberObject : 1;

            if (info.getInt("AddSucNum") != 0) {
                // 去除添加成功人员
                JSONArray addSucInfo = info.getJSONArray("AddSucInfo");
                // 用户和设备关联信息
                List<EduUserDevice> userDeviceList = new ArrayList<>();
                // 下发成功人脸信息
                List<EduFaceGroupUser> successList = new ArrayList<>();
                EduUserFace userFace = null;

                for (Object sucInfo : addSucInfo) {
                    try {
                        JSONObject sucMsg = (JSONObject) sucInfo;
                        String customId = sucMsg.getStr("customId");
                        // 删除redis中的缓存
//                    redisService.hdel(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn, customId);
                        // 保存设备的下发记录
                        userFace = (EduUserFace) faceList.get(customId);
                        if (userFace != null) {
                            if (type == 1) {
                                // 新增下发成功添加人脸与设备的关联关系
                                EduUserDevice userDevice = new EduUserDevice();
                                userDevice.setDeviceId(device.getId());
                                userDevice.setFaceGroupId(userFace.getFaceGroupUserId());
                                userDevice.setUserId(userFace.getUserId());
                                userDevice.setUserType(userFace.getUserType());
                                userDeviceList.add(userDevice);
                            }
                            // 添加策略下发人员信息
                            userFaceList.add(userFace);
                        }
                        // 添加策略关联信息
//                        JSONObject singleObject = JSONUtil.createObj()
//                                // 用户id+用户类型
//                                .set("customId", customId);
//                        Map<Object, Object> map = redisService.hmget(CachePrefixConstant.USER_ATTENDANCE_STRATEGY
//                                + deviceSn + "-" + customId);
//                        if (CollUtil.isEmpty(map)) {
//                            continue;
//                        }
//                        // 设置用户信息
//                        JSONArray array = JSONUtil.createArray();
//                        map.forEach((k, v) -> array.add(k));
//                        singleObject.set("strategyID", array);
//                        personsInfo.add(singleObject);
                    } catch (Exception e) {
                        log.error("成功下发人脸错误信息: {}", sucInfo, e);
                    } finally {
                        // 添加成功人脸信息
                        if (Objects.nonNull(userFace)) {
                            // 添加人脸信息锁
                            String userFaceLockKey = LockPrefixConstant.ISSUE_FACE_GROUP_LOCK + userFace.getFaceGroupUserId()
                                + StringConstant.DOT + userFace.getUserId();
                            redisService.lock(userFaceLockKey);
                            // 判断数量. 所有数量成功才修改人脸状态
                            String groupUserSuccessKey =
                                CachePrefixConstant.ISSUE_GROUP_USER_FACE_SUCCESS + userFace.getFaceGroupUserId();
                            if (redisService.hHasKey(groupUserSuccessKey, String.valueOf(userFace.getUserId()))) {
                                // 成功数量
                                Integer successNum = (Integer) redisService
                                    .hget(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                if (successNum + 1 >= onlineNumber) {
                                    // 下发成功的人脸
                                    EduFaceGroupUser user = new EduFaceGroupUser();
                                    user.setUserId(userFace.getUserId());
                                    user.setUserType(userFace.getUserType());
                                    user.setFaceGroupId(userFace.getFaceGroupUserId());
                                    successList.add(user);
                                    redisService.hdel(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                } else {
                                    // 成功数量加1
                                    redisService.hashIncrement(groupUserSuccessKey, String.valueOf(userFace.getUserId()), 1L);
                                }
                            } else {
                                if (onlineNumber == 1) {
                                    // 下发成功的人脸
                                    EduFaceGroupUser user = new EduFaceGroupUser();
                                    user.setUserId(userFace.getUserId());
                                    user.setUserType(userFace.getUserType());
                                    user.setFaceGroupId(userFace.getFaceGroupUserId());
                                    successList.add(user);
                                    redisService.hdel(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                } else {
                                    // 添加成功数量
                                    redisService.hset(groupUserSuccessKey, String.valueOf(userFace.getUserId()), 1);
                                }
                            }
                            redisService.unlock(userFaceLockKey);
                        }
                    }
                }
                // 需要绑定策略的信息
//                if (CollUtil.isNotEmpty(personsInfo)) {
//                    userParams.set("info", JSONUtil.createObj().set("personsInfo", personsInfo));
//                    log.info("推送的人员绑定策略消息=================sn: {}, params: {}", device.getSn(), userParams);
//                    // 绑定策略消息
//                    mqttUtil.publish(userParams.toString(), "mqtt/face/" + device.getSn());
//                }
                // 保存人员和设备关联信息
                if (CollUtil.isNotEmpty(userDeviceList)) {
                    remoteUserDeviceService.batchSave(userDeviceList);
                }
                // 保存下发成功信息
                if (CollUtil.isNotEmpty(successList)) {
                    UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                    dto.setUserList(successList);
                    dto.setStatus(EduFaceGroupUser.ISSUED);
                    remoteUserFaceService.updateFaceGroupUserStatus(dto);
                }
                // 添加成功人员数量
                redisService.incr(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId, info.getLong("AddSucNum"));
            }
            if (info.getInt("AddErrNum") != 0) {
                // 下发失败修改下发状态
                JSONArray addErrInfo = info.getJSONArray("AddErrInfo");
                // 用户和设备关联信息
                List<EduUserDevice> userDeviceList = new ArrayList<>();
                // 下发成功人脸信息
                List<EduFaceGroupUser> successList = new ArrayList<>();
                // 下发失败人脸信息
                List<EduFaceGroupUser> errorList = new ArrayList<>();

                long error = 0;
                long finish = 0;
                for (Object errInfo : addErrInfo) {
                    String customId = "";
                    String errCode = "";
                    // 保存设备的下发记录
                    EduUserFace userFace = null;
                    try {
                        JSONObject errMsg = (JSONObject) errInfo;
                        customId = errMsg.getStr("customId");
                        errCode = errMsg.getStr("errcode");
                        userFace = (EduUserFace) faceList.get(customId);
                        log.error("issue face fail,msg:[{}],customId:[{}]", errCode, customId);
                        if (Objects.nonNull(userFace)) {
                            // 新增人员时, 461代表设备上customId已经存在, 需要添加本地数据
                            if (HaiqingCallbackCodeConstant.ADD_PERSON_EXIST.equals(errCode) && type == 1) {

                                // 将本地数据修改为已存在
                                EduUserDevice userDevice = new EduUserDevice();
                                // 人脸与设备的关联关系
                                userDevice.setDeviceId(device.getId());
                                userDevice.setFaceGroupId(userFace.getFaceGroupUserId());
                                userDevice.setUserId(userFace.getUserId());
                                userDevice.setUserType(userFace.getUserType());
                                userDeviceList.add(userDevice);

                                // 添加人脸信息锁
                                String userFaceLockKey = LockPrefixConstant.ISSUE_FACE_GROUP_LOCK + userFace.getFaceGroupUserId()
                                    + StringConstant.DOT + userFace.getUserId();
                                redisService.lock(userFaceLockKey);
                                // 判断数量. 所有数量成功才修改人脸状态
                                String groupUserSuccessKey =
                                    CachePrefixConstant.ISSUE_GROUP_USER_FACE_SUCCESS + userFace.getFaceGroupUserId();
                                if (redisService.hHasKey(groupUserSuccessKey, String.valueOf(userFace.getUserId()))) {
                                    // 成功数量
                                    Integer successNum = (Integer) redisService
                                        .hget(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                    if (successNum + 1 >= onlineNumber) {
                                        // 下发成功的人脸
                                        EduFaceGroupUser user = new EduFaceGroupUser();
                                        user.setUserId(userFace.getUserId());
                                        user.setUserType(userFace.getUserType());
                                        user.setFaceGroupId(userFace.getFaceGroupUserId());
                                        successList.add(user);
                                        redisService.hdel(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                    } else {
                                        // 成功数量加1
                                        redisService.hashIncrement(groupUserSuccessKey, String.valueOf(userFace.getUserId()), 1L);
                                    }
                                } else {
                                    if (onlineNumber == 1) {
                                        // 下发成功的人脸
                                        EduFaceGroupUser user = new EduFaceGroupUser();
                                        user.setUserId(userFace.getUserId());
                                        user.setUserType(userFace.getUserType());
                                        user.setFaceGroupId(userFace.getFaceGroupUserId());
                                        successList.add(user);
                                        redisService.hdel(groupUserSuccessKey, String.valueOf(userFace.getUserId()));
                                    } else {
                                        // 添加成功数量
                                        redisService.hset(groupUserSuccessKey, String.valueOf(userFace.getUserId()), 1);
                                    }
                                }
                                redisService.unlock(userFaceLockKey);
                                // 添加策略下发人员信息
                                userFaceList.add(userFace);

                                // 添加策略关联信息
//                                JSONObject singleObject = JSONUtil.createObj()
//                                        // 用户id+用户类型
//                                        .set("customId", customId);
//                                Map<Object, Object> map = redisService.hmget(CachePrefixConstant.USER_ATTENDANCE_STRATEGY
//                                        + deviceSn + "-" + customId);
//                                if (CollUtil.isEmpty(map)) {
//                                    continue;
//                                }
//                                // 设置用户信息
//                                JSONArray array = JSONUtil.createArray();
//                                map.forEach((k, v) -> array.add(k));
//                                singleObject.set("strategyID", array);
//                                personsInfo.add(singleObject);
                            }

                            // 编辑人员时, 462代表设备上不存在此customId, 需要删除本地数据
                            if (HaiqingCallbackCodeConstant.EDIT_PERSON_NOT_EXIST.equals(errCode) && type == 0) {
                                // 删除人脸与设备的关联关系
                                EduUserDevice userDevice = new EduUserDevice();
                                userDevice.setDeviceId(device.getId());
                                userDevice.setFaceGroupId(userFace.getFaceGroupUserId());
                                userDevice.setUserId(userFace.getUserId());
                                userDevice.setUserType(userFace.getUserType());
                                remoteUserDeviceService.deleteRelation(userDevice);
                            }
                        }
                    } catch (Exception e) {
                        log.error("保存人员错误下发人脸错误信息: {}", errInfo, e);
                    } finally {
                        if (StrUtil.isEmpty(customId) || StrUtil.isEmpty(errCode) || Objects.isNull(userFace)
                            || !(HaiqingCallbackCodeConstant.ADD_PERSON_EXIST.equals(errCode) && type == 1)) {
                            error++;
                            if (Objects.nonNull(userFace)) {
                                EduFaceGroupUser user = new EduFaceGroupUser();
                                user.setUserId(userFace.getUserId());
                                user.setUserType(userFace.getUserType());
                                user.setFaceGroupId(userFace.getFaceGroupUserId());
                                errorList.add(user);
                                // 添加错误人脸
                                redisService
                                    .lSet(CachePrefixConstant.ISSUE_FACE_ERROR_MSG + faceGroupId + StringConstant.DOT + deviceSn,
                                        userFace);
                            }
                        }
                        // 移除下发人脸信息
//                    redisService.hdel(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn, customId);
                        finish++;
                    }
                }

//                if (CollUtil.isNotEmpty(personsInfo)) {
//                    userParams.set("info", JSONUtil.createObj().set("personsInfo", personsInfo));
//                    log.info("推送的人员绑定策略消息=================sn: {}, params: {}", device.getSn(), userParams);
//                    // 绑定策略消息
//                    mqttUtil.publish(userParams.toString(), "mqtt/face/" + device.getSn());
//                }
                // 保存人员和设备关联信息
                if (CollUtil.isNotEmpty(userDeviceList)) {
                    remoteUserDeviceService.batchSave(userDeviceList);
                }
                // 保存下发成功信息
                if (CollUtil.isNotEmpty(successList)) {
                    UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                    dto.setUserList(successList);
                    dto.setStatus(EduFaceGroupUser.ISSUED);
                    remoteUserFaceService.updateFaceGroupUserStatus(dto);
                }
                // 添加失败人员数量
                redisService.incr(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId, finish);
                redisService.incr(CachePrefixConstant.ISSUE_FACE_ERROR + faceGroupId, error);
                // 保存下发失败信息
                if (CollUtil.isNotEmpty(errorList)) {
                    // 失败的用户  修改其下发状态  删除下发记录
                    UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                    dto.setUserList(errorList);
                    dto.setStatus(EduFaceGroupUser.ISSUE_FAIL);
                    remoteUserFaceService.updateFaceGroupUserStatus(dto);
                }
            }
            // 异步推送策略绑定信息
            if (CollUtil.isNotEmpty(userFaceList)) {
                HaiqingCallbackService bean = applicationContext.getBean(HaiqingCallbackService.class);
                bean.pushStrategyMessage(deviceSn, userFaceList, faceGroupId);
            }
        } catch (Exception e) {
            log.error("下发人脸回调失败", e);
        } finally {
            // 解除设备锁
            redisService.falseUnLock(lockKey);
        }
    }

    private void deleteUser(JSONObject info) {
        if ("fail".equals(info.get("result").toString())) {
            log.error("mqtt删除用户失败,userId:{},msg:{}", info.get("customId"), info.get("detail").toString());
        }
    }

    /**
     * 批量删除人员回调
     */
    private void deleteUserBatch(JSONObject info) {
        String deviceSn = info.getStr("facesluiceId");
        // 释放该设备的删除锁
        redisService.falseUnLock(LockPrefixConstant.HQ_DEVICE_USER_DELETE_LOCK + deviceSn);
    }

    private void setSysTime(JSONObject info) {
        if ("fail".equals(info.get("result").toString())) {
            log.error("mqtt设置时间失败,deviceId:{}", info.get("facesluiceId").toString());
        }
    }

    private void openDoor(JSONObject info) {
        if ("fail".equals(info.get("result").toString())) {
            log.error("mqtt开门失败,deviceId:{}", info.get("facesluiceId").toString());
        }
    }

    /**
     * 接收认证结果
     */
    private void recPerson(String topic, JSONObject info) {
        AddAttendanceRecordDTO dto = new AddAttendanceRecordDTO();
        Long schoolId = null;
        try {
            // TODO: 2023/10/28 断网续传验证后开启注释
//        try {
//            // 设备号
//            String deviceSn = info.getStr("facesluiceId");
//            // 记录id
//            Integer recordId = info.getInt("RecordID");
//            redisService.hset(CachePrefixConstant.HQ_RECEIVE_INFO + deviceSn, String.valueOf(recordId), info);
//
//            HaiqingCallbackService bean = applicationContext.getBean(HaiqingCallbackService.class);
//            // 接收认证消息推送方法
//            bean.pushConfirmMessage(deviceSn, 2, recordId);
//        } catch (Exception e) {
//            log.error("接收认证消息推送方法失败: {}", info, e);
//        }

//        log.info("接收认证结果:{}", info);
            String verifyStatus = info.getStr("VerifyStatus");

            if (StrUtil.equals("24", verifyStatus)) {
                //表示，无权限通过
                return;
            }

            String userFlag = info.getStr("customId");
            if (StrUtil.isBlank(userFlag)) {
                return;
            }
            Long userId = Long.valueOf(userFlag.substring(0, userFlag.length() - 1));
            // 设备号
            String sn = info.get("facesluiceId").toString();
            // 查询设备信息
            EduAttendanceDevice attendanceDevice = eduAttendanceDeviceMapper
                .selectOne(Wrappers.lambdaQuery(new EduAttendanceDevice())
                    .eq(EduAttendanceDevice::getSn, sn));
            if (Objects.isNull(attendanceDevice)) {
                log.error("设备信息不存在: {}", sn);
                return;
            }
            schoolId = attendanceDevice.getSchoolId();
            dto.setSchoolId(attendanceDevice.getSchoolId());
            dto.setDeviceSn(sn);
            dto.setCreateTime(LocalDateTime.parse(info.getStr("time"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            dto.setUserId(userId);
            // 优化类型
            char type = userFlag.charAt(userFlag.length() - 1);
            dto.setUserType(Integer.parseInt(Character.toString(type)) == 0 ? 1 : 0);
            dto.setName(info.getStr("persionName"));

            // 获取识别机推送信息
            String base64 = info.getStr("pic");
            String url = "";
            if (StrUtil.isNotEmpty(base64)) {
//            log.error("人脸认证结果接收失败, 没有图片信息");
//            return;
                // 本地路径
                String localPath = FileUtil.uploadBase64Img(base64.substring(base64.indexOf(",") + 1), "png");
                // 上传到媒资
                url = FileUtil.uploadFileToMediaServer(uploadUrl, localPath).split(",")[0];
                cn.hutool.core.io.FileUtil.del(localPath);
            }
            dto.setPicUrl(url);
//        Double temp = info.get("temperature") != null ? Double.valueOf(info.get("temperature").toString()) : null;
            // 人脸区域
//        String facePath = null;
            // 上传人脸部分
//        String faceUrl = StrUtil.isNotBlank(facePath) ? FileUtil.uploadFileToMediaServer(facePath).split(",")[0] : url;
            // 删除本地文件

//        if (facePath != null) {
//            cn.hutool.core.io.FileUtil.del(facePath);
//        }
//        log.info("人脸识别信息:[{}]", dto);
            dto.setMsgChannel(MsgChannel.MQTT);
            dto.setOriginalRecordId(info.getStr("RecordID"));
            attendanceRecordService.addRecord(dto);
        } catch (Exception e) {
            // 添加数据到es
            errorRecordMsgService.saveMsg(ErrorRecordMsg.builder()
//                    .id(SNOWFLAKE.nextId())
                    .createDate(LocalDateTime.now())
                    .schoolId(schoolId)
                    .msg(JSONUtil.toJsonStr(dto))
                    .topic(topic)
                    .channel(MsgChannel.MQTT.getValue())
                    .recordType(RecordTypeEnum.PANEL_MACHINE.getType())
                    .build()
            );
            throw e;
        }
    }

    /**
     * 接收心跳消息
     */
    private void heartbeat(JSONObject info) {
        MQTT_HEARTBEAT_POOL.execute(() -> {
            String deviceSn = info.getStr("facesluiceId");
            // 一次心跳过期时间为发送时间60s之后，即连续两次未检测到心跳该设备才失活
            redisService.del(CachePrefixConstant.ALIVE_DEVICE + deviceSn);
            redisService.set(CachePrefixConstant.ALIVE_DEVICE + deviceSn, 1, 90L);
        });
    }

    /**
     * 设备上线
     */
    private void online(JSONObject info) {
        String deviceSn = info.getStr("facesluiceId");
        log.info("设备上线,sn:[{}]", deviceSn);
        if (redisService.hasKey(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn)) {
            log.info("上线自动释放人脸下发锁,sn:[{}]", deviceSn);
            redisService.falseUnLock(LockPrefixConstant.HQ_DEVICE_USER_EDIT_LOCK + deviceSn);
            redisService.falseUnLock(LockPrefixConstant.HQ_DEVICE_USER_DELETE_LOCK + deviceSn);
        }
        JSONObject ack = new JSONObject() {{
            set("messageId", StrUtil.uuid());
            set("operator", "Online-Ack");
            set("info", new JSONObject() {{
                set("facesluiceId", deviceSn);
                set("result", "ok");
                set("detail", "");
            }});
        }};
        mqttUtil.publish(ack.toString(), "mqtt/face/" + deviceSn);
    }

    /**
     * 接收认证消息推送方法
     *
     * @param deviceSn       设备号
     * @param pushAckType    推送类型. 1:回复接收到陌生人抓拍信息; 2:回复接收到认证记录信息
     * @param snapOrRecordId 对应记录id. PushAckType=1, 填接收到的陌生人抓拍库 ID; PushAckType=2, 填接收到的控制记录库 ID
     * @author ZYJ
     * @date 2023/10/27 11:20
     */
    @Async
    public void pushConfirmMessage(String deviceSn, int pushAckType, int snapOrRecordId) {
        try {
            log.info("开始推送消息");
            JSONObject jsonObject = JSONUtil.createObj()
                .set("messageId", StrUtil.uuid())
                .set("operator", HaiqingConstant.PUSH_MESSAGE)
                .set("info", JSONUtil.createObj()
                    .set("PushAckType", pushAckType)
                    .set("SnapOrRecordID", snapOrRecordId)
                );
            Boolean publish = mqttUtil.publish(jsonObject.toString(), "mqtt/face/" + deviceSn);
            log.info("结束推送消息: {}", publish);
        } catch (Exception e) {
            log.error("接收认证消息推送方法失败", e);
        }
    }

    /**
     * 推送人员和策略绑定信息
     *
     * @param deviceSn     设备号
     * @param userFaceList 人脸集合
     * @param faceGroupId  人脸组id
     * @author ZYJ
     * @date 2023/11/7 16:44
     */
    @Async
    public void pushStrategyMessage(String deviceSn, List<EduUserFace> userFaceList, Long faceGroupId) {
        try {
            // 人员信息
            JSONArray personsInfo = JSONUtil.createArray();

            for (EduUserFace userFace : userFaceList) {
                if (Objects.isNull(userFace)) {
                    continue;
                }
                // 设置用户信息
                JSONArray array = JSONUtil.createArray();
                // 判断是否有请假策略
                List<EduUserStrategy> list = eduUserStrategyService.list(Wrappers.lambdaQuery(new EduUserStrategy())
                    .eq(EduUserStrategy::getUserId, userFace.getUserId())
                    .eq(EduUserStrategy::getUserType, userFace.getUserType())
                    .ge(EduUserStrategy::getEndTime, LocalDateTime.now())
                );
                if (CollUtil.isNotEmpty(list)) {
                    list.forEach(strategy -> array.add(strategy.getStrategyId()));
                }
                // 判断人脸组是否有策略信息
                if (Objects.nonNull(faceGroupId)) {
                    EduFaceGroupStrategy strategy = remoteFaceGroupStrategyService.getStrategyInfo(faceGroupId).getData();
                    if (Objects.nonNull(strategy)) {
                        array.add(faceGroupId);
                    }
                }
                if (CollUtil.isNotEmpty(array)) {
                    // 设置绑定信息
                    JSONObject singleObject = JSONUtil.createObj()
                        // 用户id+用户类型
                        .set("customId", userFace.getUserId() + userFace.getUserType().toString())
                        // 策略id数组
                        .set("strategyID", array);
                    personsInfo.add(singleObject);
                }
            }
            // 推送绑定信息
            if (CollUtil.isNotEmpty(personsInfo)) {
                // 策略下发信息
                JSONObject userParams = JSONUtil.createObj();
                // 设置人员绑定策略
                userParams.set("operator", HaiqingConstant.PERSONS_BIND_STRATEGY)
                    .set("messageId", StrUtil.uuid().replace("-", ""));
                // 人员信息
                userParams.set("info", JSONUtil.createObj().set("personsInfo", personsInfo));
                log.info("推送的人员绑定策略消息=================sn: {}, params: {}", deviceSn, userParams);
                // 绑定策略消息
                mqttUtil.publish(userParams.toString(), "mqtt/face/" + deviceSn);
            }
        } catch (Exception e) {
            log.error("推送人员绑定策略消息失败, deviceSn: {}", deviceSn, e);
        }
    }
}
