package com.market.scheduler;

import com.market.entity.RobotConfig;
import com.market.service.PriceRobotService;
import com.market.service.SpotDataProcessService;
import com.market.service.SpotKlineRecordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 行情机器人调度器
 * <p>
 * 职责：
 * <ul>
 *   <li>应用启动（{@link PostConstruct}）：加载活跃配置，每个 symbol 启动一条死循环线程</li>
 *   <li>死循环线程：tick() → sleep(intervalMs)，最小 1ms，被中断后自行退出</li>
 *   <li>每分钟巡检（{@link Scheduled}）：与上次快照 diff，启动新增、停止已关闭、重启意外崩溃线程</li>
 * </ul>
 *
 * @author jean
 * @date 2026-05-11
 */
@Component
public class PriceRobotScheduler {

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

    // ------------------------------------------------------------------ 线程状态

    /**
     * 机器人线程运行状态
     */
    public enum ThreadStatus {
        /** 线程正在运行 */
        RUNNING,
        /** 线程已退出（正常或意外） */
        DEAD
    }

    // ------------------------------------------------------------------ 内存结构

    /** 线程名称计数器（跨实例共享） */
    private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(0);

    /** 线程池（按需创建守护线程） */
    private final ExecutorService threadPool = Executors.newCachedThreadPool(r -> {
        Thread t = new Thread(r, "robot-" + THREAD_COUNTER.incrementAndGet());
        t.setDaemon(true);
        return t;
    });

    /** symbol → 线程 Future（用于 cancel） */
    private final Map<String, Future<?>> threadMap = new ConcurrentHashMap<>();

    /** symbol → 线程状态（用于检测意外崩溃后重启） */
    private final Map<String, ThreadStatus> threadStatusMap = new ConcurrentHashMap<>();

    /** 上次巡检的活跃 symbol 快照（用于 diff 对比） */
    private volatile Set<String> lastActiveSymbols = new CopyOnWriteArraySet<>();

    // ------------------------------------------------------------------ 依赖

    @Autowired
    private PriceRobotService priceRobotService;

    @Autowired
    private SpotKlineRecordService spotKlineRecordService;

    @Autowired
    private SpotDataProcessService spotDataProcessService;

    // ------------------------------------------------------------------ 启动

    /**
     * 应用启动时初始化：加载所有活跃配置并启动对应线程
     */
    @PostConstruct
    public void init() {
        List<RobotConfig> activeConfigs = priceRobotService.listAllActive();
        Set<String> symbols = activeConfigs.stream()
                .map(RobotConfig::getSymbol)
                .collect(Collectors.toSet());

        for (String symbol : symbols) {
            startThread(symbol);
        }

        lastActiveSymbols = new CopyOnWriteArraySet<>(symbols);
        log.info("行情机器人初始化完成，启动 {} 个线程: {}", symbols.size(), symbols);
    }

    // ------------------------------------------------------------------ 定时巡检

    /**
     * 每分钟巡检：与上次快照 diff，动态启停线程并检测重启意外崩溃的线程
     */
    @Scheduled(fixedRate = 60_000)
    public void syncThreads() {
        // 1. 从 DB 获取当前活跃 symbol 集合
        List<RobotConfig> activeConfigs = priceRobotService.listAllActive();
        Set<String> currentActiveSymbols = activeConfigs.stream()
                .map(RobotConfig::getSymbol)
                .collect(Collectors.toSet());

        // 2. 与上次快照 diff
        Set<String> toStart = new HashSet<>(currentActiveSymbols);
        toStart.removeAll(lastActiveSymbols);

        Set<String> toStop = new HashSet<>(lastActiveSymbols);
        toStop.removeAll(currentActiveSymbols);

        // 3. 停止已关闭的线程
        for (String symbol : toStop) {
            stopThread(symbol);
        }

        // 4. 启动新增的线程
        for (String symbol : toStart) {
            priceRobotService.invalidateConfig(symbol);
            startThread(symbol);
        }

        // 5. 检测意外崩溃（DEAD）且仍在活跃列表中的线程，自动重启；正常运行中的线程刷新配置缓存
        for (String symbol : currentActiveSymbols) {
            if (ThreadStatus.DEAD.equals(threadStatusMap.get(symbol))) {
                log.warn("行情机器人线程意外退出，自动重启: symbol={}", symbol);
                priceRobotService.invalidateConfig(symbol);
                startThread(symbol);
            } else if (ThreadStatus.RUNNING.equals(threadStatusMap.get(symbol))) {
                // 正常运行中：使 Caffeine 配置缓存失效，下次 tick() 读取最新 DB 配置
                priceRobotService.invalidateConfig(symbol);
            }
        }

        // 6. 更新快照
        lastActiveSymbols = new CopyOnWriteArraySet<>(currentActiveSymbols);

        if (!toStart.isEmpty() || !toStop.isEmpty()) {
            log.info("行情机器人巡检完成: 新启动={}, 已停止={}", toStart, toStop);
        }
    }

