package org.springblade.modules.api.service.impl;

import org.springblade.common.enums.OrderTypeEnum;
import org.springblade.common.enums.PreferentialTypeEnum;
import org.springblade.common.enums.TransactionStatusEnum;
import org.springblade.common.enums.YesOrNoEnum;
import org.springblade.common.utils.CommonUtil;
import org.springblade.modules.api.request.CartSubmitOrderRequest;
import org.springblade.modules.api.service.SubmitOrderService;
import org.springblade.modules.finance.entity.OrderDetail;
import org.springblade.modules.finance.entity.OrderGoods;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.modules.api.request.SubmitOrderRequest;
import org.springblade.modules.api.service.IMallService;
import org.springblade.modules.finance.entity.Order;
import org.springblade.modules.finance.entity.TransactionFlow;
import org.springblade.modules.finance.service.IOrderDetailService;
import org.springblade.modules.finance.service.IOrderGoodsService;
import org.springblade.modules.finance.service.IOrderService;
import org.springblade.modules.finance.service.ITransactionFlowService;
import org.springblade.modules.finance.service.impl.OrderTimeoutService;
import org.springblade.modules.mall.entity.Coupon;
import org.springblade.modules.mall.entity.CouponUser;
import org.springblade.modules.mall.entity.GoodsInfo;
import org.springblade.modules.mall.entity.GoodsSpec;
import org.springblade.modules.mall.service.*;
import org.springblade.modules.platform.entity.UserAddress;
import org.springblade.modules.platform.service.IUserAddressService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Author pangqijun
 * Date 2023/3/15
 * Description
 */
@Service("mallServiceImpl")
public class MallServiceImpl implements IMallService, SubmitOrderService {

