package com.nexo.job.service;

import com.nexo.common.redis.utils.RedisUtils;
import com.nexo.job.mapper.NexoDouyinUserInfoJobMapper;
import com.nexo.module.api.douyin.domain.NexoDouyinUserInfo;
import com.nexo.module.api.douyin.utils.DouYinUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * XxlJob开发示例（Bean模式）
 * <p>
 * 开发步骤：
 * 1、任务开发：在Spring Bean实例中，开发Job方法；
 * 2、注解配置：为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"，注解value值对应的是调度中心新建任务的JobHandler属性的值。
 * 3、执行日志：需要通过 "XxlJobHelper.log" 打印执行日志；
 * 4、任务结果：默认任务结果为 "成功" 状态，不需要主动设置；如有诉求，比如设置任务结果为失败，可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果；
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Slf4j
@Service
public class SampleService {

    @Value("${spring.profiles.active}")
    private String activeProfile;


    @Resource
    private NexoDouyinUserInfoJobMapper nexoDouyinUserInfoMapper;
    /**
     * 抖音直播监控任务专用线程池
     * <p>
     * 线程池配置说明：
     * - 核心线程数：5，保持常驻线程数量
     * - 最大线程数：10，高峰期可扩展的最大线程数
     * - 空闲线程存活时间：60秒，超过核心线程数的空闲线程将在60秒后回收
     * - 工作队列：有界阻塞队列，容量1000，用于缓存待处理任务
     * - 线程工厂：自定义线程命名，便于日志追踪和问题定位
     * - 拒绝策略：CallerRunsPolicy，当队列满且线程达最大值时，由调用线程执行任务，避免任务丢失
     * <p>
     * 适用场景：
     * - 批量处理抖音用户数据更新
     * - 并发执行网络请求和数据库操作
     * - 提高定时任务执行效率
     */
    private static final ExecutorService executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactory() {
        private final AtomicInteger threadNumber = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "douyin-live-monitor-" + threadNumber.getAndIncrement());
            t.setDaemon(false);
            return t;
        }
    }, new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 抖音直播监控定时任务
     * <p>
     * 功能说明：
     * 1. 获取所有需要更新的抖音用户信息列表
     * 2. 使用线程池并发处理每个用户的数据更新
     * 3. 调用抖音API获取最新用户信息
     * 4. 批量更新数据库中对应的用户记录
     * 5. 统计成功和失败数量，记录执行日志
     * <p>
     * 并发策略：
     * - 使用预配置的线程池（核心30线程，最大50线程）并发执行
     * - 通过CountDownLatch等待所有任务完成
     * - 使用AtomicInteger保证计数的线程安全
     * <p>
     * 异常处理：
     * - 单个用户处理失败不影响其他用户
     * - 详细记录失败原因便于问题排查
     * - 捕获中断异常并恢复中断状态
     *
     * @throws Exception 任务执行异常
     */
    @XxlJob("douyinLiveMonitor")
    public void douyinLiveMonitor() throws Exception {
        long startTime = new Date().getTime();
        List<NexoDouyinUserInfo> updateInfoList = nexoDouyinUserInfoMapper.getUpdateInfoList(null);
        XxlJobHelper.log("更新用户数：{}", updateInfoList.size());

        if (updateInfoList.isEmpty()) {
            XxlJobHelper.log("没有需要更新的用户数据");
            return;
        }

        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failCount = new AtomicInteger(0);
        CountDownLatch latch = new CountDownLatch(updateInfoList.size());

        for (int i = 0; i < updateInfoList.size(); i++) {
            final NexoDouyinUserInfo info = updateInfoList.get(i);
            final int index = i + 1;
            executor.submit(() -> {
                try {
                    long time1 = new Date().getTime();
                    XxlJobHelper.log("开始处理第{}条数据：{} - {} - {}", index, info.getUserId(), info.getNickname(), info.getSecUid());
                    NexoDouyinUserInfo userInfo = DouYinUtils.getUserInfo(info.getSecUid(), !activeProfile.equals("prod") ? RedisUtils.getCacheMapValue("sys_config", "douyin_config_dev") : RedisUtils.getCacheMapValue("sys_config", "douyin_config_pord"));
                    List<Long> longs = nexoDouyinUserInfoMapper.getUpdateInfoList(info.getSecUid()).stream().map(item -> item.getId()).collect(Collectors.toList());

                    XxlJobHelper.log("user_id {} 昵称 {} 需要更新 {} 条用户记录", info.getUserId(), info.getNickname(), longs.size());

                    for (Long id : longs) {
                        try {
                            userInfo.setId(id);
                            userInfo.setJobInfoStatus(null);
                            userInfo.setJobLiveStatus(null);
                            userInfo.setJobVideoStatus(null);
                            userInfo.setCreateBy(null);
                            userInfo.setCreateTime(null);
                            userInfo.setDeptId(null);
                            userInfo.setUpdateTime(new Date());

                            nexoDouyinUserInfoMapper.updateById(userInfo);
                            successCount.incrementAndGet();
                        } catch (Exception e) {
                            failCount.incrementAndGet();
                            XxlJobHelper.log("更新用户记录失败，ID: {}, 错误: {}", id, e.getMessage());
                            log.error("更新用户记录失败，ID: {}", id, e);
                        }
                    }

                    long costTime = (new Date().getTime() - time1) / 1000;
                    XxlJobHelper.log("第{}条数据处理完成，耗时 {} 秒", index, costTime);
                } catch (Exception e) {
                    failCount.incrementAndGet();
                    XxlJobHelper.log("处理第{}条数据失败：{}, 错误: {}", index, info.getSecUid(), e.getMessage());
                    log.error("处理第{}条数据失败：{}", index, info.getSecUid(), e);
                } finally {
                    latch.countDown();
                }
            });
        }

        try {
            latch.await();
            long totalTime = (new Date().getTime() - startTime) / 1000;
            XxlJobHelper.log("所有数据处理完成！总计: {}, 成功: {}, 失败: {}, 总耗时: {} 秒", updateInfoList.size(), successCount.get(), failCount.get(), totalTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            XxlJobHelper.log("任务被中断：{}", e.getMessage());
            log.error("任务被中断", e);
        }
    }
    @Resource
    private ConfigurableEnvironment configurableEnvironment;

    @XxlJob("test")
    public void test() throws Exception {
        XxlJobHelper.log("开始执行任务");
        String profile = configurableEnvironment.getProperty("spring.profiles.active");
        log.info("activeProfile:{}", profile);
        XxlJobHelper.log("任务执行完成");
    }

}
