package com.liquidnet.service.platform.controller.candy.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.common.exception.constant.ErrorCode;
import com.liquidnet.commons.lang.util.DESUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.candy.constant.CandyRedisConst;
import com.liquidnet.service.candy.dto.CandyCommonCouponBasicDto;
import com.liquidnet.service.candy.dto.CandyCouponCodeDto;
import com.liquidnet.service.candy.dto.CandyUserCouponBasicDto;
import com.liquidnet.service.candy.entity.CandyCommonCoupon;
import com.liquidnet.service.candy.entity.CandyCoupon;
import com.liquidnet.service.candy.entity.CandyCouponCode;
import com.liquidnet.service.candy.entity.CandyUserCoupon;
import com.liquidnet.service.platform.service.impl.candy.PlatformCandyCommonCouponService;
import com.liquidnet.service.platform.service.impl.candy.PlatformCandyCouponCodeService;
import com.liquidnet.service.platform.service.impl.candy.PlatformCandyCouponService;
import com.liquidnet.service.platform.service.impl.candy.PlatformCandyUserCouponService;
import com.liquidnet.service.platform.utils.ObjectUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

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

/**
 * <p>
 * 券到期检查任务
 * </p>
 * - 兑换类
 * --- 检查[coupon.redeem_stop]
 * - 发放类
 * --- 公有券：检查[coupon.expire_at]
 * --- 私有券：检查[user_coupon.dued_at]
 *
 * @author zhanggb
 * Created by IntelliJ IDEA at 2021/9/1
 */
@Slf4j
@RestController
@RequestMapping("ccoupon/task/due")
public class CandyCouponDueTaskController {
    @Autowired
    private RedisDataSourceUtil redisDataSourceUtil;
    @Autowired
    private PlatformCandyCouponService platformCandyCouponService;
    @Autowired
    private PlatformCandyCouponCodeService platformCandyCouponCodeService;
    @Autowired
    private PlatformCandyCommonCouponService platformCandyCommonCouponService;
    @Autowired
    private PlatformCandyUserCouponService platformCandyUserCouponService;

    private static final String OPERATOR_CHECK = "CHECK";

    /**
     * <p>
     * 兑换类
     * </p>
     * - 更新兑换码状态
     * - 同步REDIS.DTO
     *
     * @return ResponseDto<String>
     */
    @PutMapping("redeem")
    public ResponseDto<String> processForRedeem() {
        LocalDateTime now = LocalDateTime.now();

        // 查取状态可用的到期兑换码
        LambdaQueryWrapper<CandyCouponCode> couponCodeQueryWrapper = Wrappers.lambdaQuery(CandyCouponCode.class);
        couponCodeQueryWrapper.eq(CandyCouponCode::getState, 0);
        couponCodeQueryWrapper.le(CandyCouponCode::getRedeemStop, now.minusSeconds(30));
        int totalCount = platformCandyCouponCodeService.count(couponCodeQueryWrapper);
        AtomicInteger pl = new AtomicInteger();
        int remainCount = totalCount;
        int num = 0, pSize = 1000;
        if (totalCount > 0) {
            log.info("券码到期检查处理:兑换码[到期总记录数:{},AT:{}] >>> BEGIN BEGIN BEGIN", totalCount, now);
        }

        ArrayList<CandyCouponCode> updateCouponCodeList = ObjectUtil.getCandyCouponCodeArrayList();
        couponCodeQueryWrapper.select(CandyCouponCode::getMid, CandyCouponCode::getCcode);
        couponCodeQueryWrapper.orderByAsc(CandyCouponCode::getMid);
        while (remainCount > 0) {
            updateCouponCodeList.clear();

            LambdaQueryWrapper<CandyCouponCode> couponCodeLambdaQueryWrapper = couponCodeQueryWrapper.clone();
            String lastLimitSql = "LIMIT " + 0 + "," + pSize;
            couponCodeLambdaQueryWrapper.last(lastLimitSql);
            List<CandyCouponCode> couponCodeList = platformCandyCouponCodeService.list(couponCodeLambdaQueryWrapper);

            int listSize = CollectionUtils.isEmpty(couponCodeList) ? -1 : couponCodeList.size();
            for (int i = 0; i < listSize; i++) {
                CandyCouponCode couponCode = couponCodeList.get(i);

                couponCode.setState(3);
                couponCode.setOperator(OPERATOR_CHECK);
                couponCode.setUpdatedAt(now);

                updateCouponCodeList.add(couponCode);
            }

            if (!CollectionUtils.isEmpty(updateCouponCodeList)) {
                if (platformCandyCouponCodeService.updateBatchById(updateCouponCodeList, updateCouponCodeList.size())) {
                    AbstractRedisUtil redisCandyUtil = redisDataSourceUtil.getRedisCandyUtil();
                    updateCouponCodeList.forEach(r -> {
                        String ccKey = CandyRedisConst.BASIC_COUPON_CODE.concat(r.getCcode());

                        CandyCouponCodeDto dto = (CandyCouponCodeDto) redisCandyUtil.get(ccKey);

                        if (null != dto) {
                            dto.setState(3);
//                            redisCandyUtil.set(ccKey, dto);
                            redisCandyUtil.set(ccKey, dto, 604800);
                        }

                        pl.getAndIncrement();
                    });
                } else {
                    throw new LiquidnetServiceException("-1", "券码到期处理失败");
                }
            }

            num++;
            remainCount -= pSize;
            log.info("券码到期检查处理中:兑换码[到期总记录数:{},单次处理:{},剩余处理数:{}]", totalCount, lastLimitSql, remainCount);
        }
        if (totalCount > 0) {
            log.info("券码到期检查处理:兑换码[到期总记录数:{},处理总数:{}] >>> END END END", totalCount, pl.get());
        }
        return ResponseDto.success();
    }

