package com.market.manager;

import com.market.service.SpotConfigSymbolService;
import com.market.service.SpotDataProcessService;
import com.market.service.SpotKlineRecordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 现货 Binance WebSocket 管理器
 * <p>
 * 使用单条合并流连接所有活跃币种，订阅 kline / trade / miniTicker。
 * 定时刷新（默认 60 秒）检查币种列表和连接状态，断连或列表变化时重建连接。
 *
 * @author system
 * @date 2026-05-10
 */
@Component
public class BinanceSpotWebSocketManager {

    private static final Logger log = LoggerFactory.getLogger(BinanceSpotWebSocketManager.class);

    /** 现货订阅 K 线周期（币安格式），与合约保持一致 */
    private static final String[] KLINE_INTERVALS = {
        "1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w", "1M"
    };

    private final SpotConfigSymbolService configSymbolService;
    private final SpotDataProcessService  dataProcessService;
    private final SpotKlineRecordService  klineRecordService;

    /** 已订阅的 stream 集合，用于 diff 判断是否需要重连 */
    private volatile Set<String> lastStreamSet = new HashSet<>();

    private BinanceSpotWebSocketClient wsClient;

    public BinanceSpotWebSocketManager(SpotConfigSymbolService configSymbolService,
                                       SpotDataProcessService dataProcessService,
                                       SpotKlineRecordService klineRecordService) {
        this.configSymbolService = configSymbolService;
        this.dataProcessService  = dataProcessService;
        this.klineRecordService  = klineRecordService;
    }

    /**
     * 启动时初始化 WebSocket 客户端并连接
     */
    @PostConstruct
    public void init() {
        wsClient = new BinanceSpotWebSocketClient(dataProcessService);
        refresh();
    }

    /**
     * 定时刷新：预建 K 线分表，断连时重连，stream 列表变化时重建连接
     */
    @Scheduled(fixedRateString = "${spot.binance.symbol-refresh-seconds:60}000")
    public void refresh() {
        List<String> symbols = getActiveSymbols();
        if (symbols.isEmpty()) {
            log.warn("未找到活跃现货交易对，跳过 Binance WS 初始化");
            return;
        }

        // 1. 预建 K 线分表
        symbols.forEach(klineRecordService::ensureKlineTable);

        // 2. 构建 stream 列表
        Set<String> newStreamSet = new HashSet<>(buildStreams(symbols));

        // 3. 连接断开时强制重连
        if (!wsClient.isConnected()) {
            log.info("现货 WS 连接断开，全量重连: {} 个交易对, {} 个 stream",
                    symbols.size(), newStreamSet.size());
            wsClient.connect(new ArrayList<>(newStreamSet));
            lastStreamSet = newStreamSet;
            return;
        }

        // 4. stream 列表未变化则跳过
        if (newStreamSet.equals(lastStreamSet)) {
            return;
        }

        // 5. stream 列表有变化，重建连接
        log.info("现货 stream 列表变化，重建连接: {} 个交易对, {} 个 stream",
                symbols.size(), newStreamSet.size());
        wsClient.connect(new ArrayList<>(newStreamSet));
        lastStreamSet = newStreamSet;
    }

    /**
     * 应用关闭时释放连接
     */
    @PreDestroy
    public void destroy() {
        if (wsClient != null) {
            wsClient.disconnect();
        }
    }

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

    /** 为每个 symbol 构建 stream 列表（kline + trade + miniTicker） */
    private List<String> buildStreams(List<String> symbols) {
        List<String> streams = new ArrayList<>();
        for (String sym : symbols) {
            String s = sym.toLowerCase();
            for (String interval : KLINE_INTERVALS) {
                streams.add(s + "@kline_" + interval);
            }
            streams.add(s + "@trade");
            streams.add(s + "@miniTicker");
        }
        return streams;
    }

    /** 通过 Service 层查询所有开启且非自定义的币种 */
    private List<String> getActiveSymbols() {
        return configSymbolService.getActiveSymbols();
    }
}
