/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.api.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.app.dto.ShopCartOrderDto;
import com.yami.shop.bean.app.dto.ShopCartOrderMergerDto;
import com.yami.shop.bean.enums.OrderStatus;
import com.yami.shop.bean.enums.OrderType;
import com.yami.shop.bean.event.SubmitOrderEvent;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.order.SubmitOrderOrder;
import com.yami.shop.common.enums.PayType;
import com.yami.shop.common.exception.GlobalException;
import com.yami.shop.common.util.Arith;
import com.yami.shop.dao.*;
import com.yami.shop.security.api.util.SecurityUtils;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.SkuService;
import com.yami.shop.service.UserAddrOrderService;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 确认订单信息时的默认操作
 *
 * @author LGH
 */
@Component("defaultSubmitOrderListener")
@AllArgsConstructor
public class SubmitOrderListener {

    private final MapperFacade mapperFacade;

    private final UserAddrOrderService userAddrOrderService;

    private final ProductService productService;

    private final SkuService skuService;

    private final Snowflake snowflake;

    private final PointsRecordMapper pointsRecordMapper;

    private final OrderSettlementMapper orderSettlementMapper;

    private final BasketMapper basketMapper;

    /**
     * 计算订单金额
     */
    @EventListener(SubmitOrderEvent.class)
    @Order(SubmitOrderOrder.DEFAULT)
    @Transactional(rollbackFor = Exception.class)
    public void defaultSubmitOrderListener(SubmitOrderEvent event) {
        Date now = new Date();
        String userId = SecurityUtils.getUser().getUserId();

        ShopCartOrderMergerDto mergerOrder = event.getMergerOrder();

        // 订单商品参数
        List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders();

        List<Long> basketIds = new ArrayList<>();
        // 商品skuid为key 需要更新的sku为value的map
        Map<Long, Sku> skuStocksMap = new HashMap<>(16);
        // 商品productid为key 需要更新的product为value的map
        Map<Long, Product> prodStocksMap = new HashMap<>(16);

        // 把订单地址保存到数据库
        UserAddrOrder userAddrOrder = mapperFacade.map(mergerOrder.getUserAddr(), UserAddrOrder.class);
        if (userAddrOrder == null) {
            throw new GlobalException("请填写收货地址");
        }
        userAddrOrder.setUserId(userId);
        userAddrOrder.setCreateTime(now);
        userAddrOrderService.save(userAddrOrder);
        //所有当前用户可用的积分记录
        List<PointsRecord> pointsRecords = pointsRecordMapper.selectList(new LambdaQueryWrapper<PointsRecord>()
                        .eq(PointsRecord::getUserId, userId)
                        .eq(PointsRecord::getChannelId, mergerOrder.getChannelId())
                        .in(PointsRecord::getPointsType, Arrays.asList(1, 3))
                        .in(PointsRecord::getPointsAudit, Arrays.asList(1, 2)).and(wrapper -> wrapper
                                .gt(PointsRecord::getExpiryDate, LocalDateTime.now())
                                .or()
                                .isNull(PointsRecord::getExpiryDate)
                        )).stream()
                .sorted(Comparator
                        .comparing(PointsRecord::getExpiryDate,
                                Comparator.nullsLast(Comparator.naturalOrder()))
                        .thenComparing(PointsRecord::getId))
                .collect(Collectors.toList());
        ;
        // 将记录添加到队列中
        ArrayDeque<PointsRecord> expiryQueue = new ArrayDeque<>(pointsRecords);
        // 订单地址id
        Long addrOrderId = userAddrOrder.getAddrOrderId();

        if (CollectionUtils.isNotEmpty(shopCartOrders)) {
            // 每个店铺生成一个订单
            for (ShopCartOrderDto shopCartOrderDto : shopCartOrders) {
                //兑换人民币是分,必须要实时统计计算
                Long point = pointsRecordMapper.statisticsPoint(userId, mergerOrder.getChannelId());
                // 使用雪花算法生成的订单号
                String orderNumber = "FD" + snowflake.nextId();
                shopCartOrderDto.setOrderNumber(orderNumber);

                Long shopId = shopCartOrderDto.getShopId();
                // 订单信息
                com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order();
                order.setIsPayed(0);
                order.setHbOrderStatus(OrderStatus.UNPAY.value());
                order.setOrderType(OrderType.ORDINARY.value());
                //这个运费是已经算过的了
                double actualTotal = shopCartOrderDto.getActualTotal();
                //企业用户才进这个判断,并且该用户的积分必须大于0
                if (null != point && point > 0) {
                    order.setOrderType(OrderType.SCORE.value());
                    // 计算商品金额和积分
                    Double mul = Arith.mul(actualTotal, 100);
                    Double vp = Double.valueOf(point);
                    if (vp >= mul) {
                        order.setPayType(PayType.SCOREPAY.value());
                        order.setIsPayed(1);
                        order.setPayTime(new Date());
                        order.setHbOrderStatus(OrderStatus.CONSIGNMENT.value());
                        //积分完全足够支付
                        order.setOffsetPoints(Long.valueOf(String.valueOf(mul).split("\\.")[0]));
                        actualTotal = 0.0;//剩下需要付的钱
                    } else {
                        //积分不够抵扣
                        double sub = Arith.sub(mul, vp);
                        order.setOffsetPoints(point);
                        //剩下需要付的钱
                        actualTotal = sub / 100;
                        mergerOrder.setNextPay(Boolean.TRUE);
                    }
                } else {
                    mergerOrder.setNextPay(Boolean.TRUE);
                }
                double sumItemA = 0.0;
                // 订单商品名称
                StringBuilder orderProdName = new StringBuilder(100);
                List<OrderItem> orderItems = new ArrayList<>();
                List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCartOrderDto.getShopCartItemDiscounts();
                boolean isUseTransfee = false;
                for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
                    List<ShopCartItemDto> shopCartItems = shopCartItemDiscount.getShopCartItems();
                    for (ShopCartItemDto shopCartItem : shopCartItems) {
                        Sku sku = checkAndGetSku(shopCartItem.getSkuId(), mergerOrder.getChannelId(), shopCartItem, skuStocksMap);
                        Product product = checkAndGetProd(shopCartItem.getProdId(), shopCartItem, prodStocksMap,shopId);
                        OrderItem orderItem = new OrderItem();
                        orderItem.setShopId(shopId);
                        orderItem.setOrderNumber(orderNumber);
                        orderItem.setSkuId(sku.getSkuId());
                        orderItem.setProdId(sku.getProdId());
                        orderItem.setSkuName(sku.getSkuName());
                        orderItem.setProdCount(shopCartItem.getProdCount());
                        orderItem.setProdName(sku.getProdName());
                        orderItem.setPic(StrUtil.isBlank(sku.getPic()) ? product.getPic() : sku.getPic());
                        orderItem.setPrice(shopCartItem.getPrice());
                        orderItem.setUserId(userId);
                        orderItem.setProductTotalAmount(shopCartItem.getProductTotalAmount());
                        orderItem.setRecTime(now);
                        orderItem.setCommSts(0);
                        orderItem.setBasketDate(shopCartItem.getBasketDate());
                        //平台的补贴优惠金额
                        orderItem.setPlatformShareReduce(shopCartItem.getPlatformShareReduce());
                        Double actualItem = shopCartItem.getActualTotal();
                        //后台充值的积分
                        if (null != point && point > 0) {
                            Double useActualItem = actualItem;
                            if (!isUseTransfee) {//每个订单增加运费，都只扣一次
                                Double transfee = shopCartOrderDto.getTransfee();
                                useActualItem = Arith.add(useActualItem, transfee);
                                isUseTransfee = Boolean.TRUE;
                            }
                            //把钱换算成积分
                            actualItem = this.doGetOrderItemPoints(Arith.mul(useActualItem, 100), expiryQueue, orderNumber, userId, mergerOrder.getChannelId());
                            //把积分换算成钱
                            if (actualItem > 0) {
                                actualItem = Arith.div(actualItem, 100);
                            }
                        }
                        //用于判定是否需要微信支付
                        sumItemA = Arith.add(sumItemA, actualItem);
                        // 实际订单项支付金额
                        // 根据platform来计算是否优先扣减积分抵扣
                        orderItem.setActualTotal(actualItem);
                        // 分摊优惠金额
                        orderItem.setShareReduce(shopCartItem.getShareReduce());
                        orderProdName.append(orderItem.getProdName()).append(",");
                        //推广员卡号
                        orderItem.setDistributionCardNo(shopCartItem.getDistributionCardNo());
                        orderItem.setWeight( shopCartItem.getWeight());
                        //使用积分价格
                        orderItem.setUseScore((int) Arith.mul(Arith.sub(shopCartItem.getActualTotal(), actualItem), 100));
                        orderItems.add(orderItem);

                        if (shopCartItem.getBasketId() != null && shopCartItem.getBasketId() != 0) {
                            basketIds.add(shopCartItem.getBasketId());
                        }
                    }

                }

                orderProdName.subSequence(0, Math.min(orderProdName.length() - 1, 100));
                if (orderProdName.lastIndexOf(",") == orderProdName.length() - 1) {
                    orderProdName.deleteCharAt(orderProdName.length() - 1);
                }

                order.setShopId(shopId);
                order.setOrderNumber(orderNumber);
                // 订单商品名称
                order.setProdName(orderProdName.toString());
                // 用户id
                order.setUserId(userId);
                // 商品总额
                order.setTotal(shopCartOrderDto.getTotal());
                // 实际总额
                order.setActualTotal(actualTotal);
                order.setNextPay(sumItemA > 0);//true是需要微信支付
                order.setChannelId(Long.valueOf(mergerOrder.getChannelId()));
                order.setUpdateTime(now);
                order.setCreateTime(now);
                order.setDeleteStatus(0);
                order.setProductNums(shopCartOrderDto.getTotalCount());
                order.setAddrOrderId(addrOrderId);
                order.setDvyType(mergerOrder.getDvyType());
                order.setReduceAmount(shopCartOrderDto.getShopReduce());
                order.setFreightAmount(shopCartOrderDto.getTransfee());
                order.setRemarks(shopCartOrderDto.getRemarks());

                order.setPlatformAmount(shopCartOrderDto.getPlatformAmount());
                order.setOrderItems(orderItems);
                order.setScore(shopCartOrderDto.getUseScore());
                order.setDvyType(mergerOrder.getDvyType());
                event.getOrders().add(order);
                // 插入订单结算表
                OrderSettlement orderSettlement = new OrderSettlement();
                orderSettlement.setUserId(userId);
                orderSettlement.setIsClearing(0);
                orderSettlement.setCreateTime(now);
                orderSettlement.setOrderNumber(orderNumber);
                orderSettlement.setPayAmount(order.getActualTotal());
                orderSettlement.setPayStatus(0);
                orderSettlement.setVersion(0);
                orderSettlement.setPayScore(0);
                //如果用使用积分，结算表将积分价格插入
                if (mergerOrder.getIsScorePay() != null && mergerOrder.getIsScorePay() == 1) {
                    orderSettlement.setPayScore(shopCartOrderDto.getUseScore());
                }
                orderSettlementMapper.insert(orderSettlement);
            }
        }

