
package com.tmerclub.cloud.discount.manager;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.tmerclub.cloud.api.discount.vo.DiscountSumVO;
import com.tmerclub.cloud.combo.service.ComboService;
import com.tmerclub.cloud.common.cache.constant.CacheNames;
import com.tmerclub.cloud.common.cache.util.RedisUtil;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.StatusEnum;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.order.constant.ShopCartItemDiscountType;
import com.tmerclub.cloud.common.order.vo.*;
import com.tmerclub.cloud.common.product.constant.ShopCartSpuActivityType;
import com.tmerclub.cloud.common.product.vo.ComboSpuSkuVO;
import com.tmerclub.cloud.common.product.vo.ComboSpuVO;
import com.tmerclub.cloud.common.product.vo.ComboVO;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.util.BooleanUtil;
import com.tmerclub.cloud.common.util.PriceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 满减活动金额计算
 *
 * @author tmerclub
 */
@Service
public class DiscountConfirmOrderManager {

    private final Logger LOGGER = LoggerFactory.getLogger(DiscountConfirmOrderManager.class);

    @Autowired
    private DiscountManager discountManager;
    @Autowired
    private ComboService comboService;

    /**
     * 计算优惠券和套餐活动
     * @param shopCarts 购物车信息
     * @return
     */
    public List<ShopCartVO> calculateDiscountAndMakeUpShopCartAndAmount(List<ShopCartVO> shopCarts) {
        List<ShopCartVO> newShopCart = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (ShopCartVO shopCart : shopCarts) {
            newShopCart.add(reBuildShopCart(shopCart, shopCart.getShopCartItemDiscounts().get(0).getShopCartItems()));
        }
        return newShopCart;
    }


    /**
     * 校验套餐活动的状态
     * @param shopCartItemDiscountVOList
     * @return
     */
    public ServerResponseEntity checkComboActivity(List<ShopCartItemDiscountVO> shopCartItemDiscountVOList) {
        Set<Long> comboIdSet = shopCartItemDiscountVOList.stream().map(shopCartItemDiscountVO -> shopCartItemDiscountVO.getChooseComboItemVo().getComboId()).collect(Collectors.toSet());
        Map<Long, ComboVO> comboMap = mapComboByComboIds(comboIdSet);

        for (Long comboId : comboIdSet) {
            ComboVO comboVO = comboMap.get(comboId);
            if (Objects.isNull(comboVO) || !Objects.equals(comboVO.getStatus(), 1)) {
                LOGGER.info("套餐状态不可用，comboId:{}", comboId);
                return ServerResponseEntity.showFailMsg("当前选择的套餐活动已经过期，请返回重新提交订单");
            }

            if (comboVO.getEndTime().getTime() < System.currentTimeMillis()) {
                LOGGER.info("套餐已过期，comboId:{}", comboId);
                return ServerResponseEntity.showFailMsg("当前选择的套餐活动已经过期，请返回重新提交订单");
            }
        }
        return ServerResponseEntity.success();
    }

