/*
 * 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 static final String XSB = "XSB";

    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涓簁ey 闇€瑕佹洿鏂扮殑sku涓簐alue鐨刴ap
        Map<Long, Sku> skuStocksMap = new HashMap<>(16);
        // 鍟嗗搧productid涓簁ey 闇€瑕佹洿鏂扮殑product涓簐alue鐨刴ap
        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;
                boolean hasXsbMemberDiscount = false;
                // 璁㈠崟鍟嗗搧鍚嶇О
                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());
                        if (XSB.equalsIgnoreCase(shopCartItem.getBusinessType())) {
                            Double memberDiscountShareAmount = shopCartItem.getMemberDiscountShareAmount();
                            orderItem.setMemberDiscountShareAmount(memberDiscountShareAmount == null ? 0.0 : memberDiscountShareAmount);
                            hasXsbMemberDiscount = hasXsbMemberDiscount || (memberDiscountShareAmount != null && memberDiscountShareAmount > 0);
                        } else {
                            orderItem.setMemberDiscountShareAmount(0.0);
                        }
                        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());
                if (hasXsbMemberDiscount) {
                    order.setMemberDiscountAmount(shopCartOrderDto.getMemberDiscountAmount());
                    order.setMemberDiscountDesc(shopCartOrderDto.getMemberDiscountDesc());
                } else {
                    order.setMemberDiscountAmount(0.0);
                    order.setMemberDiscountDesc(null);
                }
                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[" + product.getProdName() + "] is unavailable");
        }

        // 鍟嗗搧闇€瑕佹敼鍙樼殑搴撳瓨
        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("Product[" + sku.getProdName() + "] is unavailable");
        }
        // -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("Product[" + sku.getProdName() + "] is unavailable");
        }
        // -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;
    }

}