    @Resource
    private IGoodsInfoService goodsInfoService;
    @Resource
    private IGoodsSpecService goodsSpecService;
    @Resource
    private IOrderGoodsService orderGoodsService;
    @Resource
    private IOrderService orderService;
    @Resource
    private IOrderDetailService orderDetailService;
    @Resource
    private IUserAddressService userAddressService;
    @Resource
    private ITransactionFlowService transactionFlowService;
    @Resource
    private OrderTimeoutService orderTimeoutService;
    @Resource
    private ICouponUserService couponUserService;
    @Resource
    private ICouponService couponService;
    @Resource
    private IShoppingCartService shoppingCartService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String submitOrder(SubmitOrderRequest request) {
        Assert.notNull(request.getGoodsInfoId(), "goodsInfoId 不能为空");
        // 查询商品信息，并校验
        GoodsInfo goodsInfo = goodsInfoService.getById(request.getGoodsInfoId());
        this.checkGoodsState(goodsInfo);
        // 查询商品规格信息，并校验
        GoodsSpec goodsSpec = goodsSpecService.getById(request.getGoodsSpecId());
        this.checkGoodsSpecState(goodsSpec);
        // 查询用户地址信息
        UserAddress userAddress = userAddressService.getById(request.getUserAddressId());
        Assert.notNull(userAddress, "用户地址信息不存在");

        // 初始化订单数据
        Order order = orderService.init();
        order.setOrderType(request.getOrderType());
        order.setQuantity(request.getGoodsNum());
        order.setDistributeMode(request.getDistributeMode());
        order.setConsignee(userAddress.getRealName());
        order.setConsigneePhoneNumber(userAddress.getPhone());
        order.setImage(goodsSpec.getCover());
        order.setTotalAmount(goodsSpec.getSalePrice().multiply(BigDecimal.valueOf(request.getGoodsNum())));
        order.setDiscountAmount(this.calDiscountAmount(order.getOrderNo(), order.getTotalAmount(), request.getCouponUserId()));// 优惠金额
        order.setPaymentAmount(order.getTotalAmount().subtract(order.getDiscountAmount()));
        // 邮费
        order.setPostage(YesOrNoEnum.YES.getValue().equals(goodsInfo.getIsFreeShipping()) ? new BigDecimal(0) : goodsInfo.getFreePrice());
        orderService.save(order);

        //订单详情实体
        OrderDetail orderDetail = this.buildOrderDetail(request.getRemark(), userAddress);
        orderDetail.setOrderId(order.getId());
        orderDetailService.save(orderDetail);

        // 保存订单商品关联信息表
        OrderGoods orderGoods = this.buildOrderGoods(goodsInfo, goodsSpec, request.getGoodsNum(), order.getOrderNo());
        orderGoodsService.save(orderGoods);

        // 更新商品规格的库存
        goodsSpecService.reduceStock(goodsSpec, request.getGoodsNum());

        // 添加订单到延时队列，处理订单支付超时问题
        orderTimeoutService.addTask(order.getId(), null);

        return order.getOrderNo();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String cartSubmitOrder(CartSubmitOrderRequest cartSubmitOrderRequest) {
        // 交易流水号
        String transactionNo = CommonUtil.genTimeID();
        TransactionFlow flow = new TransactionFlow();
        flow.setStatus(TransactionStatusEnum.NOT_PAY.getValue());// 未支付
        flow.setTransactionNo(transactionNo);

        // 用户地址
        UserAddress userAddress = userAddressService.getById(cartSubmitOrderRequest.getUserAddressId());
        Assert.notNull(userAddress, "用户地址不存在");

        // 将商品和规格信息一次性查出来
        List<Long> goodsIds = cartSubmitOrderRequest.getGoodsList().stream().map(SubmitOrderRequest::getGoodsInfoId).collect(Collectors.toList());
        List<Long> goodsSpecIds = cartSubmitOrderRequest.getGoodsList().stream().map(SubmitOrderRequest::getGoodsSpecId).collect(Collectors.toList());
        List<GoodsInfo> goodsInfoList = goodsInfoService.lambdaQuery().in(GoodsInfo::getId, goodsIds).list();
        List<GoodsSpec> goodsSpecList = goodsSpecService.lambdaQuery().in(GoodsSpec::getId, goodsSpecIds).list();
        Map<Long, GoodsInfo> goodsInfoMap = goodsInfoList.stream().collect(Collectors.toMap(GoodsInfo::getId, Function.identity()));
        Map<Long, GoodsSpec> goodsSpecMap = goodsSpecList.stream().collect(Collectors.toMap(GoodsSpec::getId, Function.identity()));

        // 商品总金额
        BigDecimal goodsAmount = new BigDecimal(0);
        // 先将所有商品进行校验
        for (SubmitOrderRequest request : cartSubmitOrderRequest.getGoodsList()) {
            GoodsInfo goodsInfo = goodsInfoMap.get(request.getGoodsInfoId());
            GoodsSpec goodsSpec = goodsSpecMap.get(request.getGoodsSpecId());
            // 校验商品状态
            this.checkGoodsState(goodsInfo);
            // 校验规格信息
            this.checkGoodsSpecState(goodsSpec);
            goodsAmount = goodsAmount.add(goodsSpec.getSalePrice().multiply(BigDecimal.valueOf(request.getGoodsNum())));
        }

        // 总优惠金额
        BigDecimal totalDiscountAmount = this.calDiscountAmount(transactionNo, goodsAmount, cartSubmitOrderRequest.getCouponUserId());

        for (SubmitOrderRequest request : cartSubmitOrderRequest.getGoodsList()) {
            GoodsInfo goodsInfo = goodsInfoMap.get(request.getGoodsInfoId());
            GoodsSpec goodsSpec = goodsSpecMap.get(request.getGoodsSpecId());
            // 初始化订单数据
            Order order = orderService.init();
            order.setTransactionNo(transactionNo);
            order.setOrderType(OrderTypeEnum.GOODS.getValue());
            order.setQuantity(request.getGoodsNum());
            order.setDistributeMode(request.getDistributeMode());
            order.setConsignee(userAddress.getRealName());
            order.setConsigneePhoneNumber(userAddress.getPhone());
            order.setImage(goodsSpec.getCover());
            order.setTotalAmount(goodsSpec.getSalePrice().multiply(BigDecimal.valueOf(request.getGoodsNum())));

            // 先计算商品优惠金额的比例，然后用比例乘以优惠金额
            BigDecimal divide = order.getTotalAmount().divide(goodsAmount, 4, BigDecimal.ROUND_DOWN);
            BigDecimal discountAmount = divide.multiply(totalDiscountAmount);
            order.setDiscountAmount(discountAmount);// 优惠金额
            order.setPaymentAmount(order.getTotalAmount().subtract(order.getDiscountAmount()));
            // 邮费
            order.setPostage(YesOrNoEnum.YES.getValue().equals(goodsInfo.getIsFreeShipping()) ? new BigDecimal(0) : goodsInfo.getFreePrice());
            // 保存订单信息
            orderService.save(order);
            // 保存订单详细信息
            OrderDetail orderDetail = this.buildOrderDetail(request.getRemark(), userAddress);
            orderDetail.setOrderId(order.getId());
            orderDetailService.save(orderDetail);
            // 创建订单商品关联数据
            OrderGoods orderGoods = this.buildOrderGoods(goodsInfo, goodsSpec, request.getGoodsNum(), order.getOrderNo());
            orderGoodsService.save(orderGoods);
            // 更新商品规格的库存
            goodsSpecService.reduceStock(goodsSpec, request.getGoodsNum());
        }
        // 交易总金额
        flow.setAmount(goodsAmount.subtract(totalDiscountAmount));
        transactionFlowService.save(flow);

        // 移除购物车商品
        List<Long> shopCartIds = cartSubmitOrderRequest.getGoodsList().stream().map(SubmitOrderRequest::getShoppingCartId).collect(Collectors.toList());
        shoppingCartService.remove(shopCartIds);

        // 添加订单到延时队列，处理订单支付超时问题
        orderTimeoutService.addTask(null, flow.getId());

        return transactionNo;
    }

    /**
     * 计算优惠金额
     *
     * @param totalAmount
     * @param userCouponId
     * @return
     */
    @Override
    public BigDecimal calDiscountAmount(String orderNo, BigDecimal totalAmount, Long userCouponId) {
        if (userCouponId == null) {
            return BigDecimal.valueOf(0.00);
        }
        CouponUser couponUser = couponUserService.getById(userCouponId);
        Assert.notNull(couponUser, "用户优惠券数据不存在");
        Coupon coupon = couponService.getById(couponUser.getCouponId());
        Assert.notNull(coupon, "优惠券信息不存在");

        // 修改用户优惠券状态
        couponUserService.use(orderNo, couponUser.getId());

        // 判断金额是否达到满减条件
        if (coupon.getFullReduction().compareTo(totalAmount) > 0) {
            throw new ServiceException("支付金额未达到满减条件");
        }
        if (PreferentialTypeEnum.DISCOUNT.getValue().equals(coupon.getPreferentialType())) {//折扣优惠券
            double ratio = coupon.getDiscountRatio() / 10;
            return totalAmount.multiply(BigDecimal.valueOf(ratio));
        } else {
            return coupon.getDiscountAmount();
        }
    }
}