    /**
     * <p>
     * 发放类-公有券
     * </p>
     * - 更新公有券状态
     * - 同步REDIS.DTO
     *
     * @return ResponseDto<String>
     */
    @PutMapping("common")
    public ResponseDto<String> processForCommon() {
        LocalDateTime now = LocalDateTime.now();

        // 查取状态可用的公有券
        LambdaQueryWrapper<CandyCommonCoupon> commonCouponQueryWrapper = Wrappers.lambdaQuery(CandyCommonCoupon.class);
        commonCouponQueryWrapper.eq(CandyCommonCoupon::getState, 1);
        commonCouponQueryWrapper.select(CandyCommonCoupon::getMid, CandyCommonCoupon::getCcouponId, CandyCommonCoupon::getCouponId);
        List<CandyCommonCoupon> commonCouponList = platformCandyCommonCouponService.list(commonCouponQueryWrapper);
        log.info("券到期检查处理:公有券[可用券总数:{},AT:{}] >>> BEGIN BEGIN BEGIN", commonCouponList.size(), now);

        if (!CollectionUtils.isEmpty(commonCouponList)) {
            List<CandyCommonCoupon> updateCommonCouponList = new ArrayList<>();

            // 查取状态可用的公有券 - 券ID集
            Object[] commonCouponIdArr = commonCouponList.stream().map(CandyCommonCoupon::getCouponId).toArray();
            Map<String, List<CandyCommonCoupon>> commonCouponIdToListMap = commonCouponList.stream().collect(Collectors.groupingBy(CandyCommonCoupon::getCouponId));

            // 查取状态可用的公有券 - 对应的到期券列表
            LambdaQueryWrapper<CandyCoupon> couponQueryWrapper = Wrappers.lambdaQuery(CandyCoupon.class);
            couponQueryWrapper.in(CandyCoupon::getCouponId, commonCouponIdArr);
            couponQueryWrapper.le(CandyCoupon::getExpireAt, now.minusSeconds(30));
            int totalCount = platformCandyCouponService.count(couponQueryWrapper), remainCount = totalCount, num = 0, pSize = 1000;
            log.info("券到期检查处理:公有券[可用券总数:{},其中到期券ID数:{}]", commonCouponList.size(), totalCount);

            couponQueryWrapper.select(CandyCoupon::getMid, CandyCoupon::getCouponId);
            couponQueryWrapper.orderByAsc(CandyCoupon::getMid);
            while (remainCount > 0) {
                LambdaQueryWrapper<CandyCoupon> couponLambdaQueryWrapper = couponQueryWrapper.clone();
                String lastLimitSql = "LIMIT " + 0 + "," + pSize;
                couponLambdaQueryWrapper.last(lastLimitSql);
                List<CandyCoupon> couponList = platformCandyCouponService.list(couponLambdaQueryWrapper);

                int listSize = CollectionUtils.isEmpty(couponList) ? -1 : couponList.size();
                for (int i = 0; i < listSize; i++) {
                    CandyCoupon coupon = couponList.get(i);

                    List<CandyCommonCoupon> inCommonCouponList = commonCouponIdToListMap.get(coupon.getCouponId());
                    inCommonCouponList.forEach(r -> {
                        r.setState(3);
                        r.setOperator(OPERATOR_CHECK);
                        r.setUpdatedAt(now);

                        updateCommonCouponList.add(r);
                    });
                }

                num++;
                remainCount -= pSize;
            }

            if (!CollectionUtils.isEmpty(updateCommonCouponList)) {
                if (platformCandyCommonCouponService.updateBatchById(updateCommonCouponList, updateCommonCouponList.size())) {
                    AbstractRedisUtil redisCandyUtil = redisDataSourceUtil.getRedisCandyUtil();
                    String ccKey = CandyRedisConst.BASIC_COMMON_COUPON;
                    List<CandyCommonCouponBasicDto> vos = (List<CandyCommonCouponBasicDto>) redisCandyUtil.get(ccKey);

                    if (!CollectionUtils.isEmpty(vos)) {
                        Map<String, CandyCommonCouponBasicDto> vosMap = vos.stream().collect(Collectors.toMap(CandyCommonCouponBasicDto::getCcouponId, t -> t));
                        updateCommonCouponList.forEach(r -> {
                            vos.removeIf(v -> v.getCcouponId().equals(r.getCcouponId()));

                            CandyCommonCouponBasicDto commonCouponBasicDto = vosMap.get(r.getCcouponId());
                            commonCouponBasicDto.setState(3);
                            vos.add(commonCouponBasicDto);
                        });

                        redisCandyUtil.set(ccKey, vos);
                    }
                } else {
                    throw new LiquidnetServiceException("-1", String.format("券到期处理失败[updateCommonCouponList.size=%s]", updateCommonCouponList.size()));
                }
            }
            log.info("券到期检查处理:公有券[可用券总数:{},到期券ID数:{},到期券处理数:{}] >>> END END END", commonCouponList.size(), totalCount, updateCommonCouponList.size());
        }
        return ResponseDto.success();
    }

