package com.wecloud.im.netty.core;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.wecloud.im.ws.model.WsConstants;
import com.wecloud.im.ws.receive.ReadWsData;
import com.wecloud.im.ws.service.MangerChannelService;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description app端 长连接事件处理
 * @Author hewei hwei1233@163.com
 * @Date 2019-07-26
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Resource
    private ReadWsData readWsData;

    @Resource
    private MangerChannelService mangerChannelService;

    private final static ThreadFactory NAMED_THREAD_FACTORY = new ThreadFactoryBuilder()
            .setNamePrefix("WS-business-").build();
    /**
     * 耗时核心业务处理线程池
     * 属于io密集型业务
     * io密集型任务配置尽可能多的线程数量
     */
    private final static ExecutorService TASK_THREAD_POOL_EXECUTOR =
            new ThreadPoolExecutor(WsConstants.CPU_PROCESSORS * 5, WsConstants.CPU_PROCESSORS * 50,
                    3L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>(1), NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        String data = msg.text();
        try {
            if (data.isEmpty()) {
                return;
            }
            /*
             * 在此进入耗时业务线程池，  将不再阻塞netty的I/O线程，提高网络吞吐
             */
            TASK_THREAD_POOL_EXECUTOR.execute(() ->
                    execute(ctx, data)
            );

        } catch (Exception e) {
            //返回错误
            ctx.channel().writeAndFlush(new TextWebSocketFrame("error=" + e.toString() + ",data=" + data));
            log.error(e.getMessage() + data, e);
        }
    }

    private void execute(ChannelHandlerContext ctx, String data) {
        String appKey = ctx.channel().attr(MangerChannelService.APP_KEY).get();
        String clientId = ctx.channel().attr(MangerChannelService.CLIENT_ID).get();
        try {
            readWsData.convertModel(data, ctx, appKey, clientId);
        } catch (Exception e) {
            log.error("系统繁忙data:" + data + ",appKey:" + appKey + ",clientId:" + clientId +
                    ",channelId:" + ctx.channel().id().asShortText(), e);
        }

    }


//    /**
//     * 检测到异常
//     *
//     * @param ctx
//     * @param cause
//     * @throws Exception
//     */
//    @Override
//    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
//        String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
//        log.info("uid:" + userIdByChannel + ",ws异常,channelId:" + ctx.channel().id().asShortText(), cause);
//    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
        log.info("连接WS成功handlerAdded,uid:" + userIdByChannel + "，" + ",channelId:" + ctx.channel().id().asShortText());

    }

//    /**
//     * 客户端不活跃
//     *
//     * @param ctx
//     * @throws Exception
//     */
//    @Override
//    public void channelInactive(ChannelHandlerContext ctx) {
//        String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
//        log.info("uid:" + userIdByChannel + "," + "channelInactive" + ",channelId:" + ctx.channel().id().asShortText());
//    }

    /**
     * 移除时触发， 不活跃的情况下会移除，会再次触发该事件
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
        String asLongText = ctx.channel().id().asLongText();
        log.info("uid:" + userIdByChannel + "," + "handlerRemoved-begin" + ",channelId:" + asLongText);
        // 关掉连接
        ChannelFuture close = ctx.close();
        close.addListener(
                //执行后回调的方法
                (ChannelFutureListener) channelFuture1 -> {
                    log.info("uid:" + userIdByChannel + "," + "handlerRemoved-success" + ",channelId:" + asLongText);
                });
    }
}