    // ------------------------------------------------------------------ 线程管理

    /**
     * 启动指定 symbol 的死循环线程
     * <p>
     * 线程内循环：tick() → sleep(intervalMs，最小 1ms），被中断后退出。
     * 线程退出时（正常或异常），在 finally 块中标记状态为 DEAD。
     *
     * @param symbol 交易对（小写）
     */
    public void startThread(String symbol) {
        // 已有运行中的线程则跳过
        if (threadMap.containsKey(symbol) && ThreadStatus.RUNNING.equals(threadStatusMap.get(symbol))) {
            return;
        }

        // 确保该 symbol 的 K 线分表存在（首次启动时建表，避免写库报 Table not found）
        try {
            spotKlineRecordService.ensureKlineTable(symbol);
        } catch (Exception e) {
            log.warn("行情机器人启动前建表失败，继续启动线程: symbol={}", symbol, e);
        }

        // 预热各周期 kline 缓存（从 DB 加载最新一条），保证首个新周期开盘价连续
        try {
            spotDataProcessService.preloadRobotKlineCache(symbol);
        } catch (Exception e) {
            log.warn("行情机器人 kline 缓存预热失败，继续启动线程: symbol={}", symbol, e);
        }

        threadStatusMap.put(symbol, ThreadStatus.RUNNING);

        Future<?> future = threadPool.submit(() -> {
            // 在线程内设置真实名称，方便排查日志
            Thread.currentThread().setName("robot-" + symbol);
            log.info("行情机器人线程启动: symbol={}", symbol);

            try {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        // 读取配置（Caffeine 缓存，未命中回源 DB）
                        RobotConfig config = priceRobotService.getConfig(symbol);
                        if (config == null) {
                            log.warn("行情机器人配置不存在，线程退出: symbol={}", symbol);
                            break;
                        }

                        // 执行一次价格生成
                        priceRobotService.tick(symbol, config);

                        // 按配置间隔休眠，最小 1ms
                        Thread.sleep(Math.max(1L, config.getIntervalMs()));

                    } catch (InterruptedException e) {
                        // 被 cancel(true) 中断，正常退出
                        Thread.currentThread().interrupt();
                        break;
                    } catch (Exception e) {
                        log.error("行情机器人 tick 异常: symbol={}", symbol, e);
                        // 非中断异常不终止循环，继续运行
                    }
                }
            } finally {
                // 无论正常退出还是意外崩溃，均标记为 DEAD
                threadStatusMap.put(symbol, ThreadStatus.DEAD);
                threadMap.remove(symbol);
                log.info("行情机器人线程退出: symbol={}", symbol);
            }
        });

        threadMap.put(symbol, future);
    }

    /**
     * 停止指定 symbol 的线程（向线程发送中断信号）
     *
     * @param symbol 交易对（小写）
     */
    public void stopThread(String symbol) {
        Future<?> future = threadMap.remove(symbol);
        if (future != null) {
            future.cancel(true);
        }
        threadStatusMap.remove(symbol);
        priceRobotService.invalidateConfig(symbol);
        log.info("行情机器人线程已停止: symbol={}", symbol);
    }

    // ------------------------------------------------------------------ Controller 触发接口

    /**
     * 由 Controller 触发立即启动（不等待下次巡检）
     *
     * @param symbol 交易对（小写）
     */
    public void triggerStart(String symbol) {
        priceRobotService.invalidateConfig(symbol);
        startThread(symbol);
        lastActiveSymbols.add(symbol);
    }

    /**
     * 由 Controller 触发立即停止（不等待下次巡检）
     *
     * @param symbol 交易对（小写）
     */
    public void triggerStop(String symbol) {
        stopThread(symbol);
        lastActiveSymbols.remove(symbol);
    }

    /**
     * 获取线程状态字符串（供响应 VO 使用）
     *
     * @param symbol 交易对（小写）
     * @return "RUNNING" / "DEAD" / "NOT_STARTED"
     */
    public String getThreadStatusStr(String symbol) {
        ThreadStatus ts = threadStatusMap.get(symbol);
        if (ts == null) {
            return "NOT_STARTED";
        }
        return ts.name();
    }

    // ------------------------------------------------------------------ 销毁

    /**
     * 应用关闭时优雅停止线程池
     */
    @PreDestroy
    public void destroy() {
        log.info("行情机器人调度器关闭，中止所有线程...");
        threadPool.shutdownNow();
    }
}
