package net.northking.cctp.device.bus.receiver;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.device.bus.BusConstants;
import net.northking.cctp.device.bus.receiver.dto.CtrlReceiverDto;
import net.northking.cctp.device.bus.receiver.dto.HeartMQDto;
import net.northking.cctp.device.constants.DeviceConstants;
import net.northking.cctp.device.constants.DeviceError;
import net.northking.cctp.device.db.entity.CdDeviceCtrl;
import net.northking.cctp.device.db.entity.CdDeviceStatus;
import net.northking.cctp.device.db.entity.CdDeviceToken;
import net.northking.cctp.device.db.entity.CdEngineInfo;
import net.northking.cctp.device.db.service.*;
import net.northking.cctp.device.util.DeviceStatusUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.locks.Lock;

@Component
public class HeartMQReceiver {

    private final static Logger logger = LoggerFactory.getLogger(HeartMQReceiver.class);

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedisLockRegistry lockRegistry;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private CdDeviceStatusService cdDeviceStatusService;

    @Autowired
    private CdEngineInfoService cdEngineInfoService;

    @Autowired
    private CdDeviceCtrlService cdDeviceCtrlService;

    @Autowired
    private CdMobileDeviceService cdMobileDeviceService;

    @Autowired
    private CdDeviceTokenService cdDeviceTokenService;

    @Autowired
    private DeviceStatusUtil statusUtil;

    //接收上位机心跳
    @RabbitListener(queuesToDeclare = {@Queue(value = "mq.upper.computer.heart.queue")})
    public void deviceHeartReceiver(Message message) {
        CtrlReceiverDto ctrlReceiverDto = new CtrlReceiverDto();
        MessageProperties mp = message.getMessageProperties();
        Map<String, Object> headers = mp.getHeaders();
        Lock lock = lockRegistry.obtain(BusConstants.Lock_Heart + ctrlReceiverDto.getId());
        try {
            byte[] body = message.getBody();
            ctrlReceiverDto = JSON.parseObject(body, CtrlReceiverDto.class);
            if (lock.tryLock()) {
                //1、更新上位机状态
                logger.info("上位机心跳【{}】-------------------------------start", ctrlReceiverDto.getId());
                CdDeviceCtrl ctrl = new CdDeviceCtrl();
                ctrl.setId(ctrlReceiverDto.getId());
                ctrl.setCtrlStatus(ctrlReceiverDto.getStatus());
                this.cdDeviceCtrlService.updateByPrimaryKey(ctrl);
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String lastHeart = sdf.format(date);
                redisTemplate.opsForValue().set("UITEST-ctrl:heart:" + ctrl.getId(), lastHeart);
                //deviceId是否为空
                if (ctrlReceiverDto.getDeviceIds() != null && ctrlReceiverDto.getDeviceIds().size() > 0) {
                    this.cdDeviceStatusService.updateHeartbeatTime(ctrlReceiverDto.getDeviceIds());
                    this.cdMobileDeviceService.updateCtrlByDevice(ctrlReceiverDto.getDeviceIds(), ctrlReceiverDto.getId());
                    for (String deviceId : ctrlReceiverDto.getDeviceIds()) {
                        //遍历更新缓存中stf信息
                        redisTemplate.opsForValue().set("UITEST-stfAddress:device:" + deviceId, ctrlReceiverDto.getStfAddress());
                    }
                }
                logger.info("上位机心跳【{}】-------------------------------end", ctrlReceiverDto.getId());
            }
        } catch (Exception e) {
            logger.error("接收队列异常：" + e);
            throw new PlatformRuntimeException(DeviceError.UPDATE_HEART_ERROR);
        } finally {
            lock.unlock();
        }
    }


    @RabbitListener(queuesToDeclare = {@Queue(value = "engine.heartbeat")})
    public void engineHeartReceiver(Message message) {
        MessageProperties mp = message.getMessageProperties();
        Map<String, Object> headers = mp.getHeaders();
        try {
            HeartMQDto dto = objectMapper.readValue(message.getBody(), new TypeReference<HeartMQDto>() {
            });
            CdEngineInfo engineInfo = this.cdEngineInfoService.findByPrimaryKey(dto.getEngineId());
            if (engineInfo!=null){
                //更新引擎心跳
                Date date = new Date();
                engineInfo.setEngineStatus("1");
                engineInfo.setLastHeartbeatTime(date);
                engineInfo.setRunningRelease(dto.getEngineVersion());
                this.cdEngineInfoService.updateByPrimaryKey(engineInfo);

                if (engineInfo.getEnabled()) {
                    // 更新缓存中引擎可执行线程数量
                    Map<String, Object> executePoolInfo = dto.getExecutePoolInfo();
                    if (executePoolInfo != null) {
                        boolean enableMobile = executePoolInfo.get("enableMobile") != null
                                && (boolean) executePoolInfo.get("enableMobile");
                        if (enableMobile) {
                            Object mobileThread = executePoolInfo.get("mobileThread");
                            Object mobileThreadActive = executePoolInfo.get("mobile_thread_active");
                            if (mobileThread != null && mobileThreadActive != null) {
                                BigDecimal mobileThreadBd = new BigDecimal(String.valueOf(mobileThread));
                                BigDecimal mobileThreadActiveBd = new BigDecimal(String.valueOf(mobileThreadActive));
                                if (mobileThreadBd.compareTo(new BigDecimal("0")) > 0) {
                                    BigDecimal divide = mobileThreadActiveBd.divide(mobileThreadBd, 2, RoundingMode.HALF_UP)
                                            .multiply(new BigDecimal("100"));
                                    redisTemplate.opsForZSet().add(DeviceConstants.ENGINE_MOB_ACTIVE_THREAD_KEY, engineInfo.getId(),
                                            divide.doubleValue());
                                }
                            }
                        }
                    }
                }

                //更新对应的PC设备的状态信息
                CdDeviceStatus status = new CdDeviceStatus();
                status.setId(engineInfo.getDeviceId());
                //重启时，设备设置为不可用
                if (!StringUtils.isEmpty(dto.getEngineStatus())){
                    status.setUsageStatus(dto.getEngineStatus());
                }else {
                    CdDeviceToken token = cdDeviceTokenService.findByPrimaryKey(engineInfo.getDeviceId());
                    if (ObjectUtils.isEmpty(token)) {
                        status.setUsageStatus("1");
                    }else {
                        status.setUsageStatus("2");
                    }
                }
                status.setDeviceStatus("1");
                status.setLastHeartbeatTime(date);
                statusUtil.updatePcStatus(status);
            }
        } catch (IOException e) {
            logger.error("接收队列异常：",e);
        }
    }

}
