package cn.com.taiji.track.handler;

import cn.com.taiji.track.config.NettyConfig;
import cn.com.taiji.track.dto.HistoryTrackQueryDTO;
import cn.com.taiji.track.event.HistoryTrackChannelAddEvent;
import cn.com.taiji.track.event.HistoryTrackChannelRemoveEvent;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.util.Objects;


/**
 * @author wudskq
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class HistoryTrackHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    Logger logger = LoggerFactory.getLogger(HistoryTrackHandler.class);

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 一旦连接,第一个被执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerAdded 被调用" + ctx.channel().id().asLongText());
        // 添加到channelGroup 通道组
        NettyConfig.getChannelGroup().add(ctx.channel());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.getChannelGroup().add(ctx.channel());
        log.info("与客户端建立连接，通道开启！信道ID为 " + ctx.channel().id());
    }
    //读就绪事件
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        HistoryTrackQueryDTO dto = JSONObject.parseObject(msg.text(), HistoryTrackQueryDTO.class);
        log.info("本次会话(信道)查询条件为：" + msg.text());
        if (!NettyConfig.getUserChannelMap().containsKey(dto)) {
            NettyConfig.getUserChannelMap().put(ctx.channel().id().asShortText(),dto.toString());
            // 将用户ID作为自定义属性加入到channel中，方便随时channel中获取用户ID
            AttributeKey<String> key = AttributeKey.valueOf("history-track");
            ctx.channel().attr(key).setIfAbsent(dto.toString());
            applicationEventPublisher.publishEvent(new HistoryTrackChannelAddEvent(this,ctx,ctx.channel().id(), dto));
            log.info("发送启动新线程event成功!");
        } else {
            // 前端定时请求,保持心跳连接,避免服务端误删通道
            ctx.channel().writeAndFlush(new TextWebSocketFrame("keep alive success！"));
        }
        // 回复消息
//        ctx.channel().writeAndFlush(new TextWebSocketFrame("此次会话CQL-ID为 "+ ctx.channel().id().asShortText() + " 查询的条件为：" + dto.toString() + "与服务器成功通信！"));
    }

    /**
     * 心跳检测相关方法 - 会主动调用handlerRemoved
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {
                //清除超时会话
                ChannelFuture writeAndFlush = ctx.writeAndFlush("you will close");
                writeAndFlush.addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        ctx.channel().close();
                    }
                });
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 移除通道及关联用户
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerRemoved 被调用" + ctx.channel().id().asLongText());
        // 删除通道
        ChannelGroup channelGroup = NettyConfig.getChannelGroup();
        if (Objects.nonNull(channelGroup)) {
            channelGroup.remove(ctx.channel());
            NettyConfig.getUserChannelMap().remove(ctx.channel().id().asShortText());
            applicationEventPublisher.publishEvent(new HistoryTrackChannelRemoveEvent(this, ctx.channel().id()));
        }
        removeUserId(ctx);
    }

    /**
     * 删除用户与channel的对应关系
     *
     * @param ctx
     */
    private void removeUserId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = ctx.channel().attr(key).get();
        if (StringUtils.isNotBlank(userId)) {
            NettyConfig.getUserChannelMap().remove(userId);
            log.info("删除用户与channel的对应关系,userId：{}", userId);
        }
    }
    /**
     * 异常处理
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("异常：{}", cause.getMessage());
        // 删除通道
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();
    }

    /**
     * 推送消息给指定信道
     *
     * @param channelId
     * @param msg
     */
    public void sendMsgToSpecifyChannel(ChannelId channelId, String msg) {
//        log.info("推送数据--{},到指定信道{}", msg, channelId);
        ChannelGroup channelGroup = NettyConfig.getChannelGroup();
        Channel channel = channelGroup.find(channelId);
        if (channel != null) {
            channel.writeAndFlush(new TextWebSocketFrame(msg));
        }
    }
}
