package com.huangzj.base.net;

import com.huangzj.base.cmd.CmdRpcChannelDispatch;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.base.cmd.CrossRpcInvoker;
import com.huangzj.base.cmd.GameCommandLookup;
import com.huangzj.base.cmd.ProtoRegistry;
import com.huangzj.base.frame.LengthPrefixedProtobuf;
import com.huangzj.base.frame.NettyBufUtil;
import com.huangzj.cmd.gen.CmdRpcEnvelope;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 长度前缀剥掉后的一帧 = 整包 {@link CmdRpcEnvelope}：查 {@link ProtoRegistry} 解码并分发。
 */
@Slf4j
@RequiredArgsConstructor
public class CmdRpcTcpFrameHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private final GameCommandLookup lookup;
    private final CrossRpcInvoker crossRpc;
    private final CommandSource linkSource;
    private final ProtoRegistry protoRegistry;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        byte[] raw = NettyBufUtil.readReadableBytes(msg);
        // 解码 + ProtoRegistry + GameCommand 执行在同一线程（默认 IO 线程），业务应避免长时间阻塞
        CmdRpcEnvelope out =
                CmdRpcChannelDispatch.dispatch(raw, lookup, ctx, crossRpc, linkSource, protoRegistry);
        ctx.writeAndFlush(LengthPrefixedProtobuf.encodeMessage(ctx.alloc(), out));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error(
                "CmdRpc TCP handler error source={} remote={}",
                linkSource,
                ctx.channel().remoteAddress(),
                cause);
        // 半包解码器之上若已错乱，继续读无意义，直接断链让客户端重连
        ctx.close();
    }
}
