package com.fs.live.task;

import com.fs.common.constant.LiveKeysConstant;
import com.fs.common.core.redis.RedisCache;
import com.fs.live.domain.LiveOrderTipConfig;
import com.fs.live.service.ILiveOrderTipConfigService;
import com.fs.live.service.ILiveOrderTipService;
import com.fs.live.websocket.service.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 直播下单动效提示 —— 假数据填充定时任务
 *
 * <p>策略（高并发友好）：</p>
 * <ul>
 *   <li>只对"有在线用户"的直播间才生成假订单（节省资源）</li>
 *   <li>多节点部署下，通过 Redis 分布式锁避免重复调度，确保同一时间窗口只有一个节点工作</li>
 *   <li>频率控制由 {@link ILiveOrderTipService#publishFakeOrderTip(Long)} 内部基于 Redis 滑动窗口实现，
 *       真实订单与假订单共享配额，保证总频率不超过 rateMaxPerMinute</li>
 *   <li>每轮仅补齐"rateMinPerMinute"到"rateMaxPerMinute"之间的差额，防止刷屏</li>
 * </ul>
 *
 * <p>任务节拍设为 15 秒一轮，配合 window=60 秒的滑动窗口，单直播间每分钟约有 4 次补单机会。</p>
 */
@Slf4j
@Component
public class LiveOrderTipScheduler {

    @Autowired
    private ILiveOrderTipService tipService;

    @Autowired
    private ILiveOrderTipConfigService tipConfigService;

    @Autowired
    private RedisCache redisCache;

    @Scheduled(fixedDelay = 15000, initialDelay = 20000)
    public void generateFakeOrderTips() {
        LiveOrderTipConfig cfg;
        try {
            cfg = tipConfigService.getEffectiveConfig();
        } catch (Exception e) {
            log.warn("[订单动效-定时] 读取配置异常: {}", e.getMessage());
            return;
        }
        if (cfg == null
                || cfg.getEnabled() == null || cfg.getEnabled() != 1
                || cfg.getFakeEnabled() == null || cfg.getFakeEnabled() != 1) {
            return;
        }

        // 获取当前节点"有在线用户"的直播间；在集群场景下每个节点只持有自己的 WebSocket 连接，
        // 但分布式锁按 liveId 加锁，保证多节点对同一直播间不重复推送。
        Map<Long, Integer> active = WebSocketServer.getActiveRoomsSnapshot();
        if (active == null || active.isEmpty()) {
            return;
        }

        int minPerMinute = cfg.getRateMinPerMinute() == null ? 1 : cfg.getRateMinPerMinute();
        int maxPerMinute = cfg.getRateMaxPerMinute() == null ? 3 : cfg.getRateMaxPerMinute();
        if (minPerMinute <= 0 || maxPerMinute <= 0) {
            return;
        }

        for (Map.Entry<Long, Integer> entry : active.entrySet()) {
            Long liveId = entry.getKey();
            // 集群分布式锁：同一直播间、同一 15 秒轮次只在一个节点执行
            String lockKey = String.format(LiveKeysConstant.LIVE_ORDER_TIP_FAKE_LOCK, liveId);
            boolean locked = redisCache.setIfAbsent(lockKey, "1", 12, TimeUnit.SECONDS);
            if (!locked) {
                continue;
            }
            try {
                // 补到"最小速率"即可，避免一直顶到 max 造成视觉疲劳
                // publishFakeOrderTip 内部会再校验窗口剩余配额，保证不超过 max
                int burst = Math.min(1, minPerMinute); // 单次只补一条，由定时任务节拍自然分散
                for (int i = 0; i < burst; i++) {
                    tipService.publishFakeOrderTip(liveId);
                }
            } catch (Exception e) {
                log.warn("[订单动效-定时] 直播间 {} 假订单生成异常: {}", liveId, e.getMessage());
            }
        }
    }
}