        // 删除购物车的商品信息
        if (!basketIds.isEmpty()) {
            basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds);
        }
    }

    @SuppressWarnings({"Duplicates"})
    private Product checkAndGetProd(Long prodId, ShopCartItemDto shopCartItem, Map<Long, Product> prodStocksMap,Long shopId) {
        Product product = productService.getProductByProdIdAndShopId(prodId, shopId);
        if (product == null) {
            throw new GlobalException("购物车包含无法识别的商品");
        }

        if (product.getStatus() != 1) {
            throw new GlobalException("商品[" + product.getProdName() + "]已下架");
        }

        // 商品需要改变的库存
        Product mapProduct = prodStocksMap.get(prodId);

        if (mapProduct == null) {
            mapProduct = new Product();
            mapProduct.setTotalStocks(0);
            mapProduct.setProdId(prodId);
            mapProduct.setProdName(product.getProdName());

        }

        if (product.getTotalStocks() != -1) {
            mapProduct.setTotalStocks(mapProduct.getTotalStocks() + shopCartItem.getProdCount());
            prodStocksMap.put(product.getProdId(), mapProduct);
        }

        // -1为无限库存
        if (product.getTotalStocks() != -1 && mapProduct.getTotalStocks() > product.getTotalStocks()) {
            throw new GlobalException("商品：[" + product.getProdName() + "]库存不足5");
        }

        return product;
    }

    @SuppressWarnings({"Duplicates"})
    private Sku checkAndGetSku(Long skuId, ShopCartItemDto shopCartItem, Map<Long, Sku> skuStocksMap) {
        // 获取sku信息
        Sku sku = skuService.getSkuBySkuId(skuId);
        if (sku == null) {
            throw new GlobalException("购物车包含无法识别的商品");
        }

        if (sku.getStatus() != 1) {
            throw new GlobalException("商品[" + sku.getProdName() + "]已下架");
        }
        // -1为无限库存
        if (sku.getActualStocks() != -1 && shopCartItem.getProdCount() > sku.getActualStocks()) {
            throw new GlobalException("商品：[" + sku.getProdName() + "]库存不足6");
        }

        if (sku.getActualStocks() != -1) {
            Sku mapSku = new Sku();
            mapSku.setProdId(sku.getProdId());
            // 这里的库存是改变的库存
            mapSku.setStocks(shopCartItem.getProdCount());
            mapSku.setSkuId(sku.getSkuId());
            mapSku.setProdName(sku.getProdName());
            skuStocksMap.put(sku.getSkuId(), mapSku);
        }
        return sku;
    }

    @SuppressWarnings({"Duplicates"})
    private Sku checkAndGetSku(Long skuId, Long channelId, ShopCartItemDto shopCartItem, Map<Long, Sku> skuStocksMap) {
        // 获取sku信息
        Sku sku = skuService.getSkuBySkuId(skuId, channelId, shopCartItem.getShopId());
        if (sku == null) {
            throw new GlobalException("购物车包含无法识别的商品");
        }

        if (sku.getStatus() != 1) {
            throw new GlobalException("商品[" + sku.getProdName() + "]已下架");
        }
        // -1为无限库存
        if (sku.getActualStocks() != -1 && shopCartItem.getProdCount() > sku.getActualStocks()) {
            throw new GlobalException("商品：[" + sku.getProdName() + "]库存不足7");
        }

        if (sku.getActualStocks() != -1) {
            Sku mapSku = new Sku();
            mapSku.setProdId(sku.getProdId());
            // 这里的库存是改变的库存
            mapSku.setStocks(shopCartItem.getProdCount());
            mapSku.setSkuId(sku.getSkuId());
            mapSku.setProdName(sku.getProdName());
            skuStocksMap.put(sku.getSkuId(), mapSku);
        }
        return sku;
    }


    private Double doGetOrderItemPoints(Double actualTotal, ArrayDeque<PointsRecord> expiryQueue, String orderNumber, String userId, Long channelId) {
        PointsRecord pointsRecord = expiryQueue.poll();
        PointsRecord pr = new PointsRecord();
        if (pointsRecord != null) {
            Long point = pointsRecordMapper.statisticsPoint(userId, channelId);
            //判定可用金额
            BigDecimal points = pointsRecord.getPoints() != null ? pointsRecord.getPoints() : BigDecimal.ZERO;
            BigDecimal variablePoints = pointsRecord.getVariablePoints() != null ? pointsRecord.getVariablePoints() : BigDecimal.ZERO;
            //充值记录中可用的记录
            double available = points.subtract(variablePoints).doubleValue();
            BeanUtil.copyProperties(pointsRecord, pr);
            if (available > actualTotal) {
                //钱已经抵扣完，但是当条积分还有剩余，所以不要重新把剩余的放入队列。钱少积分多
                //剩余的积分
                pointsRecord.setVariablePoints(variablePoints.add(BigDecimal.valueOf(actualTotal)));
                pr.setVariablePoints(BigDecimal.valueOf(actualTotal));
                expiryQueue.addFirst(pointsRecord);
                pointsRecord.setPointsAudit(2);//部分使用
                pr.setPointsAudit(2);//部分使用
                actualTotal = 0.0;
            } else if (actualTotal > available) {
                actualTotal = Arith.sub(actualTotal, available);
                //把当前积分已扣完,但是还有钱。钱多积分少
                pointsRecord.setVariablePoints(BigDecimal.valueOf(available));
                pr.setVariablePoints(BigDecimal.valueOf(available));
                pr.setPointsAudit(3);
                pointsRecord.setPointsAudit(3);
            } else {
                //恰好等于
                pr.setPointsAudit(3);
                pointsRecord.setPointsAudit(3);
                pointsRecord.setVariablePoints(variablePoints.add(BigDecimal.valueOf(actualTotal)));
                pr.setVariablePoints(BigDecimal.valueOf(actualTotal));
                actualTotal = 0.0;
            }
            int update = pointsRecordMapper.updateById(pointsRecord);
            if (update > 0) {
                pr.setId(null);
                pr.setPointsType(2);
                pr.setOrderNumber(orderNumber);
                pr.setCurrentlyAvailablePoints(BigDecimal.valueOf(point).subtract(pr.getVariablePoints()));
                pr.setCreationDate(new Date());
                pointsRecordMapper.insert(pr);
            }
            //  插入数据库
            if (actualTotal > 0.0) {
                actualTotal = this.doGetOrderItemPoints(actualTotal, expiryQueue, orderNumber, userId, channelId);
            }
        }
        return actualTotal;
    }

}