    /**
     * <p>
     * 发放类-私有券
     * </p>
     * - 更新用户券状态
     * - 同步REDIS.DTO
     *
     * @return ResponseDto<String>
     */
    @PutMapping("user")
    public ResponseDto<String> processForUser() {
        LocalDateTime now = LocalDateTime.now();

        // 查取状态可用的到期券
        LambdaQueryWrapper<CandyUserCoupon> userCouponQueryWrapper = Wrappers.lambdaQuery(CandyUserCoupon.class);
        userCouponQueryWrapper.eq(CandyUserCoupon::getState, 1);
        userCouponQueryWrapper.le(CandyUserCoupon::getDuedAt, now.minusSeconds(30));
        int totalCount = platformCandyUserCouponService.count(userCouponQueryWrapper);
        AtomicInteger pl = new AtomicInteger();
        int remainCount = totalCount;
        int num = 0, pSize = 1000;
        log.info("券到期检查处理:私有券[到期总记录数:{},AT:{}] >>> BEGIN BEGIN BEGIN", totalCount, now);

        ArrayList<CandyUserCoupon> updateUserCouponList = ObjectUtil.getCandyUserCouponArrayList();
        userCouponQueryWrapper.select(CandyUserCoupon::getMid, CandyUserCoupon::getUcouponId, CandyUserCoupon::getUid);
        userCouponQueryWrapper.orderByAsc(CandyUserCoupon::getMid);
        while (remainCount > 0) {
            updateUserCouponList.clear();

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

            int listSize = CollectionUtils.isEmpty(userCouponList) ? -1 : userCouponList.size();
            for (int i = 0; i < listSize; i++) {
                CandyUserCoupon userCoupon = userCouponList.get(i);

                userCoupon.setState(3);
                userCoupon.setOperator(OPERATOR_CHECK);
                userCoupon.setUpdatedAt(now);

                updateUserCouponList.add(userCoupon);
            }

            if (!CollectionUtils.isEmpty(updateUserCouponList)) {
                if (platformCandyUserCouponService.updateBatchById(updateUserCouponList, updateUserCouponList.size())) {
                    // 需要更新的用户券记录按UID分组处理
                    Map<String, List<CandyUserCoupon>> userCouponListByUidMap = updateUserCouponList.stream().collect(Collectors.groupingBy(CandyUserCoupon::getUid));

                    AbstractRedisUtil redisCandyUtil = redisDataSourceUtil.getRedisCandyUtil();
                    userCouponListByUidMap.forEach((uid, updateUserCouponListByUid) -> {
                        String uckey = CandyRedisConst.BASIC_USER_COUPON.concat(uid);
                        List<CandyUserCouponBasicDto> vos = (List<CandyUserCouponBasicDto>) redisCandyUtil.get(uckey);

                        if (!CollectionUtils.isEmpty(vos)) {
                            updateUserCouponListByUid.forEach(r -> {
                                int idx = IntStream.range(0, vos.size())
                                        .filter(i -> vos.get(i).getUcouponId().equals(r.getUcouponId()))
                                        .findFirst().orElse(-1);
                                if (idx != -1) {
                                    CandyUserCouponBasicDto dto = vos.get(idx);
                                    dto.setState(3);
                                    vos.set(idx, dto);
                                }
                                pl.getAndIncrement();
                            });
                            redisCandyUtil.set(uckey, vos);
                        }
                    });
                } else {
                    throw new LiquidnetServiceException("-1", String.format("券到期处理失败[updateUserCouponList.size=%s]", updateUserCouponList.size()));
                }
            }

            num++;
            remainCount -= pSize;
            log.info("券到期检查处理中:私有券[到期总记录数:{},单次处理:{},剩余处理数:{}]", totalCount, lastLimitSql, remainCount);
        }
        log.info("券到期检查处理:私有券[到期总记录数:{},处理总数:{}] >>> END END END", totalCount, pl.get());
        return ResponseDto.success();
    }

