package com.eitc.patient.netty.server.handler;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eitc.patient.domain.ChatHistory;
import com.eitc.patient.netty.process.MsgProcessor;
import com.eitc.patient.netty.protocol.IMDecoder;
import com.eitc.patient.netty.protocol.IMMessage;
import com.eitc.patient.netty.protocol.IMP;
import com.eitc.patient.service.IAppUserService;
import com.eitc.patient.service.IChatHistoryService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;










@Log4j2
@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private final IMDecoder decoder = new IMDecoder();

    private WebSocketServerHandshaker handShaker;
    private ChannelHandlerContext ctx;
    private MsgProcessor process = new MsgProcessor();

    @Autowired
    private IChatHistoryService chatHistoryService;
    @Autowired
    private IAppUserService appUserService;

    public WebSocketHandler(){

    }
    public static WebSocketHandler webSocketHandler;

    @PostConstruct
    public void init(){
        webSocketHandler = this;
        webSocketHandler.appUserService = this.appUserService;
        webSocketHandler.chatHistoryService = this.chatHistoryService;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object mas) throws Exception {
        // 传统的HTTP接入
        if (mas instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) mas);
        }
        // WebSocket接入
        else if (mas instanceof WebSocketFrame) {
            handleWebSocket(ctx, (WebSocketFrame) mas);
            //保存当前用户
            WebSocketFrame frame = (WebSocketFrame) mas;
            if (frame.toString().contains("CloseWebSocketFrame")){
                // 前端主动断开 websocket
                return;
            }
            String msg =((TextWebSocketFrame) frame).text();
            if (msg != null && msg.contains("HEART_CHECK")){
                Channel channel = ctx.channel();
                channel.writeAndFlush(new TextWebSocketFrame("心跳检测成功"));
            }else{
                //保存当前用户
                IMMessage request = decoder.decode(msg);
                if (null == request) {
                    return;
                }
                try {
                    if (Objects.equals(request.getCmd(), IMP.CHAT.getName()) || Objects.equals(request.getCmd(), IMP.CHATIMG.getName())) {
                        saveHistory(request,msg);
                    }
                    if (Objects.equals(request.getCmd(), IMP.LOGIN.getName())) {
                        updateChatStatus(request);
                    }
                } catch (Exception e) {
                    log.error("保存聊天记录失败，内容为【{}】",msg);
                }
                process.individually(ctx.channel(),request,getHistoryList(request));
            }
        }
    }

    /**
     * 保存聊天消息
     *
     * @param request
     */
    public void saveHistory(IMMessage request, String msg)  {
        ChatHistory chatHistory = new ChatHistory();
        chatHistory.setAppUserId(request.getSender());
        chatHistory.setUserId(request.getReceiver());
        chatHistory.setChatHistory(msg);
        webSocketHandler.chatHistoryService.save(chatHistory);

    }

    /**
     * 修改聊天记录状态为已读
     * @param request
     */
    public void updateChatStatus(IMMessage request)  {
        webSocketHandler.chatHistoryService.update(Wrappers.<ChatHistory>lambdaUpdate()
                .set(ChatHistory::getIsRead,1)
                .eq(ChatHistory::getAppUserId,request.getReceiver())
                .eq(ChatHistory::getUserId,request.getSender()));
    }
    /**
     * 查询历史记录
     * @param request
     */
    public List<String> getHistoryList(IMMessage request)  {
        List<ChatHistory> list = webSocketHandler.chatHistoryService.list(Wrappers.<ChatHistory>lambdaQuery()
                .in(ChatHistory::getAppUserId, Arrays.asList(request.getReceiver(), request.getSender()))
                .in(ChatHistory::getUserId, Arrays.asList(request.getReceiver(), request.getSender()))
                .orderByAsc(ChatHistory::getCreateDate)
        );
        return list.stream().map(ChatHistory::getChatHistory).collect(Collectors.toList());
    }

    /**
     * 处理Http请求，完成WebSocket握手
     * 注意：WebSocket连接第一次请求使用的是Http
     * @param ctx
     * @param request
     * @throws Exception
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        // 如果HTTP解码失败，返回HHTP异常
        if (!request.getDecoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        // 正常WebSocket的Http连接请求，构造握手响应返回
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + request.headers().get(HttpHeaders.Names.HOST), null, false);
        handShaker = wsFactory.newHandshaker(request);
        if (handShaker == null) { // 无法处理的websocket版本
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else { // 向客户端发送websocket握手,完成握手
            handShaker.handshake(ctx.channel(), request);
            // 记录管道处理上下文，便于服务器推送数据到客户端
            this.ctx = ctx;
        }
    }

    /**
     * Http返回
     * @param ctx
     * @param request
     * @param response
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
        // 返回应答给客户端
        if (response.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            HttpHeaders.setContentLength(response, response.content().readableBytes());
        }

        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(response);
        if (!HttpHeaders.isKeepAlive(request) || response.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }



    /**
     * 处理Socket请求
     * @param ctx
     * @param frame
     * @throws Exception
     */
   public void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame frame){
        // 判断是否是关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            handShaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        // 判断是否是Ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 当前只支持文本消息，不支持二进制消息
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException("当前只支持文本消息，不支持二进制消息");
        }
        String msg =((TextWebSocketFrame)frame).text();
        // 处理来自客户端的WebSocket请求

    }







    /**
     * 退出
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        process.logout(ctx.channel());
    }
}
