package com.seer.teach.netty.im.server;

import com.seer.teach.netty.common.proto.MessageProtobuf;
import com.seer.teach.netty.im.handler.im.business.ImHeartBeatHandler;
import com.seer.teach.netty.im.handler.im.business.TvImBusinessHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * TV 端 IM 服务
 *
 * @Author: Captain
 * @Date: 2025-05-20 16:49
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class TvImNettyServer implements SmartLifecycle, DisposableBean {

    /**
     * 单帧最大长度（2 字节长度字段 -> 0~65535）
     */
    private static final int MAX_FRAME_LENGTH = 1024 * 1024 * 1024;

    /**
     * 长度字段起始偏移
     */
    private static final int LENGTH_FIELD_OFFSET = 0;

    /**
     * 长度字段长度（2 字节）
     */
    private static final int LENGTH_FIELD_LENGTH = 4;

    /**
     * 长度矫正（一般为 0）
     */
    private static final int LENGTH_ADJUSTMENT = 0;

    /**
     * 解码时剥离的字节数（去掉前置长度字段）
     */
    private static final int INITIAL_BYTES_TO_STRIP = 4;

    @Value("${im.port}")
    private Integer port;

    /**
     * 服务器监听队列长度（防高并发丢连接）
     */
    @Value("${im.tcp.backlog:1024}")
    private int soBacklog;

    /**
     * 是否开启 TCP KeepAlive（应用层有心跳时可留作兜底）
     */
    @Value("${im.tcp.keepalive:true}")
    private boolean soKeepAlive;

    /**
     * 是否关闭 Nagle（低延迟：IM 强烈建议 true）
     */
    @Value("${im.tcp.nodelay:true}")
    private boolean tcpNoDelay;

    /**
     * 写水位线（低/高），用于背压与防止 OOM
     */
    @Value("${im.tcp.writeBufferLowWaterMark:32768}")
    private int writeBufferLowWaterMark;

    @Value("${im.tcp.writeBufferHighWaterMark:65536}")
    private int writeBufferHighWaterMark;

    /**
     * worker 线程数（默认 0 表示 Netty 自适应）
     */
    @Value("${im.worker.threads:0}")
    private int workerThreads;

    private final TvImBusinessHandler tvImBusinessHandler;

    private final ImHeartBeatHandler imHeartBeatHandler;

    private EventLoopGroup bossGroup;

    private EventLoopGroup workerGroup;

    private Class<? extends ServerSocketChannel> serverChannelClass;

    private Channel serverChannel;

    private final AtomicBoolean running = new AtomicBoolean(false);

    /**
     * 启动 Netty IM 服务器
     * <p>
     * 初始化并启动 Netty 服务端，绑定指定端口，配置网络参数、编解码器及业务处理器。
     * 若服务已运行，则忽略重复启动请求。
     * </p>
     */
    @Override
    public void start() {
        if (!running.compareAndSet(false, true)) {
            log.warn("TvImNettyServer 已在运行，忽略重复启动");
            return;
        }
        selectTransport();

        try {
            final ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(serverChannelClass)
                    // 服务器级别配置
                    .option(ChannelOption.SO_BACKLOG, soBacklog)
                    .option(ChannelOption.SO_RCVBUF, 1024 * 1024)
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 子通道（客户端）配置
                    .childOption(ChannelOption.TCP_NODELAY, tcpNoDelay)
                    .childOption(ChannelOption.SO_KEEPALIVE, soKeepAlive)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator())
                    .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                            new WriteBufferWaterMark(writeBufferLowWaterMark, writeBufferHighWaterMark))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            final ChannelPipeline p = ch.pipeline();

                            // 读空闲检测（业务心跳超时控制）
                            p.addLast(imHeartBeatHandler);
                            // 帧解码器：基于长度字段拆包
                            p.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(
                                    MAX_FRAME_LENGTH,
                                    LENGTH_FIELD_OFFSET,
                                    LENGTH_FIELD_LENGTH,
                                    LENGTH_ADJUSTMENT,
                                    INITIAL_BYTES_TO_STRIP));

                            // Protobuf 解码器
                            p.addLast("pbDecoder", new ProtobufDecoder(MessageProtobuf.Msg.getDefaultInstance()));

                            // 帧编码器：添加长度字段
                            p.addLast("frameEncoder", new LengthFieldPrepender(LENGTH_FIELD_LENGTH));

                            // Protobuf 编码器
                            p.addLast("pbEncoder", new ProtobufEncoder());

                            // 背压控制处理器
                            p.addLast("backpressure", new BackpressureHandler());

                            // 业务逻辑处理器
                            p.addLast("biz", tvImBusinessHandler);

                            // 异常捕获处理器
                            p.addLast("exception", new ChannelInboundHandlerAdapter() {
                                @Override
                                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
                                    log.warn("IM Channel 异常，准备关闭。remote={}, err={}",
                                            remote(ctx), cause.toString(), cause);
                                    ctx.close();
                                }
                            });
                        }
                    });

            final ChannelFuture bind = bootstrap.bind(new InetSocketAddress(port)).syncUninterruptibly();
            serverChannel = bind.channel();
            log.info("Netty IM 服务器启动成功，端口 {}", port);

            serverChannel.closeFuture().addListener(f -> stopInternal());
        } catch (Throwable t) {
            log.error("TvImNettyServer 启动失败：{}", t.toString(), t);
            stopInternal();
            running.set(false);
            throw new IllegalStateException("TvImNettyServer 启动失败", t);
        }
    }

    /**
     * 停止 Netty IM 服务器
     * <p>
     * 关闭服务端监听通道，并优雅关闭 boss 和 worker 线程组。
     * </p>
     */
    @Override
    public void stop() {
        stopInternal();
        running.set(false);
        log.info("TvImNettyServer 已停止");
    }

    /**
     * 检查服务是否正在运行
     *
     * @return true 表示服务正在运行，false 表示未运行
     */
    @Override
    public boolean isRunning() {
        return running.get();
    }

    /**
     * 获取生命周期阶段值
     * <p>
     * 返回最小整数值，确保该服务尽可能早地启动，尽可能晚地关闭。
     * </p>
     *
     * @return 生命周期阶段值
     */
    @Override
    public int getPhase() {
        return Integer.MIN_VALUE;
    }

    /**
     * 是否自动启动
     * <p>
     * 返回 true 表示容器启动时会自动调用 start() 方法。
     * </p>
     *
     * @return true 表示自动启动
     */
    @Override
    public boolean isAutoStartup() {
        return true;
    }

    /**
     * 停止服务并执行回调
     * <p>
     * 先调用 stop() 方法停止服务，然后执行传入的回调函数。
     * </p>
     *
     * @param callback 回调函数，在服务停止后执行
     */
    @Override
    public void stop(Runnable callback) {
        try {
            stop();
        } finally {
            callback.run();
        }
    }

    /**
     * 销毁 Bean 时调用的方法
     * <p>
     * 实现 DisposableBean 接口，用于在容器销毁 Bean 时关闭服务。
     * </p>
     */
    @Override
    public void destroy() {
        stop();
    }

    /**
     * 选择传输类型（Epoll 或 NIO）
     * <p>
     * 根据操作系统是否支持 Epoll 来决定使用 EpollEventLoopGroup 还是 NioEventLoopGroup。
     * </p>
     */
    private void selectTransport() {
        if (bossGroup != null || workerGroup != null) return;

        final DefaultThreadFactory bossFactory = new DefaultThreadFactory("im-boss", true);
        final DefaultThreadFactory workerFactory = new DefaultThreadFactory("im-worker", true);

        if (Epoll.isAvailable()) {
            bossGroup = new EpollEventLoopGroup(1, bossFactory);
            workerGroup = new EpollEventLoopGroup(Math.max(0, workerThreads), workerFactory);
            serverChannelClass = EpollServerSocketChannel.class;
            log.info("IM 使用 Epoll 事件循环（Linux 优化）");
        } else {
            bossGroup = new NioEventLoopGroup(1, bossFactory);
            workerGroup = new NioEventLoopGroup(Math.max(0, workerThreads), workerFactory);
            serverChannelClass = NioServerSocketChannel.class;
            log.info("IM 使用 NIO 事件循环");
        }
    }

    /**
     * 内部停止方法
     * <p>
     * 关闭服务端通道和事件循环组，释放资源。
     * </p>
     */
    private void stopInternal() {
        if (serverChannel != null) {
            try {
                serverChannel.close().syncUninterruptibly();
            } catch (Exception ignored) {
            }
            serverChannel = null;
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS).syncUninterruptibly();
            bossGroup = null;
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS).syncUninterruptibly();
            workerGroup = null;
        }
    }

    /**
     * 获取远程地址字符串
     *
     * @param ctx ChannelHandlerContext 上下文
     * @return 远程地址字符串，若为空则返回 "unknown"
     */
    private static String remote(ChannelHandlerContext ctx) {
        Channel ch = ctx.channel();
        return ch.remoteAddress() != null ? ch.remoteAddress().toString() : "unknown";
    }

    /**
     * 背压控制处理器
     * <p>
     * 监听通道可写状态变化，动态控制自动读取开关以实现背压控制。
     * </p>
     */
    @ChannelHandler.Sharable
    static class BackpressureHandler extends ChannelDuplexHandler {
        @Override
        public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
            boolean writable = ctx.channel().isWritable();
            ctx.channel().config().setAutoRead(writable);
            if (!writable) {
                if (log.isDebugEnabled()) {
                    log.debug("[Backpressure] pause autoRead: {}", remote(ctx));
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("[Backpressure] resume autoRead: {}", remote(ctx));
                }
            }
            super.channelWritabilityChanged(ctx);
        }
    }
}
