package com.market.manager;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.service.SpotDataProcessService;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 现货 Binance WebSocket 客户端（Netty 实现）
 * <p>
 * 参照合约 BinanceWebSocketClient，使用 Netty NIO 建立 WebSocket 连接。
 * 连接断开后由 BinanceSpotWebSocketManager 的定时任务检测并重连。
 *
 * @author system
 * @date 2026-05-10
 */
public class BinanceSpotWebSocketClient {

    private static final Logger log = LoggerFactory.getLogger(BinanceSpotWebSocketClient.class);
    private static final String COMBINED_BASE_URL = "wss://stream.binance.com:443/stream?streams=";

    /** 现货行情处理服务 */
    private final SpotDataProcessService dataProcessService;
    private final ObjectMapper objectMapper = new ObjectMapper();

    private NioEventLoopGroup group;
    private Channel channel;

    public BinanceSpotWebSocketClient(SpotDataProcessService dataProcessService) {
        this.dataProcessService = dataProcessService;
    }

    /**
     * 连接合并流
     *
     * @param streams 流列表（如 btcusdt@kline_1m）
     */
    public void connect(List<String> streams) {
        if (streams == null || streams.isEmpty()) {
            log.warn("现货 stream 列表为空，跳过连接");
            return;
        }
        String url = COMBINED_BASE_URL + String.join("/", streams);
        connectInternal(url, streams.size());
    }

    /**
     * 判断当前连接是否存活
     *
     * @return true 表示连接存活
     */
    public boolean isConnected() {
        return channel != null && channel.isActive();
    }

    /**
     * 断开连接并释放资源
     */
    public void disconnect() {
        if (channel != null) {
            try {
                channel.close().sync();
            } catch (Exception ignored) {
            }
            channel = null;
        }
        if (group != null) {
            group.shutdownGracefully();
            group = null;
        }
    }

    // ------------------------------------------------------------------ private

    /** 建立 Netty WebSocket 连接 */
    private void connectInternal(String url, int streamCount) {
        // 先断开已有连接
        disconnect();
        try {
            URI uri = URI.create(url);
            String host = uri.getHost();
            int port = 443;

            SslContext sslContext = SslContextBuilder.forClient().build();

            WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
                    uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), 10 * 1024 * 1024);

            BinanceSpotWebSocketHandler handler = new BinanceSpotWebSocketHandler(
                    handshaker, objectMapper, dataProcessService);

            group = new NioEventLoopGroup();
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 1. SSL
                            pipeline.addLast(sslContext.newHandler(ch.alloc(), host, port));
                            // 2. HTTP 编解码（握手阶段使用）
                            pipeline.addLast(new HttpClientCodec());
                            // 3. HTTP 聚合（握手完成后由 handler 移除）
                            pipeline.addLast(new HttpObjectAggregator(10 * 1024 * 1024));
                            // 4. 30s 无读触发 IdleStateEvent，handler 中关闭 channel 触发重连
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                            // 5. 业务 handler
                            pipeline.addLast(handler);
                        }
                    });

            channel = bootstrap.connect(host, port).sync().channel();
            handler.handshakeFuture().sync();
            log.info("现货 WebSocket 已连接: streams={}", streamCount);
        } catch (Exception e) {
            log.error("现货 WebSocket 连接失败: {}", e.getMessage(), e);
            disconnect();
        }
    }
}