    /**
     * 券移除-`过期券/已用券`超过指定时长移除不展示
     * - 过期券：更新MySQL用户券状态，删除Redis用户指定过期时段的券
     * - 已用券：暂不处理
     * 同步REDIS.DTO
     *
     * @param displaySeconds 有效展示秒数，例如："2678400,31622400"，其中2678400为过期券对应有效展示秒数(31天)，31622400为已用券对应有效展示秒数(366天)，其中31622400可以不传即不处理已用券，2678400必传，否则执行无效
     * @return ResponseDto<String>
     */
    @PostMapping("rmv")
    public ResponseDto<String> processForRmv(@RequestParam String displaySeconds) {
        int displayDaysIntVal1 = 31, displayDaysIntVal2 = 0;
        ErrorCode paramError = ErrorCode.HTTP_PARAM_ERROR;
        if (StringUtils.isNotBlank(displaySeconds)) {
            try {
                String decrypt = DESUtils.DES().decrypt(displaySeconds);
                String[] decryptArr = decrypt.split(",");
                displayDaysIntVal1 = Integer.parseInt(decryptArr[0]);
                displayDaysIntVal2 = decryptArr.length > 1 && StringUtils.isNotBlank(decryptArr[1]) ? Integer.parseInt(decryptArr[1]) : 0;
            } catch (NumberFormatException ignored) {
            } catch (Exception e) {
                return ResponseDto.failure(paramError.getCode(), paramError.getMessage());
            }
            if (displayDaysIntVal1 <= 0) {
                return ResponseDto.failure(paramError.getCode(), paramError.getMessage());
            }
        } else {
            return ResponseDto.failure(paramError.getCode(), paramError.getMessage());
        }
        LocalDateTime currentDateTime = LocalDateTime.now();

        int plCommonCoupon = platformCandyCommonCouponService.rmvForCommonCouponProcessing(currentDateTime, displayDaysIntVal1);
        int plUserCouponDue = platformCandyUserCouponService.rmvForUserCouponProcessing(currentDateTime, displayDaysIntVal1, false);
        int plUserCouponUse = 0;
        if (displayDaysIntVal2 > 31536000) {// 31536000秒对应365天
            plUserCouponUse = platformCandyUserCouponService.rmvForUserCouponProcessing(currentDateTime, displayDaysIntVal2, true);
        }
        return ResponseDto.success(String.format("结果[%s,%d,%d,过期公有券:%d,过期私有券:%d,已用券:%d", currentDateTime, displayDaysIntVal1, displayDaysIntVal2, plCommonCoupon, plUserCouponDue, plUserCouponUse));
    }
}
