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.DealRecordDTO;
import com.yida.data.attendance.mapper.EduAttendanceDeviceMapper;
import com.yida.data.attendance.service.EduAttendanceRecordService;
import com.yida.data.attendance.service.NoticeService;
import com.yida.data.attendance.utils.MqttUtil;
import com.yida.data.common.core.entity.ReceiveMsg;
import com.yida.data.common.core.entity.attendance.EduAttendanceDevice;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.constant.HaiqingConstant;
import com.yida.data.common.core.entity.constant.LockPrefixConstant;
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.utils.FileUtil;
import com.yida.data.user.dto.UpdateFaceGroupUserStatusDTO;
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.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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

    private static MqttUtil mqttUtil;
    @Resource
    private EduAttendanceRecordService eduAttendanceRecordService;
    @Resource
    private EduAttendanceDeviceMapper eduAttendanceDeviceMapper;
    @Resource
    private RemoteUserFaceService remoteUserFaceService;
    @Resource
    private RemoteUserDeviceService remoteUserDeviceService;
    @Resource
    private NoticeService noticeService;
    @Resource
    private RedisService redisService;

    private static final Snowflake SNOWFLAKE;

    @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("丢失连接");
        log.info("----------------");
    }

    /**
     * 接受消息
     */
    @Override
    public void messageArrived(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);
        }
        try {
            switch (type) {
                // 编辑单个人员
                case HaiqingConstant.EDIT_PERSON_CALL:
                    editUser(info);
                    break;
                // 批量增加与修改 处理逻辑相同
                case HaiqingConstant.ADD_PERSON_BATCH_CALL:
                    log.info("info:{}", info);
                    editUserBacth(info, 1);
                    break;
                case HaiqingConstant.UPDATE_PERSON_BATCH_CALL:
                    log.info("info:{}", info);
                    editUserBacth(info, 0);
                    break;
                // 删除单个人员
                case HaiqingConstant.DELETE_PERSON_CALL:
                    deleteUser(info);
                    break;
                // 批量删除人员
                case HaiqingConstant.DELETE_PERSON_BATCH_CALL:
                    deleteUserBatch(info);
                    break;
                // 认证信息推送
                case HaiqingConstant.REC_PERSON:
                    recPerson(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;
                default:
            }
        } catch (Exception e) {
            log.error("haiqingcallback fail", 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 info
     * @param type 0-修改，1-增加
     */
    private void editUserBacth(JSONObject info, Integer type) {
        String deviceSn = info.getStr("facesluiceId");
        EduAttendanceDevice device = eduAttendanceDeviceMapper.selectOne(
            Wrappers.lambdaQuery(new EduAttendanceDevice()).eq(EduAttendanceDevice::getSn, deviceSn)
                .eq(EduAttendanceDevice::getDeviceType, DeviceType.HAIQING.getValue()));
        Map<Object, Object> faceList = redisService.hmget(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn);
        Long faceGroupId = type == 1 ? Long
            .valueOf(redisService.hget(CachePrefixConstant.ISSUE_FACE_GROUP_DEVICE, deviceSn).toString()) : null;
        if (info.getInt("AddSucNum") != 0) {
            // 去除添加成功人员
            JSONArray addSucInfo = info.getJSONArray("AddSucInfo");
            List<EduUserDevice> userDeviceList = new ArrayList<>();
            List<EduFaceGroupUser> faceGroupUserList = new ArrayList<>();
            for (Object sucInfo : addSucInfo) {
                JSONObject sucMsg = (JSONObject) sucInfo;
                String customId = sucMsg.getStr("customId");
                // 删除redis中的缓存
                redisService.hdel(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn, customId);
                // 保存设备的下发记录
                EduUserFace userFace = (EduUserFace) faceList.get(customId);
                if (userFace != null && type == 1) {
                    EduFaceGroupUser user = new EduFaceGroupUser();
                    EduUserDevice userDevice = new EduUserDevice();
                    // 人脸与设备的关联关系
                    userDevice.setDeviceId(device.getId());
                    userDevice.setFaceGroupId(userFace.getFaceGroupUserId());
                    userDevice.setUserId(userFace.getUserId());
                    userDevice.setUserType(userFace.getUserType());
                    userDeviceList.add(userDevice);
                    // 下发成功的人脸
                    user.setUserId(userFace.getUserId());
                    user.setUserType(userFace.getUserType());
                    user.setFaceGroupId(userFace.getFaceGroupUserId());
                    faceGroupUserList.add(user);
                }
            }
            if (CollUtil.isNotEmpty(userDeviceList)) {
                remoteUserDeviceService.batchSave(userDeviceList);
            }
            if (CollUtil.isNotEmpty(faceGroupUserList)) {
                UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                dto.setUserList(faceGroupUserList);
                dto.setStatus(EduFaceGroupUser.ISSUED);
                remoteUserFaceService.updateFaceGroupUserStatus(dto);
            }
            // 添加成功人员数量
            if (type == 1) {
                redisService.incr(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId, info.getLong("AddSucNum"));
            }
        }
        if (info.getInt("AddErrNum") != 0) {
            // 下发失败修改下发状态
            JSONArray addErrInfo = info.getJSONArray("AddErrInfo");
            List<EduFaceGroupUser> faceGroupUserList = new ArrayList<>();
            long error = 0;
            long finish = 0;
            for (Object errInfo : addErrInfo) {
                JSONObject errMsg = (JSONObject) errInfo;
                String customId = errMsg.getStr("customId");
                String errCode = errMsg.getStr("errcode");
                log.error("issue face fail,msg:[{}],customId:[{}]", errCode, customId);
                if ("461".equals(errCode)) {
                    // 461为该id已存在
                    // 添加时id已存在  不进行添加
                    redisService.hdel(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn, customId);
                    finish++;
                    continue;
                }
                // 468错误码为人脸特征提取失败
                if ("468".equals(errCode)) {
                    // 人脸特征提取失败进行提醒
                    EduUserFace userFace = (EduUserFace) faceList.get(customId);
                    EduFaceGroupUser user = new EduFaceGroupUser();
                    if (userFace != null) {
                        user.setUserId(userFace.getUserId());
                        user.setUserType(userFace.getUserType());
                        user.setFaceGroupId(userFace.getFaceGroupUserId());
                        faceGroupUserList.add(user);
                        // 发送重新采集通知
                        noticeService
                            .sendCollectFailNotice(userFace.getPicPath(), userFace.getUserId(), userFace.getUserType());
                    }
                    redisService.hdel(CachePrefixConstant.HQ_EDIT_BATCH + deviceSn, customId);
                    if (type == 1) {
                        // 添加错误人脸
                        redisService.lSet(CachePrefixConstant.ISSUE_FACE_ERROR_MSG + faceGroupId, userFace);
                    }
                    error++;
                    finish++;
                }
            }
            // 添加失败人员数量
            if (type == 1) {
                redisService.incr(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId, finish);
                redisService.incr(CachePrefixConstant.ISSUE_FACE_ERROR + faceGroupId, error);
                if (CollUtil.isNotEmpty(faceGroupUserList)) {
                    // 人脸特征提取失败的用户  修改其下发状态  删除下发记录
                    UpdateFaceGroupUserStatusDTO dto = new UpdateFaceGroupUserStatusDTO();
                    dto.setUserList(faceGroupUserList);
                    dto.setStatus(EduFaceGroupUser.ISSUE_FAIL);
                    remoteUserFaceService.updateFaceGroupUserStatus(dto);
                }
            }
        }
        // 释放批量编辑锁
        redisService.falseUnLock(LockPrefixConstant.HQ_DEVICE_USER_EDIT_LOCK + deviceSn);
    }


    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(JSONObject info) {
        String userFlag = info.getStr("customId");
        if (StrUtil.isBlank(userFlag)) {
            return;
        }
        char type = userFlag.charAt(userFlag.length() - 1);
        // 获取识别机推送信息
        String base64 = info.get("pic").toString();
        Long userId = Long.valueOf(userFlag.substring(0, userFlag.length() - 1));
        String sn = info.get("facesluiceId").toString();
//        Double temp = info.get("temperature") != null ? Double.valueOf(info.get("temperature").toString()) : null;
        // 本地路径
        String localPath = FileUtil.uploadBase64Img(base64.substring(base64.indexOf(",") + 1), "png");
        // 上传到媒资
        String url = FileUtil.uploadFileToMediaServer(uploadUrl, localPath).split(",")[0];
        // 人脸区域
//        String facePath = null;
        // 上传人脸部分
//        String faceUrl = StrUtil.isNotBlank(facePath) ? FileUtil.uploadFileToMediaServer(facePath).split(",")[0] : url;
        // 删除本地文件
        cn.hutool.core.io.FileUtil.del(localPath);
//        if (facePath != null) {
//            cn.hutool.core.io.FileUtil.del(facePath);
//        }
        // 提取的考勤数据
        DealRecordDTO dealRecordDTO = new DealRecordDTO();
        dealRecordDTO.setSn(sn);
        dealRecordDTO.setDate(info.getStr("time"));
        dealRecordDTO.setUserId(userId);
        dealRecordDTO.setUserType(Integer.valueOf(Character.toString(type)));
        dealRecordDTO.setPicUrl(url);
        dealRecordDTO.setUserFlag(userFlag);
        // 保存考勤记录
        eduAttendanceRecordService.receiveRecord(dealRecordDTO);
    }

    /**
     * 接收心跳消息
     */
    private void heartbeat(JSONObject info) {
        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.pulish(ack.toString(), "mqtt/face/" + deviceSn);
    }
}