    private ShopCartVO reBuildShopCart(ShopCartVO shopCart, List<ShopCartItemVO> shopCartItems) {
        // 购物车项顺序
        List<Long> cartOrderArr = shopCartItems.stream().map(ShopCartItemVO::getCartItemId).toList();
        // 订单项金额从小到大排序
        shopCartItems = shopCartItems.stream().sorted(Comparator.comparingDouble(ShopCartItemVO::getActualTotal)).collect(Collectors.toList());
        shopCart.setShopReduce(0L);
        shopCart.setDiscountReduce(0L);
        // 初步计算店铺中每个满减的优惠金额，并选择优惠最大的满减活动（自动选择触发条件：立即提交订单或者购物车不指定满减活动）
        // 1.获取总的优惠金额
        // 2.将商品活动进行保存
        DiscountSumVO discountSum = discountManager.calculateDiscount(shopCart.getShopId(), shopCartItems);
        List<ShopCartItemDiscountVO> shopCartItemDiscountVOList = Lists.newArrayList();
        // 对数据经行组装
        // 通过活动项id划分,获取有活动的商品项
        //如果是套餐商品不划分
        for (ShopCartItemVO shopCartItem : shopCartItems) {
            if (Objects.nonNull(shopCartItem.getComboId()) && shopCartItem.getComboId() > 0) {
                shopCartItem.setDiscountId(0L);
            }
        }
        Map<Long, List<ShopCartItemVO>> hasDiscountShopCartItemMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemVO::getDiscountId));
        long reduce = 0L;
        long total = 0L;
        long actualTotal = 0L;
        int totalCount = 0;
        for (Long discountId : hasDiscountShopCartItemMap.keySet()) {
            // 获取该列表
            List<ShopCartItemVO> shopCartItemvos = hasDiscountShopCartItemMap.get(discountId);
            ChooseDiscountItemVO chooseDiscountItemVO = discountManager.getChooseDiscountItemVO(discountSum, discountId);
            long discountAmount = 0L;
            // 计算分摊优惠金额
            for (int index = 0; index < shopCartItemvos.size(); index++) {
                ShopCartItemVO shopCartItem = shopCartItemvos.get(index);
                total += shopCartItem.getTotalAmount();
                actualTotal += shopCartItem.getActualTotal();
                totalCount += shopCartItem.getCount();
                if (!BooleanUtil.isTrue(shopCartItem.getIsChecked())) {
                    continue;
                }

                long shareReduce = 0L;
                // 满减活动过期、删除等原因没有查询到时， 不需要计算满减的金额，直接下一个循环即可
                if (Objects.isNull(chooseDiscountItemVO)) {
                    shopCartItem.setShareReduce(shopCartItem.getShareReduce() + shareReduce);
                    shopCartItem.setActualTotal(shopCartItem.getActualTotal() - shareReduce);
                    continue;
                }

                shareReduce = getShareReduce(shopCartItems, chooseDiscountItemVO, discountAmount, index, shopCartItem);
                discountAmount += shareReduce;
            }
            discountAmount = this.recalculateDiscountAmount(shopCartItems, chooseDiscountItemVO, discountAmount);

            // 满减活动项信息中的优惠金额，更改为计算后实际的优惠金额， 一般情况下都是等于原本优惠金额
            // 极端情况为多个0.01的商品达到了满减的条件，但由于商品金额已经为最小值，无法再扣除优惠，而出现的实际满减金额小于活动设置的满减金额
            if (Objects.nonNull(chooseDiscountItemVO)) {
                chooseDiscountItemVO.setReduceAmount(discountAmount);
            }
            // 计算总的优惠金额，一定要等满减金额计算完成不再变动后再进行总优惠金额的计算
            if (discountAmount > 0) {
                reduce += discountAmount;
            }
            ShopCartItemDiscountVO shopCartItemDiscountVO = new ShopCartItemDiscountVO();
            // 把购物车项的商品顺序还原
            shopCartItemvos.sort(Comparator.comparingInt(prev -> cartOrderArr.indexOf(prev.getCartItemId())));
            Set<Long> comboSkuIds = shopCartItemvos.stream().map(ShopCartItemVO::getMainComboSkuId).filter(mainComboSkuId -> Objects.nonNull(mainComboSkuId)).collect(Collectors.toSet());
            shopCartItemDiscountVO.setComboMainSkuIds(comboSkuIds);
            shopCartItemDiscountVO.setShopCartItems(shopCartItemvos);
            shopCartItemDiscountVO.setChooseDiscountItem(chooseDiscountItemVO);
            shopCartItemDiscountVOList.add(shopCartItemDiscountVO);
        }
        // 倒序排序，将拥有优惠活动的商品放到最上面
        Collections.reverse(shopCartItemDiscountVOList);
        shopCart.setShopCartItemDiscounts(shopCartItemDiscountVOList);
        //获取套餐商品
        buildComboShopCartBefore(shopCart);
        //获取套餐优惠价格
        Long discounts = combDiscounts(shopCart);
        shopCart = getShopCartVO(shopCart, reduce, total, actualTotal, totalCount, discounts);
        return shopCart;
    }

    private static long getShareReduce(List<ShopCartItemVO> shopCartItems, ChooseDiscountItemVO chooseDiscountItemVO, long discountAmount, int index, ShopCartItemVO shopCartItem) {
        long shareReduce = 0L;
        if (index + 1 == shopCartItems.size()) {
            shareReduce = chooseDiscountItemVO.getReduceAmount() - discountAmount;
            // 不是列表中最后一项商品的优惠金额计算 - 按公式计算满减金额即可，偏差值留给最后一步再去计算
        } else {
            // 分摊金额 = 该优惠活动优惠金额 * (商品金额 / 参与该活动的商品总金额)
            shareReduce = PriceUtil.divideByBankerRounding(chooseDiscountItemVO.getReduceAmount() * shopCartItem.getActualTotal(), chooseDiscountItemVO.getProdsPrice());
        }
        // 判断下满减优惠金额是否大于等于商品实际金额，由于商品的最小金额为0.01，所以满减优惠金额必须要小于商品实际金额
        if (shareReduce >= shopCartItem.getActualTotal()) {
            shareReduce = shopCartItem.getActualTotal() - Constant.MIN_SPU_AMOUNT;
        }
        // 第一个优惠就是满减
        shopCartItem.setShareReduce(shopCartItem.getShareReduce() + shareReduce);
        shopCartItem.setDiscountAmount(shareReduce);
        shopCartItem.setActualTotal(shopCartItem.getActualTotal() - shareReduce);
        return shareReduce;
    }

    private long recalculateDiscountAmount(List<ShopCartItemVO> shopCartItemList, ChooseDiscountItemVO chooseDiscountItemDto, long discountAmount) {
        // 没有满减活动的信息， 或者优惠金额已分摊完成
        if (Objects.isNull(chooseDiscountItemDto) || discountAmount >= chooseDiscountItemDto.getReduceAmount()) {
            return discountAmount;
        }
        // 如果实际计算的满减金额都是小于满减活动原本的优惠金额，再重新分摊下剩余的满减金额
        // 按商品实际金额倒序，从金额最大的开始分摊
        shopCartItemList.sort(Comparator.comparing(ShopCartItemVO::getActualTotal).reversed());
        for (ShopCartItemVO shopCartItem : shopCartItemList) {
            // 未勾选不参与满减， 或者满减金额已分摊完毕
            if (!BooleanUtil.isTrue(shopCartItem.getIsChecked()) || discountAmount >= chooseDiscountItemDto.getReduceAmount()) {
                continue;
            }
            // 商品剩余可分摊金额 = 商品实际金额 - 商品满减优惠金额（上面循环中计算的满减金额） - 商品最小金额（最小金额为0.01）
            long canShareReduce = (shopCartItem.getActualTotal() - shopCartItem.getDiscountAmount()) - Constant.MIN_SPU_AMOUNT;
            if (canShareReduce <= 0) {
                continue;
            }
            // 满减剩余可分摊金额
            long shareReduce = chooseDiscountItemDto.getReduceAmount() - discountAmount;
            // 商品剩余可分摊金额 和 满减剩余可分摊金额 取最小值
            shareReduce = Math.min(canShareReduce, shareReduce);
            // 累加优惠金额
            shopCartItem.setShareReduce(shopCartItem.getShareReduce() + shareReduce);
            shopCartItem.setDiscountAmount(shopCartItem.getShareReduce());
            shopCartItem.setActualTotal(shopCartItem.getActualTotal() - shareReduce);
            discountAmount = discountAmount + shareReduce;
        }
        return discountAmount;
    }

    /**
     * 取出购物车中套餐商品
     *
     * @param shopCart
     */
    private void buildComboShopCartBefore(ShopCartVO shopCart) {
        // 获取订单中所有套餐id
        List<ShopCartItemVO> shopCartComboItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 通过购物车生成的订单
        Set<Long> comboIdSet = new HashSet<>(shopCart.getShopCartItemDiscounts().size());

        boolean shopCartOrder = loadComboShopCartItems(shopCartComboItems, comboIdSet, shopCart);
        // 没有套餐活动需要处理
        if (CollUtil.isEmpty(shopCartComboItems)) {
            return;
        }
        // 获取套餐信息
        Map<Long, ComboVO> comboMap = this.mapComboByComboIds(comboIdSet);
        // 立即提交生成的订单，只包含一个套餐，组装套餐数据后直接返回
        if (!shopCartOrder) {
            ComboVO comboVO = comboMap.get(shopCartComboItems.get(0).getComboId());
            ShopCartItemDiscountVO comboShopCartItemDiscountVOList = this.buildComboShopCart(shopCartComboItems, comboVO);
            shopCart.getShopCartItemDiscounts().add(comboShopCartItemDiscountVOList);
            return;
        }

        // 购物车生成的订单，可以根据购物车id来区分不同套餐
        Map<Long, List<ShopCartItemVO>> comboShopCartItemMap = new HashMap<>(shopCartComboItems.size());
        for (ShopCartItemVO shopCartItemVO : shopCartComboItems) {
            Long cartItemId;
            if (Objects.isNull(shopCartItemVO.getParentCartItemId())) {
                cartItemId = shopCartItemVO.getCartItemId();
            } else {
                cartItemId = shopCartItemVO.getParentCartItemId();
            }
            List<ShopCartItemVO> shopCartItemList = comboShopCartItemMap.get(cartItemId);
            if (Objects.isNull(shopCartItemList)) {
                shopCartItemList = new ArrayList<>(Constant.INITIAL_CAPACITY);
                comboShopCartItemMap.put(cartItemId, shopCartItemList);
            }
            shopCartItemList.add(shopCartItemVO);
        }
        List<ShopCartItemDiscountVO> shopCartItemDiscountList = new ArrayList<>(comboShopCartItemMap.size());
        for (Map.Entry<Long, List<ShopCartItemVO>> entry : comboShopCartItemMap.entrySet()) {
            List<ShopCartItemVO> shopCartItemList = entry.getValue();
            ComboVO comboVO = comboMap.get(shopCartItemList.get(0).getComboId());
            ShopCartItemDiscountVO comboShopCartItemDiscountVOList = this.buildComboShopCart(shopCartItemList, comboVO);
            shopCartItemDiscountList.add(comboShopCartItemDiscountVOList);
        }
        // 根据套餐加入时间排序(先加入的放后面，后加入的放前面)
        shopCartItemDiscountList.sort(Comparator.comparing(ShopCartItemDiscountVO::getComboJoinTime).reversed());
        // 套餐活动放在前面, 将其他活动加到套餐的后面
        shopCartItemDiscountList.addAll(shopCart.getShopCartItemDiscounts());
        shopCart.setShopCartItemDiscounts(shopCartItemDiscountList);
    }

    private static boolean loadComboShopCartItems(List<ShopCartItemVO> shopCartComboItems, Set<Long> comboIdSet, ShopCartVO shopCart) {
        ListIterator<ShopCartItemDiscountVO> discountIterator = shopCart.getShopCartItemDiscounts().listIterator();
        boolean shopCartOrder = false;
        while (discountIterator.hasNext()) {
            List<ShopCartItemVO> shopCartItems = discountIterator.next().getShopCartItems();
            Iterator<ShopCartItemVO> shopCartItemsIterator = shopCartItems.iterator();
            while (shopCartItemsIterator.hasNext()) {
                ShopCartItemVO cartItemVO = shopCartItemsIterator.next();
                if (Objects.nonNull(cartItemVO.getComboId()) && cartItemVO.getComboId() > 0) {
                    shopCartComboItems.add(cartItemVO);
                    shopCartItemsIterator.remove();
                }
            }
            if (shopCartComboItems.size() == 0) {
                continue;
            }
            // 加入套餐id
            for (ShopCartItemVO shopCartItem : shopCartComboItems) {
                comboIdSet.add(shopCartItem.getComboId());
                // 订单项中包含主商品的购物车id，证明是由购物车生成的订单
                if (!shopCartOrder && Objects.nonNull(shopCartItem.getParentCartItemId())) {
                    shopCartOrder = true;
                }
            }
            // 活动中的订单项为空，则删除此活动对象；套餐商品会生成新的
            if (CollUtil.isEmpty(shopCart.getShopCartItemDiscounts())) {
                discountIterator.remove();
            }
        }
        return shopCartOrder;
    }

    /**
     * 组装套餐信息
     *
     * @param shopCartComboItems 套餐订单项列表
     * @param comboVO 套餐数据
     * @return
     */
    private ShopCartItemDiscountVO buildComboShopCart(List<ShopCartItemVO> shopCartComboItems, ComboVO comboVO) {
        // 没有发现套餐主商品，说明套餐订单数据错误，抛出异常
        if (Objects.isNull(comboVO) && Objects.equals(comboVO.getStatus(), StatusEnum.ENABLE.value())) {
            throw new LuckException("套餐数据发生变更，请重新下单");
        }
        List<ShopCartItemVO> shopCartItemList = new ArrayList<>(shopCartComboItems.size());
        ShopCartItemDiscountVO shopCartItemDiscountVO = new ShopCartItemDiscountVO();
        shopCartItemDiscountVO.setType(ShopCartItemDiscountType.COMBO.value());
        shopCartItemDiscountVO.setShopCartItems(shopCartItemList);

        ChooseComboItemVo chooseComboItemVo = new ChooseComboItemVo();
        chooseComboItemVo.setComboId(comboVO.getComboId());
        chooseComboItemVo.setName(comboVO.getName());
        shopCartItemDiscountVO.setChooseComboItemVo(chooseComboItemVo);


        // 套餐结束时间小于当前时间，套餐活动已结束
        long currentTime = System.currentTimeMillis();
        if (comboVO.getEndTime().getTime() <= currentTime) {
            throw new LuckException("套餐活动已结束");
        }

        Map<Long, ShopCartItemVO> shopCartItemMap = shopCartComboItems.stream().collect(Collectors.toMap(ShopCartItemVO::getSpuId, Function.identity()));

        // 先放入套餐主商品
        ComboSpuVO mainSpu = comboVO.getMainSpu();
        // 没有发现套餐主商品，说明套餐订单数据错误，抛出异常
        if (!shopCartItemMap.containsKey(mainSpu.getSpuId())) {
            throw new LuckException("套餐数据发生变更，请重新下单");
        }
        ShopCartItemVO mainShopCartItem = shopCartItemMap.get(mainSpu.getSpuId());
        handleComboSpu(mainShopCartItem, mainSpu);
        // 订单套餐活动信息-设置套餐数量
        chooseComboItemVo.setComboCount(mainShopCartItem.getComboCount());
        // 先加入主商品到订单项列表
        shopCartItemList.add(mainShopCartItem);

        // 插入搭配商品，并校验是否必选
        for (ComboSpuVO matchingSpu : comboVO.getMatchingSpus()) {
            if (!shopCartItemMap.containsKey(matchingSpu.getSpuId())) {
                // 搭配商品为必选，但订单项中没有包含
                if (Objects.equals(matchingSpu.getRequired(), 1)) {
                    throw new LuckException("套餐必选商品没有在订单中，请重新下单");
                }
                continue;
            }
            ShopCartItemVO shopCartItemVO = shopCartItemMap.get(matchingSpu.getSpuId());
            handleComboSpu(shopCartItemVO, matchingSpu);
            shopCartItemList.add(shopCartItemVO);
        }

        //重新计算套餐价格
        long comboTotalAmount = 0L;
        //套餐金额(套餐单价)
        long comboAmount = 0L;
        //套餐优惠价格
        long preferentialAmount = 0L;
        for (ShopCartItemVO shopCartItemVO : shopCartItemList) {
            comboTotalAmount += shopCartItemVO.getMatchingPrice() * shopCartItemVO.getCount();
            comboAmount += shopCartItemVO.getMatchingPrice() * shopCartItemVO.getCount();
            //商品sku套餐价格
            //sku原价
            Long skuPriceFee = shopCartItemVO.getSkuPriceFee() * shopCartItemVO.getCount();
            //sku套餐价格
            Long comboSkuPrice = shopCartItemVO.getMatchingPrice() * shopCartItemVO.getCount();
            //单个sku优惠金额
            long onePreferentialAmount = skuPriceFee - comboSkuPrice;
            preferentialAmount = preferentialAmount + onePreferentialAmount;
            shopCartItemVO.setComboAmount(onePreferentialAmount);
            shopCartItemVO.setActualTotal(comboSkuPrice);
            shopCartItemVO.setShareReduce(shopCartItemVO.getShareReduce() + shopCartItemVO.getComboAmount());
        }
        chooseComboItemVo.setComboTotalAmount(comboTotalAmount);
        chooseComboItemVo.setComboAmount(comboAmount);
        chooseComboItemVo.setPreferentialAmount(preferentialAmount);
        shopCartItemDiscountVO.setComboJoinTime(shopCartItemList.get(0).getCreateTime());

        return shopCartItemDiscountVO;
    }

    private static void handleComboSpu(ShopCartItemVO shopCartItem, ComboSpuVO mainSpu) {
        for (ComboSpuSkuVO comboSpuSkuVO : mainSpu.getSkuList()) {
            if (!Objects.equals(comboSpuSkuVO.getSkuId(), shopCartItem.getSkuId())) {
                continue;
            }
            //设置商品套餐价格
            shopCartItem.setMatchingPrice(comboSpuSkuVO.getMatchingPrice());
            // 商品数量 = 套餐数量 * 起搭数量
            shopCartItem.setCount(shopCartItem.getComboCount() * mainSpu.getLeastNum());
        }
    }
    private Map<Long, ComboVO> mapComboByComboIds(Set<Long> comboIdSet) {
        List<Long> comboIds = new ArrayList<>(comboIdSet);
        if (comboIdSet.size() == 1) {
            ComboVO comboVO = comboService.getComboInfoByComboId(comboIds.remove(0));
            return Collections.singletonMap(comboVO.getComboId(), comboVO);
        }

        Map<Long, ComboVO> comboMap = RedisUtil.mapByIds(CacheNames.SINGLE_COMBO_INFO, comboIds, ComboVO.class);
        if (comboMap.size() == comboIds.size()) {
            return comboMap;
        }
        comboIds.forEach(comboId -> {
            if (!comboMap.containsKey(comboId)) {
                comboMap.put(comboId, comboService.getComboInfoByComboId(comboIds.remove(0)));
            }
        });
        return comboMap;
    }

    /**
     * 获取套餐优惠金额
     *
     * @param shopCart
     * @return
     */
    private Long combDiscounts(ShopCartVO shopCart) {
        Long discountsSum = 0L;
        List<ShopCartItemDiscountVO> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
        for (ShopCartItemDiscountVO shopCartItemDiscount : shopCartItemDiscounts) {
            if (Objects.equals(shopCartItemDiscount.getType(), ShopCartSpuActivityType.COMBO.value())) {
                discountsSum += shopCartItemDiscount.getChooseComboItemVo().getPreferentialAmount();
            }
        }
        return discountsSum;
    }


    private ShopCartVO getShopCartVO(ShopCartVO shopCart, long reduce, long total, long actualTotal, int totalCount, Long discounts) {
        shopCart.setTotal(total);
        shopCart.setTotalCount(totalCount);
        shopCart.setActualTotal(actualTotal - reduce - discounts);
        shopCart.setDiscountReduce(reduce);
        // 最开始的店铺满减
        shopCart.setShopReduce(reduce);
        shopCart.setShopReduce(reduce + discounts);
        shopCart.setShopComboAmount(discounts);
        return shopCart;
    }
}
