package com.docus.server.common.netty.server.handler;

import com.docus.core.util.DateUtil;
import com.docus.core.util.Func;
import com.docus.core.util.json.JSON;
import com.docus.server.api.scheduling.management.SchCollectRecordApi;
import com.docus.server.common.CommMsg;
import com.docus.server.common.netty.server.CollectorChannelCacheMap;
import com.docus.server.common.netty.state.DeviceStateContext;
import com.docus.server.dto.scheduling.management.schcollectrecord.EditSchCollectRecordDTO;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.util.Date;

import static com.docus.server.common.MsgConstants.*;

/**
 * 业务消息处理
 */
@Slf4j
@ChannelHandler.Sharable
@Component
public class NettyBusinessHandler extends SimpleChannelInboundHandler<CommMsg<Serializable>> {

    private static final ChannelGroup DEFAULT_CHANNEL_GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Resource
    private CollectorChannelCacheMap collectorChannelCacheMap;
    @Resource
    private SchCollectRecordApi schCollectRecordApi;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, CommMsg commMsg) throws Exception {
        DeviceStateContext deviceStateContext = collectorChannelCacheMap.getAttributeValue(ctx);

        String messageType = commMsg.getMessageType();
        String messageTime = commMsg.getMessageTime();
        String messageContent = (String) commMsg.getContent();

        log.info("======== 【终端服务端-收到采集器消息】, 消息时间={}, 消息类型={}, 消息内容={}", messageTime, messageType, messageContent + " ======== ");

        switch (messageType) {
            case ONLINE_REGISTER:
                onlineRegister(deviceStateContext, ctx, messageContent);
                break;
            case HEARTBEAT_REQUEST:
                heartbeatRequest(deviceStateContext, ctx, messageContent);
                break;
            case TERMINATOR_RESTART:
                terminatorRestart(messageContent);
                break;
            case COLLECTOR_RESTART:
                collectorRestart(messageContent);
                break;
            case VIRTUAL_RESTART:
                virtualRestart(messageContent);
                break;
            case UPDATE_COLLECTOR_FILE:
                updateCollectorFile(messageContent);
                break;
            case UPDATE_COLLECTOR_CONFIG:
                updateCollectorConfig(messageContent);
                break;
            case SCH_DISTRIBUTE_TASKS:
                schDistributeTasks(commMsg, messageContent);
                break;
            case REV_COLLECTOR_TASK:
                revCollectorTask(messageContent);
                break;
            default:
                break;
        }
    }

    private void revCollectorTask(String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到采集器上报的任务状态，内容{}", messageContent);
        EditSchCollectRecordDTO editSchCollectRecordDTO = JSON.fromJSON(messageContent, EditSchCollectRecordDTO.class);
        schCollectRecordApi.edit(editSchCollectRecordDTO);
    }

    private void schDistributeTasks(CommMsg commMsg, String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到采集调度器下发任务命令，内容{}", messageContent);
    }

    private void updateCollectorConfig(String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到更新采集器配置命令，内容{}", messageContent);
    }

    private void updateCollectorFile(String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到更新采集器文件命令，内容{}", messageContent);
    }

    private void virtualRestart(String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到虚拟机重启命令，内容{}", messageContent);
    }

    private void collectorRestart(String messageContent) {
        log.info("【终端服务端-接收采集器消息】收到采集器重启命令，内容{}", messageContent);
    }

    private void terminatorRestart(String messageContent) {
        log.info("【终端服务端-接收采集器消息】接受到终端重启命令，内容{}", messageContent);
    }

    private void heartbeatRequest(DeviceStateContext deviceStateContext, ChannelHandlerContext ctx, String messageContent) {
        //============状态为上行数据=============
        deviceStateContext.onHeartbeat(System.currentTimeMillis(), "采集器上行了数据");

        //============返回消息==================
        CommMsg<Serializable> resp = CommMsg.builder()
            .messageTime(DateUtil.formatDateTime(new Date()))
            .content("HEARTBEAT_REQUEST ok 【终端服务器】收到【采集器客户端】心跳")
            .messageType("response HEARTBEAT_REQUEST")
            .build();
        ctx.writeAndFlush(resp);


        log.info("接受到【终端服务端-接收采集器消息】客户端的心跳消息：消息内容={}", messageContent);

        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();

        log.info("【终端服务端-接收采集器消息】:{}，连接上线,IP地址信息:{}", clientIp, clientIp);

    }

    private void onlineRegister(DeviceStateContext deviceStateContext, ChannelHandlerContext ctx, String messageContent) {
        //===========login ok,切换到已登录状态===============messageContent=collectorId=========
        deviceStateContext.onLoginSucc(messageContent, System.currentTimeMillis(), 10, "采集器认证通过");

        CommMsg<Serializable> authMsg = CommMsg.builder()
            .messageTime(DateUtil.formatDateTime(new Date()))
            .content("采集器认证通过")
            .messageType("response")
            .build();
        ctx.writeAndFlush(authMsg);


        Channel channel = collectorChannelCacheMap.get(messageContent);
        if (channel != null && channel.isOpen()) {
            channel.close();
        }
        if (Func.isNotBlank(messageContent)) {
            collectorChannelCacheMap.put(messageContent, ctx.channel());
        }
    }

    /**
     * 空闲一段时间，就进行检查 (当前时间-上次上行数据的时间) 如果大于设定的超时时间 设备状态就就行一次 onTimeout
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println(getClass().getSimpleName() + "." + "userEventTriggered" + ctx.channel().remoteAddress());
        if (evt instanceof IdleStateEvent) {

            DeviceStateContext deviceStateContext = collectorChannelCacheMap.getAttributeValue(ctx);

            long lastUpdateTime = deviceStateContext.getLastUpdateTime();

            long currentTimeMillis = System.currentTimeMillis();

            long intervalTime = currentTimeMillis - lastUpdateTime;

            if (intervalTime > deviceStateContext.getHeartRate()) {
                //==============发生超时，进入超时状态==============
                deviceStateContext.onTimeout("采集器发生超时");
                System.out.println("userEventTriggered:" + deviceStateContext.toString());
            }
        } else {
            //不是超时事件，进行传递
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * netty client 上线
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelRegistered();

        //链接成功
        DeviceStateContext deviceStateContext = new DeviceStateContext(ctx.channel(), false);
        //===========设置设备状态为 未登录=================
        deviceStateContext.onConnect(System.currentTimeMillis(), "设备 active");
        //更新添加 state 属性
        collectorChannelCacheMap.setAttribute(ctx, deviceStateContext);
        System.out.println("channelActive:" + deviceStateContext.toString());
        //===========设置设备状态为 未登录=================

        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        log.info("【终端服务端-采集器IP连接成功】:{}，连接上线,IP地址信息:{}", clientIp, clientIp);

        DEFAULT_CHANNEL_GROUP.add(ctx.channel());

        System.out.println(ctx.channel().remoteAddress() + " 上线," + "【终端-采集器】在线数量：" + DEFAULT_CHANNEL_GROUP.size());
    }

    /**
     * netty client 下线
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();

        String collectorId = collectorChannelCacheMap.getClientKey(channel);

        log.error("客户端下线，采集器id：{}", collectorId);

        //移除终端，终端离线
        if (collectorId != null) {
            collectorChannelCacheMap.remove(collectorId);
        }

        System.out.println(channel.remoteAddress() + " 下线," + "【终端-采集器】在线数量：" + DEFAULT_CHANNEL_GROUP.size());

        //================设置为断开================
        DeviceStateContext deviceStateContext = collectorChannelCacheMap.getAttributeValue(ctx);
        deviceStateContext.onDisconnect("设备 inactive");
        System.out.println("channelInactive:" + deviceStateContext.toString());
    }

    /**
     * netty exception 通道异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //==============发生异常切换到断开模式===============
        System.out.println("exceptionCaught:" + cause.getMessage());
        DeviceStateContext deviceStateContext = collectorChannelCacheMap.getAttributeValue(ctx);
        deviceStateContext.onDisconnect(cause.getMessage());
        System.out.println("exceptionCaught:" + deviceStateContext.toString());


        cause.printStackTrace();
        String collectorId = collectorChannelCacheMap.getClientKey(ctx.channel());
        log.error("通道发生异常，终端连接：{}", collectorId);
        //移除终端，终端离线
        if (collectorId != null) {
            collectorChannelCacheMap.remove(collectorId);
        }
        if (ctx.channel().isActive()) {
            ctx.close();
        }
        super.exceptionCaught(ctx, cause);
    }

}
