package com.sonic.frog.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.sonic.frog.dao.AiUserDao;
import com.sonic.frog.domain.entity.AiUser;
import com.sonic.frog.domain.input.AgeRange;
import com.sonic.frog.domain.input.ClassificationListInput;
import com.sonic.frog.domain.output.AiUserBaseOutput;
import com.sonic.frog.domain.output.ClassificationListOutput;
import com.sonic.frog.enums.AgeTypeEnum;
import com.sonic.frog.service.AiUserSearchService;
import com.sonic.frog.service.AiUserStatService;
import com.sonic.frog.service.HomeClassificationService;
import com.sonic.frog.utils.BeanConver;
import com.sonic.frog.utils.CacheUtils;
import com.sonic.frog.utils.RedisKeyUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class HomeClassificationServiceImpl implements HomeClassificationService {

    @Autowired
    private AiUserDao aiUserDao;
    @Autowired
    private RedisKeyUtils redisKeyUtils;
    @Autowired
    private CacheUtils cacheUtils;

    /**
     * 每页最多拉取20条数据
     */
    private final int MAX_PS = 20;

    /**
     * 20条数据中每个档位最多取4个
     */
    private final int PER_LEVEL_MAX = 4;

    /**
     * 每个档位最多取100个
     */
    private final int PER_LEVEL_LIMIT = 100;
    @Autowired
    private AiUserSearchService aiUserSearchService;
    @Autowired
    private AiUserStatService aiUserStatService;

    /**
     * 每一次推荐取20张卡片
     * 按照被喜欢数进行阶梯取卡片
     * 0个-取4个
     * 1-20-取4个
     * 21-50-取4个
     * 51-100-取4个
     * 大于100-取4个
     * 如果高档位没有，就像低档位取，依次取到最低档位
     * 每一次推荐将取出来的角色在20条内随机排列
     * 公开列表不包含被删除的，私密的，可以包含登录的创建者的
     *
     * @param userId
     * @param input
     * @return
     */
    @Override
    public List<ClassificationListOutput> classificationList(Long userId, ClassificationListInput input) {
        //超过25页，直接返回空
        if (input.getPn() > 25) {
            return Lists.newArrayList();
        }
        //限制ps最大为20
        input.setPs(input.getPs() > MAX_PS ? MAX_PS : input.getPs());
        //1. 按档位查询aiId列表, 每个档位预先查询100个，相当于最多查询25页
        // 用户刚进来的，默认查询类别(角色)所有，选择情感分类，标签为情感所有标签，定时任务缓存，定时任务每1分钟更新一次缓存，缓存时间1小时, 每次用户进来都从缓存获取
        // 用户选择条件,缓存到redis，缓存时间1小时,后面查询从缓存中查询
        List<Long> likedListGt100 = queryAiIdsByLikedRange(input, 101, Integer.MAX_VALUE, PER_LEVEL_LIMIT, false);
        List<Long> likedList51to100 = queryAiIdsByLikedRange(input, 51, 100, PER_LEVEL_LIMIT, false);
        List<Long> likedList21To50 = queryAiIdsByLikedRange(input, 21, 50, PER_LEVEL_LIMIT, false);
        List<Long> likedList1To20 = queryAiIdsByLikedRange(input, 1, 20, PER_LEVEL_LIMIT, false);
        List<Long> likedList0 = queryAiIdsByLikedRange(input, 0, 0, 500, false);
        log.info("===> classificationList pn:{},likedListGt100:{}", input.getPn(), JSON.toJSONString(likedListGt100));
        log.info("===> classificationList pn:{},likedList51to100:{}", input.getPn(), JSON.toJSONString(likedList51to100));
        log.info("===> classificationList pn:{},likedList21To50:{}", input.getPn(), JSON.toJSONString(likedList21To50));
        log.info("===> classificationList pn:{},likedList1To20:{}", input.getPn(), JSON.toJSONString(likedList1To20));
        log.info("===> classificationList pn:{},likedList0:{}", input.getPn(), JSON.toJSONString(likedList0));

        // 2. 过滤已查看过的AI（排除exList中的ID）
        List<Long> filteredGt100 = filterExcluded(likedListGt100, input.getExList());
        List<Long> filtered51to100 = filterExcluded(likedList51to100, input.getExList());
        List<Long> filtered21To50 = filterExcluded(likedList21To50, input.getExList());
        List<Long> filtered1To20 = filterExcluded(likedList1To20, input.getExList());
        List<Long> filtered0 = filterExcluded(likedList0, input.getExList());

        log.info("===> classificationList pn:{},filteredGt100:{}", input.getPn(), JSON.toJSONString(filteredGt100));
        log.info("===> classificationList pn:{},filtered51to100:{}", input.getPn(), JSON.toJSONString(filtered51to100));
        log.info("===> classificationList pn:{},filtered21To50:{}", input.getPn(), JSON.toJSONString(filtered21To50));
        log.info("===> classificationList pn:{},filtered1To20:{}", input.getPn(), JSON.toJSONString(filtered1To20));
        log.info("===> classificationList pn:{},filtered0:{}", input.getPn(), JSON.toJSONString(filtered0));

        // 3. 按优先级取数：高档位优先，每个档位最多取4个，总取20个
        List<Long> selectedAiIds = selectAiIdsByPriority(filteredGt100, filtered51to100, filtered21To50, filtered1To20, filtered0,
                PER_LEVEL_MAX,  // 每个档位最多取4个
                MAX_PS  // 总数量
        );

        log.info("===> classificationList pn:{},selectedAiIds:{}", input.getPn(), JSON.toJSONString(selectedAiIds));

        // 4. 查询选中AI的详细信息（实际场景中这里是批量查询数据库）
        List<ClassificationListOutput> outputList = buildOutputList(selectedAiIds);

        // 5. 随机排列结果
        Collections.shuffle(outputList);

        return outputList;
    }

    /**
     * 按被喜欢数范围查询AIID
     * 1. 筛选条件：role_code、character_code、tag_code（input中获取）
     * 2. 筛选被喜欢数在[minLiked, maxLiked]之间
     * 3. 排除is_delete=1和permission!=1（非公开）的数据
     * 4. 限制查询数量为limit
     *
     * @param input    包含查询条件（role_code等）
     * @param minLiked 最小被喜欢数
     * @param maxLiked 最大被喜欢数
     * @param limit    最多查询数量
     * @return AIID列表
     */
    private List<Long> queryAiIdsByLikedRange(ClassificationListInput input, int minLiked, int maxLiked, int limit, boolean isUpdateCache) {
        log.info("===> queryAiIdsByLikedRange input:{}", JSON.toJSONString(input));
        //年龄多选处理
        List<AgeRange> ageRangeList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(input.getAgeList())) {
            for (AgeTypeEnum ageTypeEnum : input.getAgeList()) {
                ageRangeList.add(new AgeRange(LocalDateTime.now().minusYears(ageTypeEnum.getEndAge()), LocalDateTime.now().minusYears(ageTypeEnum.getStartAge())));
            }
        }
        input.setAgeRangeList(ageRangeList);
        //缓存键
        String codeStr = JSON.toJSONString(Lists.newArrayList(input.getRoleCodeList(), input.getCharacterCodeList(), input.getTagCodeList()));
        String redisKey = redisKeyUtils.homeClassificationCacheKey(codeStr, input.getSexList(), input.getAgeList(), minLiked, maxLiked);
        log.info("===> queryAiIdsByLikedRange redisKey:{}", redisKey);
        if (!isUpdateCache) {
            //缓存1小时
            return cacheUtils.getCacheListAndSet(redisKey, Long.class, () -> {
                List<Long> aiIdList = aiUserDao.queryAiIdsByLikedRange(input, minLiked, maxLiked, limit);
                return JSON.toJSONString(aiIdList);
            }, 60 * 60);
        } else {
            //强制更新缓存
            List<Long> aiIdList = aiUserDao.queryAiIdsByLikedRange(input, minLiked, maxLiked, limit);
            cacheUtils.setCache(redisKey, JSON.toJSONString(aiIdList), 60 * 60);
            return aiIdList;
        }
    }

    /**
     * 过滤已查看过的AI
     *
     * @param aiIds       原始AIID列表
     * @param excludedIds 已查看的AIID列表
     * @return 过滤后的AIID列表
     */
    private List<Long> filterExcluded(List<Long> aiIds, List<Long> excludedIds) {
        if (aiIds == null || aiIds.isEmpty()) {
            return Lists.newArrayList();
        }
        if (excludedIds == null || excludedIds.isEmpty()) {
            return new ArrayList<>(aiIds);
        }
        // 利用HashSet优化contains判断（O(1)复杂度）
        Set<Long> excludedSet = new HashSet<>(excludedIds);
        return aiIds.stream().filter(aiId -> !excludedSet.contains(aiId)).collect(Collectors.toList());
    }

    /**
     * 按优先级（高档位优先）选取AIID
     *
     * @param gt100         档位5（>100）
     * @param fiftyOneTo100 档位4（51-100）
     * @param twentyOneTo50 档位3（21-50）
     * @param oneTo20       档位2（1-20）
     * @param zero          档位1（0）
     * @param perLevelMax   每个档位最多选取数量
     * @param totalMax      总选取数量
     * @return 选中的AIID列表
     */
    private List<Long> selectAiIdsByPriority(List<Long> gt100, List<Long> fiftyOneTo100, List<Long> twentyOneTo50, List<Long> oneTo20, List<Long> zero, int perLevelMax, int totalMax) {
        List<Long> result = Lists.newArrayListWithCapacity(totalMax);
        List<Long> levelSubList1 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, Lists.newArrayList(), 0);
        List<Long> levelSubList2 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList1, 1);
        List<Long> levelSubList3 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList2, 2);
        List<Long> levelSubList4 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList3, 3);
        List<Long> levelSubList5 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList4, 4);
        log.info("levelSubList1:{}", JSON.toJSONString(levelSubList1));
        log.info("levelSubList2:{}", JSON.toJSONString(levelSubList2));
        log.info("levelSubList3:{}", JSON.toJSONString(levelSubList3));
        log.info("levelSubList4:{}", JSON.toJSONString(levelSubList4));
        log.info("levelSubList5:{}", JSON.toJSONString(levelSubList5));
        result.addAll(levelSubList1);
        result.addAll(levelSubList2);
        result.addAll(levelSubList3);
        result.addAll(levelSubList4);
        result.addAll(levelSubList5);
        return result;
    }

    /**
     * 根据aiId列表构建输出对象列表
     *
     * @param aiIds AIID列表
     * @return 输出对象列表
     */
    private List<ClassificationListOutput> buildOutputList(List<Long> aiIds) {
        if (aiIds.isEmpty()) {
            return Lists.newArrayList();
        }
        //批量获取ai基础信息Map
        Map<Long, AiUserBaseOutput> aiUserMap = aiUserSearchService.mapByAiIdList(aiIds);
        //批量获取被喜欢数Map
        Map<Long, Integer> aiLikedCountMap = aiUserStatService.queryAiLikedCount(aiIds);
        return aiIds.stream().map(aiId -> {
            AiUserBaseOutput aiUserBaseOutput = aiUserMap.get(aiId);
            //基础信息赋值
            ClassificationListOutput output = BeanConver.copeBean(aiUserBaseOutput, ClassificationListOutput.class);
            if (output != null) {
                //被喜欢数赋值
                output.setLikedNum(aiLikedCountMap.get(aiId) != null ? aiLikedCountMap.get(aiId) : 0);
            }
            return output;
        }).collect(Collectors.toList());
    }

    @Override
    public void homeClassificationUpdateCache(ClassificationListInput input) {
        queryAiIdsByLikedRange(input, 101, Integer.MAX_VALUE, PER_LEVEL_LIMIT, true);
        queryAiIdsByLikedRange(input, 51, 100, PER_LEVEL_LIMIT, true);
        queryAiIdsByLikedRange(input, 21, 50, PER_LEVEL_LIMIT, true);
        queryAiIdsByLikedRange(input, 1, 20, PER_LEVEL_LIMIT, true);
        queryAiIdsByLikedRange(input, 0, 0, PER_LEVEL_LIMIT, true);
    }

    public static void main(String[] args) {
        List<Long> gt100 = Lists.newArrayList(441983024168961L, 439266683584513L, 439266631155713L);
        List<Long> fiftyOneTo100 = Lists.newArrayList();
        List<Long> twentyOneTo50 = Lists.newArrayList(436963048357889L, 436963289530369L);
        List<Long> oneTo20 = Lists.newArrayList(436963121758209L, 436963067232257L, 436963222421505L, 437080281317377L, 441273826344961L, 437109194752001L, 437112596332545L, 437304385077249L, 437316674387969L, 443040313704449L, 437316762468353L, 437242762362881L, 437261680771073L, 437285514903553L, 440185938509825L, 437248804257793L, 440192452263937L, 437069334249473L, 442709930672129L, 440190650810369L, 437305477693441L, 437458928140289L, 442591569510401L, 440187821752321L, 437464559255553L, 438934010658817L, 440193175781377L, 437416915828737L, 441103465906177L, 436963721543681L, 437470041210881L, 441270219243521L, 441992207138817L, 442723671277569L, 437316691165185L, 444208335486977L, 440187649785857L, 440190927634433L, 445795826794497L, 445797259149313L, 437309766369281L, 440197082775553L, 446189583466497L, 446190357315585L, 439217670979585L, 445443379560449L, 445802852319233L, 446192550936577L, 445388404752385L, 445438184914945L, 446196738949121L, 446175786303489L);
        List<Long> zero = Lists.newArrayList();
        selectAiIdsByPriorityTest(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, 4, 20);
    }

    private static List<Long> selectAiIdsByPriorityTest(List<Long> gt100, List<Long> fiftyOneTo100, List<Long> twentyOneTo50, List<Long> oneTo20, List<Long> zero, int perLevelMax, int totalMax) {
        List<Long> result = Lists.newArrayListWithCapacity(totalMax);
        //从高档位开始取，依次从高档位取4个，高档位没有，从低档位取
        List<Long> levelSubList1 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, Lists.newArrayList(), 0);
        List<Long> levelSubList2 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList1, 1);
        List<Long> levelSubList3 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList2, 2);
        List<Long> levelSubList4 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList3, 3);
        List<Long> levelSubList5 = removePreLevelSubListAndGetLevelSubList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero, perLevelMax, levelSubList4, 4);
        System.out.println("levelSubList1:" + JSON.toJSONString(levelSubList1));
        System.out.println("levelSubList2:" + JSON.toJSONString(levelSubList2));
        System.out.println("levelSubList3:" + JSON.toJSONString(levelSubList3));
        System.out.println("levelSubList4:" + JSON.toJSONString(levelSubList4));
        System.out.println("levelSubList5:" + JSON.toJSONString(levelSubList5));
        result.addAll(levelSubList1);
        result.addAll(levelSubList2);
        result.addAll(levelSubList3);
        result.addAll(levelSubList4);
        result.addAll(levelSubList5);
        return result;
    }


    /**
     * 移除前上一级子列表，并获取当前级别子列表
     *
     * @param gt100
     * @param fiftyOneTo100
     * @param twentyOneTo50
     * @param oneTo20
     * @param zero
     * @param perLevelMax
     * @param preLevelSubList
     * @param start
     * @return
     */
    private static List<Long> removePreLevelSubListAndGetLevelSubList(List<Long> gt100, List<Long> fiftyOneTo100, List<Long> twentyOneTo50, List<Long> oneTo20, List<Long> zero, int perLevelMax, List<Long> preLevelSubList, Integer start) {
        //各档位先移除前一级子列表，然后获取当前级别子列表
        gt100.removeAll(preLevelSubList);
        fiftyOneTo100.removeAll(preLevelSubList);
        twentyOneTo50.removeAll(preLevelSubList);
        oneTo20.removeAll(preLevelSubList);
        zero.removeAll(preLevelSubList);
        //移除后重新拼装的各档位列表
        List<List<Long>> levelList = Lists.newArrayList(gt100, fiftyOneTo100, twentyOneTo50, oneTo20, zero);
        //获取当前级别子列表，如果当前没有，递归向低档位获取
        List<Long> levelSubList = getLevelSubList(levelList, perLevelMax, start);
        //当前档位或低档位中没有获取到或不足4个，从总的列表剩余中获取不足的个数
        if (CollectionUtils.isEmpty(levelSubList) || (CollectionUtils.isNotEmpty(levelSubList) && levelSubList.size() < perLevelMax)) {
            //总的剩余列表
            List<Long> totalList = Lists.newArrayList();
            totalList.addAll(gt100);
            totalList.addAll(fiftyOneTo100);
            totalList.addAll(twentyOneTo50);
            totalList.addAll(oneTo20);
            totalList.addAll(zero);
            //移除已获取的
            if (CollectionUtils.isNotEmpty(levelSubList)) {
                totalList.removeAll(levelSubList);
            }
            //满足remainNum个，直接取，不足从总的列表中取
            int remainNum = perLevelMax - levelSubList.size();
            if (totalList.size() >= remainNum) {
                levelSubList.addAll(totalList.subList(0, remainNum));
            } else {
                levelSubList.addAll(totalList.subList(0, totalList.size()));
            }
        }
        return levelSubList;
    }

    /**
     * 获取当前级别子列表
     *
     * @param levelList
     * @param perLevelMax
     * @param start
     * @return
     */
    private static List<Long> getLevelSubList(List<List<Long>> levelList, int perLevelMax, Integer start) {
        List<Long> levelSubList = Lists.newArrayList();
        for (int i = start; i < levelList.size(); i++) {
            List<Long> currentLevelList = levelList.get(i);
            Integer remainNum = perLevelMax - currentLevelList.size();
            if (currentLevelList.size() < perLevelMax) {
                //添加本档位的数量
                levelSubList.addAll(currentLevelList);
                //不够，从下一个档位补全
                List<Long> nextLevelSubList = getLevelSubList(levelList, remainNum, i + 1);
                levelSubList.addAll(nextLevelSubList);
                break;
            } else {
                //够，直接返回
                levelSubList.addAll(currentLevelList.subList(0, perLevelMax));
                break;
            }
        }
        return levelSubList;
    }
}
