package com.sonic.frog.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
import com.sonic.frog.dao.AiUserAlbumDao;
import com.sonic.frog.dao.AiUserDao;
import com.sonic.frog.domain.bo.AIUserAlbumGroupBo;
import com.sonic.frog.domain.bo.RandomMeetRateBo;
import com.sonic.frog.domain.entity.AiUser;
import com.sonic.frog.domain.entity.UserAiMeet;
import com.sonic.frog.domain.input.AgeRange;
import com.sonic.frog.domain.input.HomeRecommendInput;
import com.sonic.frog.domain.output.HomeRecommendOutput;
import com.sonic.frog.domain.output.ListAiAlbumOutput;
import com.sonic.frog.enums.AgeTypeEnum;
import com.sonic.frog.service.*;
import com.sonic.frog.utils.LimitUtils;
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.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class HomeRecommendServiceImpl implements HomeRecommendService {

    @Autowired
    private UserAiMeetService userAiMeetService;
    @Autowired
    private AiUserService aiUserService;
    @Autowired
    private AiDictService aiDictService;
    @Autowired
    private AiUserStatService aiUserStatService;
    @Autowired
    private AiUserDao aiUserDao;
    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisKeyUtils redisKeyUtils;
    @Autowired
    private LimitUtils limitUtils;
    @Autowired
    private AiUserAlbumDao aiUserAlbumDao;
    @Autowired
    private AiUserAlbumService userAlbumService;
    @Autowired
    private AiUserHeartbeatRelationService aiUserHeartbeatRelationService;

    @Override
    public List<HomeRecommendOutput> recommendList(Long currentUserId, HomeRecommendInput input) {
        //初始化meet概率数据，并存储到redis中
        initMeetRate(currentUserId);
        //查询出已经匹配上的AI，需要进行排除掉
        if (currentUserId != null) {
            List<Long> exAiIdList = userAiMeetService.list(Wrappers.<UserAiMeet>lambdaQuery().select(UserAiMeet::getAiId).eq(UserAiMeet::getUserId, currentUserId))
                    .stream().map(UserAiMeet::getAiId).collect(Collectors.toList());
            //将前端透传的需要排除的AI列表添加到需要排除的AI列表中
            input.getExList().addAll(exAiIdList);
            //当前端用户没有输入性别，则尝试获取当前用户性别
            if (CollectionUtils.isEmpty(input.getSexList())) {
                //获取当前用户的性别，女推男，男推女
                BaseUserInfoOutput baseUserInfoOutput = userService.baseUserInfo(currentUserId);
                if (baseUserInfoOutput != null && baseUserInfoOutput.getSex() != null) {
                    Integer sex = null;
                    if (baseUserInfoOutput.getSex() != null && baseUserInfoOutput.getSex() == 0) {
                        sex = 1;
                    } else if (baseUserInfoOutput.getSex() != null && baseUserInfoOutput.getSex() == 1) {
                        sex = 0;
                    }
                    //设置性别列表 多选
                    if (sex == null) {
                        input.setSexList(Lists.newArrayList(0,1,2));
                    } else {
                        input.setSexList(Lists.newArrayList(sex));
                    }
                }
            }

        }
        //年龄多选处理
        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);

        log.info("recommendList input:{}", JSON.toJSONString(input));
        //随机查询一批AI列表出来给前端，随机数
        List<Long> randomAiIdList = aiUserDao.randomGetAiUser(currentUserId, input.getExList(), input.getRoleCodeList(), input.getRandom(), input.getPs(), input.getSexList(), input.getAgeRangeList());
        return getHomeRecommendOutputs(currentUserId, randomAiIdList);
    }

    @NotNull
    private List<HomeRecommendOutput> getHomeRecommendOutputs(Long currentUserId, List<Long> randomAiIdList) {
        if (CollectionUtils.isEmpty(randomAiIdList)) {
            return Lists.newArrayList();
        }
        //批量查询AI基础信息出来
        List<AiUser> aiUserList = aiUserService.list(Wrappers.<AiUser>lambdaQuery().in(AiUser::getAiId, randomAiIdList));
        //构造批量字典列表
        Set<String> dictCodeList = Sets.newHashSet();
        aiUserList.forEach(aiUser -> {
            dictCodeList.add(aiUser.getRoleCode());
            dictCodeList.add(aiUser.getCharacterCode());
            dictCodeList.add(aiUser.getTagCode());
        });
        //批量获取字典信息
        Map<String, String> aiDictNameMap = aiDictService.mapNameByCodeList(dictCodeList);
        Map<Long, Integer> aiLikedCountMap = aiUserStatService.queryAiLikedCount(randomAiIdList);
        //批量获取AI的相册列表
        Map<Long, List<ListAiAlbumOutput>> albumListMap = buildAlbumListMap(currentUserId, randomAiIdList);
        //批量获取用户和AI的温度值
        Map<Long, BigDecimal> aiHeartbeatValMap = aiUserHeartbeatRelationService.queryAIHeartbeatVal(currentUserId, randomAiIdList);

        List<HomeRecommendOutput> outputList = Lists.newArrayList();
        aiUserList.forEach(aiUser -> {
            HomeRecommendOutput output = new HomeRecommendOutput();
            output.setAiId(aiUser.getAiId());
            output.setNickname(aiUser.getNickname());
            output.setSex(aiUser.getSex());
            output.setHeadImg(aiUser.getHeadImg());
            //计算年龄
            output.setAge(aiUser.getBirthday() == null ? 0 : Period.between(aiUser.getBirthday().toLocalDate(), LocalDate.now()).getYears());
            output.setRole(aiDictNameMap.get(aiUser.getRoleCode()));
            output.setCharacter(aiDictNameMap.get(aiUser.getCharacterCode()));
            output.setTag(aiDictNameMap.get(aiUser.getTagCode()));
            output.setIntroduction(aiUser.getIntroduction());
            output.setImageUrl(aiUser.getHomeImageUrl());
            //设置点赞数
            output.setLikedCount(aiLikedCountMap.get(aiUser.getAiId()) == null ? 0 : aiLikedCountMap.get(aiUser.getAiId()));
            output.setAlbumList(albumListMap.get(aiUser.getAiId()));
            output.setHeartbeatVal(aiHeartbeatValMap.get(aiUser.getAiId()));
            outputList.add(output);
        });
        //将outputList按照randomAiIdList的顺序进行排列
        outputList.sort(Comparator.comparingInt(o -> randomAiIdList.indexOf(o.getAiId())));
        return outputList;
    }

    /**
     * 批量获取AI的相册列表
     *
     * @param currentUserId
     * @param aiIdList
     * @return
     */
    private Map<Long, List<ListAiAlbumOutput>> buildAlbumListMap(Long currentUserId, List<Long> aiIdList) {
        if (CollectionUtils.isEmpty(aiIdList)) {
            return new HashMap<>(0);
        }
        //批量获取AI的id，然后每个只取出最开始的8个
        List<AIUserAlbumGroupBo> listAlbumsGroup = aiUserAlbumDao.listAlbumsGroup(aiIdList);
        Map<Long, List<Long>> idListMap = new HashMap<>(20);
        List<Long> allIdList = Lists.newArrayList();
        //批量获取基础数据
        for (AIUserAlbumGroupBo bo : listAlbumsGroup) {
            List<String> idList = Lists.newArrayList(bo.getIdListStr().split(","));
            //根据id降序排列
            idList.sort(Comparator.reverseOrder());
            //只获取前8条数据
            idList = idList.subList(0, Math.min(idList.size(), 8));
            List<Long> longIdList = Lists.newArrayList();
            idList.forEach(e -> longIdList.add(Long.valueOf(e)));
            allIdList.addAll(longIdList);
            idListMap.put(bo.getAiId(), longIdList);
        }
        //批量获取图片数据
        List<ListAiAlbumOutput> listByIds = userAlbumService.listByIds(currentUserId, allIdList);
        //先根据id进行降序排列，然后根据aiId进行分组处理
        listByIds.sort(Comparator.comparing(ListAiAlbumOutput::getAlbumId).reversed());
        //根据用户分组，然后根据id进行降序排列
        Map<Long, List<ListAiAlbumOutput>> listMap = listByIds.stream().collect(Collectors.groupingBy(ListAiAlbumOutput::getAiId));
        return listMap;
    }


    @Override
    public HomeRecommendOutput getAiMeetDetail(Long currentUserId, Long aiId) {
        List<HomeRecommendOutput> list = getHomeRecommendOutputs(currentUserId, Lists.newArrayList(aiId));
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    /**
     * 初始化meet概率数据
     *
     * @param currentUserId
     */
    private void initMeetRate(Long currentUserId) {
        if (currentUserId == null) {
            return;
        }
        //判断请求次数
        int requestCount = limitUtils.defaultLimitCheckReturnCount(redisKeyUtils.meetSdCount(currentUserId), 2 * 60 * 60);
        log.info("===> initMeetRate requestCount : {}", requestCount);
        //第一次请求的话，初始化概率数据，并存储到redis中
        if (requestCount == 1) {
            //生成概率数据，并存到redis中
            RandomMeetRateBo randomMeetRateBo = RandomMeetRateBo.builder()
                    .bl1120(generateRandom(1, 100))
                    .bl3140(generateRandom(1, 30))
                    .bl4150(generateRandom(1, 20))
                    .bl5160(generateRandom(1, 10))
                    .bl6170(generateRandom(1, 5))
                    .build();
            String redisValue = JSONObject.toJSONString(randomMeetRateBo).trim();
            log.info("===> initMeetRate redisValue : {}", redisValue);
            stringRedisTemplate.opsForValue().set(redisKeyUtils.meetRateBoKey(currentUserId), redisValue, 2 * 60 * 60, TimeUnit.SECONDS);
        }
    }

    /**
     * 生成随机数是否命中
     *
     * @param startNumber
     * @param endNumber
     * @return
     */
    public boolean generateRandom(int startNumber, int endNumber) {
        Random random = new Random();
        // 生成1-100之间的随机数
        int randomNumber = random.nextInt(100) + 1;
        if (randomNumber >= startNumber && randomNumber <= endNumber) {
            return true;
        }
        return false;
    }

}
