package com.liquidnet.service.platform.service.impl.candy;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.common.cache.redis.util.AbstractRedisUtil;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.base.OrderCloseMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.candy.constant.CandyRedisConst;
import com.liquidnet.service.candy.dto.CandyCommonCouponBasicDto;
import com.liquidnet.service.candy.dto.CandyCouponRuleDto;
import com.liquidnet.service.candy.dto.CandyUserCouponBasicDto;
import com.liquidnet.service.candy.entity.CandyUserCoupon;
import com.liquidnet.service.candy.mapper.CandyCommonCouponMapper;
import com.liquidnet.service.candy.mapper.CandyCouponRuleMapper;
import com.liquidnet.service.candy.mapper.CandyUserCouponMapper;
import com.liquidnet.service.platform.utils.ObjectUtil;
import com.liquidnet.service.platform.utils.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PlatformCandyUserCouponService extends ServiceImpl<CandyUserCouponMapper, CandyUserCoupon> {
    @Autowired
    private CandyUserCouponMapper candyUserCouponMapper;
    @Autowired
    private CandyCouponRuleMapper candyCouponRuleMapper;
    @Autowired
    private CandyCommonCouponMapper candyCommonCouponMapper;

    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private RedisDataSourceUtil redisDataSourceUtil;

    /**
     * @param uCouponUidList 内容 uCouponId,uid List
     * @param uCouponIdList  内容 uCouponId List
     * @return
     */
    public Boolean useBackCoupon(ArrayList<String> uCouponUidList, ArrayList<String> uCouponIdList) {
        //修改数据库
        CandyUserCoupon userCoupon = CandyUserCoupon.getNew();
        userCoupon.setState(1);
        this.update(userCoupon, new UpdateWrapper<CandyUserCoupon>().in("ucoupon_id", uCouponIdList));
        //sendRedis
        int forSize = 500;
        int forCount = uCouponUidList.size() % forSize == 0 ? uCouponUidList.size() / forSize : (uCouponUidList.size() / forSize) + 1;
        for (int i = 0; i < forCount; i++) {
            LinkedList<String> mqList = new LinkedList<>();
            for (int y = 0; y < forSize; y++) {
                try {
                    String t = uCouponUidList.get(i * 500 + y);
                    mqList.add(t);
                } catch (Exception e) {
                    break;
                }
            }
            queueUtils.sendMsgByCandyRedis(MQConst.CandyQueue.COUPON_ORDER_BACK.getKey(), OrderCloseMapping.get(mqList));
        }
        return true;
    }

    public List<CandyUserCouponBasicDto> ucouponBasicDtoByUidProcessing(String uid) {
        AbstractRedisUtil redisAdamUtil = redisDataSourceUtil.getRedisAdamUtil();
        if (null == redisAdamUtil.get(AdamRedisConst.INFO_USER.concat(uid))) return null;

        List<CandyUserCouponBasicDto> basicDtoList = candyUserCouponMapper.selectMultiForUserCouponBasicDto(uid);
        if (!CollectionUtils.isEmpty(basicDtoList)) {
            List<String> couponIdList = basicDtoList.stream().map(CandyUserCouponBasicDto::getCouponId).collect(Collectors.toList());

            List<CandyCouponRuleDto> couponRuleDtoList = candyCouponRuleMapper.selectListForCouponRuleDto(couponIdList);

            Map<String, List<CandyCouponRuleDto>> couponRuleDtoListMap = couponRuleDtoList.stream().collect(Collectors.groupingBy(CandyCouponRuleDto::getCouponId));

//            Map<String, List<CandyUserCouponBasicDto>> basicDtoListMap = basicDtoList.stream().collect(Collectors.groupingBy(CandyUserCouponBasicDto::getCouponId));

            basicDtoList.forEach(dto -> {
                dto.setUseRules(couponRuleDtoListMap.get(dto.getCouponId()));
            });


            AbstractRedisUtil redisCandyUtil = redisDataSourceUtil.getRedisCandyUtil();
            redisCandyUtil.set(CandyRedisConst.BASIC_USER_COUPON.concat(uid), basicDtoList);
        }
        return basicDtoList;
    }

    public List<CandyCommonCouponBasicDto> ccouponBasicDtoByUidProcessing(LocalDateTime ucreatedAt) {
        List<CandyCommonCouponBasicDto> basicDtoList = candyCommonCouponMapper.selectMultiForCommonCouponBasicDto(ucreatedAt);
        if (!CollectionUtils.isEmpty(basicDtoList)) {
            List<String> couponIdList = basicDtoList.stream().map(CandyCommonCouponBasicDto::getCouponId).collect(Collectors.toList());

            List<CandyCouponRuleDto> couponRuleDtoList = candyCouponRuleMapper.selectListForCouponRuleDto(couponIdList);

            Map<String, List<CandyCouponRuleDto>> couponRuleDtoListMap = couponRuleDtoList.stream().collect(Collectors.groupingBy(CandyCouponRuleDto::getCouponId));

            basicDtoList.forEach(dto -> {
                dto.setUseRules(couponRuleDtoListMap.get(dto.getCouponId()));
            });

            redisDataSourceUtil.getRedisCandyUtil().set(CandyRedisConst.BASIC_COMMON_COUPON, basicDtoList);
        }
        return basicDtoList;
    }

    /**
     * 移除私有券
     * - 处理已过期且过期天数超过`displayDays`的券
     *
     * @param currentDateTime 当前时间
     * @param displayDays     展示天数
     * @param usedDisplayFlg  已使用券逻辑处理
     */
    public int rmvForUserCouponProcessing(LocalDateTime currentDateTime, int displayDays, boolean usedDisplayFlg) {
        AtomicInteger pl = new AtomicInteger();
        try {
            LambdaQueryWrapper<CandyUserCoupon> userCouponQueryWrapper = Wrappers.lambdaQuery(CandyUserCoupon.class);
            if (usedDisplayFlg) {
                userCouponQueryWrapper.eq(CandyUserCoupon::getState, 5);
                userCouponQueryWrapper.le(CandyUserCoupon::getUsedAt, currentDateTime.minusSeconds(displayDays));
            } else {
                userCouponQueryWrapper.eq(CandyUserCoupon::getState, 3);
                userCouponQueryWrapper.le(CandyUserCoupon::getDuedAt, currentDateTime.minusSeconds(displayDays));
            }
            int totalCount = this.count(userCouponQueryWrapper);
            int remainCount = totalCount, pSize = 1000;
            if (totalCount > 0) {
                log.info("券移除处理:私有券[总数:{},AT:{} - {} = {}] >>> BEGIN BEGIN BEGIN", totalCount, currentDateTime, displayDays, currentDateTime.minusDays(displayDays));
            }

            ArrayList<CandyUserCoupon> updateUserCouponList = ObjectUtil.getCandyUserCouponArrayList();
            userCouponQueryWrapper.select(CandyUserCoupon::getMid, CandyUserCoupon::getUcouponId, CandyUserCoupon::getUid);
            userCouponQueryWrapper.orderByAsc(CandyUserCoupon::getMid);
            String operator = usedDisplayFlg ? "RMV-USED" : "RMV-DUED";
            while (remainCount > 0) {
                updateUserCouponList.clear();

                LambdaQueryWrapper<CandyUserCoupon> userCouponLambdaQueryWrapper = userCouponQueryWrapper.clone();
                String lastLimitSql = "LIMIT " + 0 + "," + pSize;
                userCouponLambdaQueryWrapper.last(lastLimitSql);
                List<CandyUserCoupon> userCouponList = this.list(userCouponLambdaQueryWrapper);

                for (int i = 0, listSize = CollectionUtils.isEmpty(userCouponList) ? -1 : userCouponList.size(); i < listSize; i++) {
                    CandyUserCoupon userCoupon = userCouponList.get(i);
                    userCoupon.setState(9);
                    userCoupon.setOperator(operator);
                    userCoupon.setUpdatedAt(currentDateTime);

                    updateUserCouponList.add(userCoupon);
                }

                if (!CollectionUtils.isEmpty(updateUserCouponList)) {
                    if (this.updateBatchById(updateUserCouponList, updateUserCouponList.size())) {
                        AbstractRedisUtil redisCandyUtil = redisDataSourceUtil.getRedisCandyUtil();
                        // 需要更新的用户券记录按UID分组处理
                        Map<String, List<CandyUserCoupon>> userCouponListByUidMap = updateUserCouponList.stream().collect(Collectors.groupingBy(CandyUserCoupon::getUid));
                        userCouponListByUidMap.forEach((uid, updateUserCouponListByUid) -> {
                            String uckey = CandyRedisConst.BASIC_USER_COUPON.concat(uid);
                            List<CandyUserCouponBasicDto> vos = (List<CandyUserCouponBasicDto>) redisCandyUtil.get(uckey);

                            if (!CollectionUtils.isEmpty(vos)) {
                                List<String> updateUserCouponIdListByUid = updateUserCouponListByUid.stream().map(CandyUserCoupon::getUcouponId).collect(Collectors.toList());
                                int beforeSizeVos = vos.size();
                                vos.removeIf(r -> updateUserCouponIdListByUid.contains(r.getUcouponId()));

                                if (beforeSizeVos - vos.size() > 0) {
                                    pl.getAndAdd(beforeSizeVos - vos.size());
                                    if (vos.size() > 0) {
                                        redisCandyUtil.set(uckey, vos);
                                    } else {
                                        redisCandyUtil.del(uckey);
                                    }
                                }
                            }
                        });
                    } else {
                        throw new LiquidnetServiceException("-1", "券移除数据更新失败");
                    }
                }

                remainCount = Math.max(remainCount - pSize, 0);
                log.info("券移除处理中:私有券[总数:{},单次处理:{}|{},剩余:{}]", totalCount, lastLimitSql, pl.get(), remainCount);
            }
            if (totalCount > 0) {
                log.info("券移除处理:私有券[总数:{},处理总数:{}] >>> END END END", totalCount, pl.get());
            }
        } catch (Exception e) {
            log.error("Ex.券移除处理异常:私有券[currentDateTime={},displayDays={},usedDisplayFlg={}]", currentDateTime, displayDays, usedDisplayFlg, e);
        }
        return pl.get();
    }
}
