package com.fs.app.taskService.impl;

import com.alibaba.fastjson.JSON;
import com.fs.app.taskService.QwExternalContactRatingService;
import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.course.mapper.FsCourseWatchLogMapper;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.sop.domain.SopUserLogs;
import com.fs.sop.domain.SopUserLogsInfo;
import com.fs.sop.mapper.SopUserLogsInfoMapper;
import com.fs.sop.mapper.SopUserLogsMapper;
import com.fs.sop.params.QwRatingConfig;
import com.fs.sop.service.IQwSopTempDayService;
import com.fs.sop.vo.QwRatingVO;
import com.fs.system.service.ISysConfigService;
import com.fs.voice.utils.StringUtil;
import com.google.common.util.concurrent.AtomicDouble;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class QwExternalContactRatingServiceImpl implements QwExternalContactRatingService {


    @Autowired
    private ISysConfigService configService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private SopUserLogsMapper sopUserLogsMapper;

    @Autowired
    private IQwSopTempDayService qwSopTempDayService;

    @Autowired
    private SopUserLogsInfoMapper sopUserLogsInfoMapper;

    @Autowired
    private FsCourseWatchLogMapper fsCourseWatchLogMapper;

    @Autowired
    private QwExternalContactMapper qwExternalContactMapper;

    @Autowired
    private ExecutorService sopRatingExecutor;  // 自定义线程池

    // 任务队列
    private final BlockingQueue<SopUserLogs> taskQueue = new LinkedBlockingQueue<>(10000);
    private volatile boolean running = true;
    //批量更新队列
    private final List<CompletableFuture<Void>> updateFutures = Collections.synchronizedList(new ArrayList<>());

    private final Object configLock = new Object();

    // 启动时初始化消费者线程
    @PostConstruct
    public void init() {

        loadCourseConfig();

        int consumerCount = Runtime.getRuntime().availableProcessors(); // 消费者线程数，默认 CPU 核心数
        for (int i = 0; i < consumerCount; i++) {
            sopRatingExecutor.submit(this::consumeTasks); // 提交消费者任务
        }

        log.info("初始化 {} 个消费者线程", consumerCount);
    }

    private  volatile QwRatingConfig qwRatingConfig;

    private void loadCourseConfig() {
        try {
            String json = configService.selectConfigByKey("qwRating:config");
            QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);
            if (!StringUtil.strIsNullOrEmpty(json) && config != null) {
                qwRatingConfig = config;
                log.info("Loaded qwRating.config successfully.");
            } else {
                log.error("Failed to load course.config from configService.");
            }
        } catch (Exception e) {
            log.error("Exception while loading qwRating.config: {}", e.getMessage(), e);
        }
    }

    @Override
    public R ratingUserLogs() {

        // 分页加载并放入队列
        int pageSize = 1000;
        int offset = 0;
        List<SopUserLogs> sopUserLogs;

        do {
            sopUserLogs = sopUserLogsMapper.meetsTheRatingByUserInfoWithPagination(offset, pageSize);
            if (!sopUserLogs.isEmpty()) {
                sopUserLogs.forEach(item -> {
                    try {
                        taskQueue.put(item); // 将任务放入队列
                    } catch (InterruptedException e) {
                        log.error("任务放入队列失败，sopId: {}", item.getSopId(), e);
                        Thread.currentThread().interrupt();
                    }
                });
                offset += pageSize;
            }
        } while (!sopUserLogs.isEmpty());


        // 等待队列处理完成
        CompletableFuture.runAsync(() -> {
            while (!taskQueue.isEmpty()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("等待队列处理时中断", e);
                    Thread.currentThread().interrupt();
                }
            }
        }).join(); // 等待任务完成

        return R.ok();
    }


    private void consumeTasks() {

        if (!running && taskQueue.isEmpty()) {
            return; // 如果队列为空且没有正在运行的线程，则直接返回
        }

        while (running) {
            try {
                SopUserLogs item = taskQueue.poll(1, TimeUnit.SECONDS); // 等待 1 秒
                if (item != null) {
                    processSingleTask(item);
                }
            } catch (Exception e) {
                log.error("消费者线程异常", e);
            }
        }
    }

    private void processSingleTask(SopUserLogs item) {

        // 获取缓存的配置
        QwRatingConfig config;
        synchronized(configLock) {
            config = qwRatingConfig;
        }

        Integer countDays = item.getCountDays();
        String cacheKey = "sop-tempId:" + item.getSopTempId();
        Integer sopTemIdNum = redisCache.getCacheObject(cacheKey);

        if (sopTemIdNum == null) {
            sopTemIdNum = qwSopTempDayService.getDayNumByIdLimitOne(item.getSopTempId());
            redisCache.setCacheObject(cacheKey, sopTemIdNum, 3, TimeUnit.HOURS);
        }

        if (sopTemIdNum < countDays) {
            log.info("当前营期的伦次中，模板天数不足。不评级：{}|sopId：{}", item.getSopTempId(), item.getSopId());
            return;
        }

        List<SopUserLogsInfo> sopUserLogsInfosList = sopUserLogsInfoMapper
                .selectSopUserLogsInfoListBySopId(item.getSopId(), item.getId());

        if (sopUserLogsInfosList == null || sopUserLogsInfosList.isEmpty()) {
            log.error("当前营期没有客户-sopId：{},营期id:{}", item.getSopId(), item.getId());
            return;
        }

        List<QwExternalContact> batchQwExternalContact = sopUserLogsInfosList.stream()
                .map(logsInfo -> processUserLog(logsInfo, config))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (!batchQwExternalContact.isEmpty()) {
            batchUpdateQwExternalContact(batchQwExternalContact);
        }
    }

    private QwExternalContact processUserLog(SopUserLogsInfo logsInfo, QwRatingConfig config) {
        try {
            Long externalId = logsInfo.getExternalId();
            if (externalId == null) {
                return null;
            }

            List<QwRatingVO> ratingVOS = fsCourseWatchLogMapper
                    .selectFsCourseWatchLogByExtIdRating(externalId, config.getLevelDay());

            if (ratingVOS == null || ratingVOS.isEmpty()) {
                log.info("没有记录不评级，externalId: {}", externalId);
                return null;
            }

            int scoreLevel = getScoreLevel(ratingVOS, config);
            int latestTime = getLatestTime(ratingVOS);
            int levelUpFall = ratingVOS.get(0).getLevelType() != null
                    ? getLevelUpFall(scoreLevel, ratingVOS.get(0).getLevelType())
                    : 3;

            QwExternalContact externalContact = new QwExternalContact();
            externalContact.setId(externalId);
            externalContact.setLevel(scoreLevel);
            externalContact.setLastWatchTime(latestTime);
            externalContact.setLevelType(levelUpFall);

            return externalContact;

        } catch (Exception e) {
            log.error("计算用户积分异常，用户：{}", logsInfo, e);
            return null;
        }
    }

    private void batchUpdateQwExternalContact(List<QwExternalContact> notInExternalUseridList) {
        int batchSize = 300;

        for (int i = 0; i < notInExternalUseridList.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, notInExternalUseridList.size());
            List<QwExternalContact> batchList = notInExternalUseridList.subList(i, endIndex);

            int finalI = i;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    qwExternalContactMapper.batchUpdateQwExternalContact(batchList);
                    log.info("成功更新评级数据，起始索引: {}, 数量: {}", finalI, batchList.size());
                } catch (Exception e) {
                    log.error("批量更新异常，批次起始索引: {}", finalI, e);
                }
            }, sopRatingExecutor);

            updateFutures.add(future);
        }
    }

    @PreDestroy
    public void shutdown() {
        running = false;  // 标记消费者停止
        log.info("正在关闭线程池...");

        // **等待任务队列处理完毕**
        while (!taskQueue.isEmpty()) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("等待任务队列处理完成时被中断", e);
            }
        }

        // **确保所有 `batchUpdateQwExternalContact` 的任务完成**
        log.info("等待所有批量更新任务完成...");
        CompletableFuture.allOf(updateFutures.toArray(new CompletableFuture[0])).join();

        // 关闭线程池
        sopRatingExecutor.shutdown();
        try {
            if (!sopRatingExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                List<Runnable> pendingTasks = sopRatingExecutor.shutdownNow();
                log.warn("强制关闭线程池，未完成任务数: {}", pendingTasks.size());
            }
        } catch (InterruptedException e) {
            sopRatingExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        log.info("线程池和消费者已完全关闭");
    }


    /**
    * 每6小时更新一次
    */
    @Scheduled(cron = "0 50 0/6 * * ?")
    public void refreshRatingConfig() {

        synchronized(configLock) {
            try {
                String json = configService.selectConfigByKey("qwRating:config");
                QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);
                if (!StringUtil.strIsNullOrEmpty(json) && config != null) {
                    qwRatingConfig = config;
                    log.info("LoadedTime qwRating.config successfully.");
                } else {
                    log.error("Failed to load course.config from configService.");
                }
            } catch (Exception e) {
                log.error("Exception while refreshing course.config: {}", e.getMessage(), e);
            }
        }

    }


    //评级
    public int getScoreLevel(List<QwRatingVO> qwRatingVOS, QwRatingConfig config) {

        AtomicDouble watchCount= new AtomicDouble();

        qwRatingVOS.forEach(vo -> {
            // 计算 watchDuration 除以 allDuration，并保留2位小数，四舍五入
            BigDecimal watchDuration = new BigDecimal(vo.getWatchDuration());
            BigDecimal allDuration = new BigDecimal(vo.getAllDuration());

            BigDecimal ratio = watchDuration.divide(allDuration, 3, RoundingMode.DOWN);

            // 将结果四舍五入后加到 watchCount
            watchCount.addAndGet(ratio.doubleValue());
        });


        // 计算 watchCount 除以 allSize 的结果并四舍五入为整数
        BigDecimal result = new BigDecimal(watchCount.get())
                .divide(new BigDecimal(qwRatingVOS.size()), 3, RoundingMode.DOWN);

        // 将结果乘以 100
        BigDecimal resultMultiplied = result.multiply(new BigDecimal(100));

        // 四舍五入到整数
        BigDecimal roundedResult = resultMultiplied.setScale(0, RoundingMode.HALF_UP);

        // 转换为 int 类型
        int score = roundedResult.intValue();

        if (score >= config.getALevelMin()) {
            return 1; // A 等级
        } else if (score >= config.getBLevelMin() && score < config.getBLevelMax()) {
            return 2; // B 等级
        } else if (score >= config.getCLevelMin() && score < config.getCLevelMax()) {
            return 3; // C 等级
        } else if (score >= config.getDLevelMin() && score < config.getDLevelMax()) {
            return 4; // D 等级
        } else {
            throw new IllegalArgumentException("分数不在任何等级范围内: " + score);
        }
    }
    //升降等级
    public int getLevelUpFall(int scoreLevel,int levelUpFall){

        if (scoreLevel > levelUpFall) {
            return  1;//升级
        }else if (scoreLevel < levelUpFall) {
            return  2;//降级
        }else {
            return  3;//不变
        }
    }

    //计算最晚看课时间
    public int getLatestTime(List<QwRatingVO> qwRatingVOS){

        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("HHmm");

        // 用于存储提取出的时分
        List<String> timeOnlyList = new ArrayList<>();

        qwRatingVOS.forEach(vos->{

            String finishTime = vos.getFinishTime();

            if (!StringUtil.strIsNullOrEmpty(finishTime)){
                LocalTime localTime = LocalTime.parse(finishTime, formatter);
                String formattedTime = localTime.format(outputFormatter);
                timeOnlyList.add(formattedTime);
            }

        });
        String latestTime=null;
        if (!timeOnlyList.isEmpty()){
            latestTime  = Collections.max(timeOnlyList);
        }else {
            latestTime = "0";
        }


        return Integer.parseInt(latestTime);
    }


}
