package com.seer.teach.netty.im.handler.im.business;


import com.seer.teach.common.cache.DeviceMetricsCacheService;
import com.seer.teach.common.cache.dto.DeviceMetricDTO;
import com.seer.teach.common.utils.HostIpResolver;
import com.seer.teach.iot.api.DeviceInfoServiceApi;
import com.seer.teach.iot.api.resp.DeviceRespDTO;
import com.seer.teach.netty.common.annotation.ImMessageType;
import com.seer.teach.netty.common.proto.MessageProtobuf;
import com.seer.teach.netty.im.handler.im.ImMsgHandler;
import com.seer.teach.netty.im.utils.ImUtils;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: Captain
 * @Description: Im协议中心跳消息处理
 * @Date: 2025-05-20 18:36
 */

@Service
@Slf4j
@Component
@ImMessageType(1002)
@RequiredArgsConstructor
@ChannelHandler.Sharable
public class ImHeartBeatHandler extends ChannelInboundHandlerAdapter implements ImMsgHandler {

    private final DeviceMetricsCacheService deviceMetricsCacheService;

    private final DeviceInfoServiceApi deviceInfoServiceApi;

    private final Map<String, Long> lastUpdateTimeMap = new ConcurrentHashMap<>();

    @Override
    public void handle(ChannelHandlerContext ctx, MessageProtobuf.Msg msg) {
        MessageProtobuf.Msg.Builder pushMessageBuilder = MessageProtobuf.Msg.newBuilder();
        MessageProtobuf.Head.Builder pushMessageHeadBuilder = MessageProtobuf.Head.newBuilder();
        pushMessageHeadBuilder.setMsgId(msg.getHead().getMsgId());
        pushMessageHeadBuilder.setMsgType(msg.getHead().getMsgType());
        pushMessageHeadBuilder.setTimestamp(msg.getHead().getTimestamp());
        pushMessageHeadBuilder.setStatusReport(msg.getHead().getStatusReport());
        pushMessageHeadBuilder.setDevice(msg.getHead().getDevice());
        pushMessageBuilder.setHead(pushMessageHeadBuilder);
        // 发送心跳响应
        ImUtils.sendToTvByDevice(pushMessageBuilder);
        ctx.executor().execute(() -> {
            try {
                checkRedisConnect(msg.getHead());
            } catch (Exception e) {
                log.error("异步处理心跳业务异常，device={}", msg.getHead().getDevice(), e);
            }
        });
    }

    private void checkRedisConnect(MessageProtobuf.Head head) {
        String device = head.getDevice();
        // 获取最新指标
        DeviceMetricDTO latestMetric = deviceMetricsCacheService.getLatestMetric(device);
        double cpuUsage = ImUtils.parseCpuUsage(head.getCpuUsage());
        double memoryUsage = ImUtils.parseMemoryUsage(head.getMemoryUsage());
        long lastUpdateTime = lastUpdateTimeMap.getOrDefault(device, 0L);
        long now = System.currentTimeMillis();
        // 10分钟
        boolean timeExceeded = now - lastUpdateTime >= 10 * 60 * 1000;
        boolean needUpdate = false;
        // 当前值比存储值高 20% 以上
        if (cpuUsage > latestMetric.getCpu() * 1.2 || memoryUsage > latestMetric.getMemory() * 1.2) {
            needUpdate = true;
        }
        // 如果时间到或者需要更新
        if (timeExceeded || needUpdate) {
            log.info("设备[{}]更新Redis缓存数据", device);

            DeviceRespDTO deviceInfo = deviceInfoServiceApi.getDevice(device);
            String hostIp = HostIpResolver.getHostIp();

            DeviceMetricDTO deviceMetricDTO = DeviceMetricDTO.get(
                    deviceInfo.getCityId(),
                    deviceInfo.getIp(),
                    hostIp,
                    cpuUsage,
                    memoryUsage
            );

            deviceMetricsCacheService.reportMetric(device, deviceMetricDTO);

            // 更新最后更新时间
            lastUpdateTimeMap.put(device, now);
        }
    }


    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("心跳处理异常，channel={}", ctx.channel().id(), cause);
        ctx.close();
    }

}
