package com.xingxi.api.service.impl;

import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xingxi.api.common.ApiException;
import com.xingxi.api.common.UserContext;
import com.xingxi.api.data.domain.OrderDTO;
import com.xingxi.api.data.domain.OrderDetailDTO;
import com.xingxi.api.data.mapper.WxApiOrderMapper;
import com.xingxi.api.data.mapper.WxApiPaymentInfoMapper;
import com.xingxi.api.data.mapper.WxApiProdInventoryMapper;
import com.xingxi.api.model.*;
import com.xingxi.api.service.OrderServiceI;
import com.xingxi.business.Order.domain.Order;
import com.xingxi.business.Order.domain.OrderDeliveryExpr;
import com.xingxi.business.Order.domain.OrderDetail;
import com.xingxi.business.Order.mapper.OrderDeliveryExprMapper;
import com.xingxi.business.Order.mapper.OrderDetailMapper;
import com.xingxi.business.Order.mapper.OrderMapper;
import com.xingxi.business.PaymentInfo.domain.PaymentInfo;
import com.xingxi.business.PaymentInfo.mapper.PaymentInfoMapper;
import com.xingxi.business.ProdInventory.domain.ProdInventory;
import com.xingxi.business.ProdInventory.domain.ProdInventoryBill;
import com.xingxi.business.ProdInventory.mapper.ProdInventoryBillMapper;
import com.xingxi.business.UserAddress.domain.UserAddress;
import com.xingxi.common.core.domain.entity.SysUser;
import com.xingxi.common.enums.EDelFlag;
import com.xingxi.common.enums.EInventoryBillType;
import com.xingxi.common.enums.EOrderDetailStatus;
import com.xingxi.common.enums.EOrderStatus;
import com.xingxi.common.exception.BusinessException;
import com.xingxi.common.mq.bo.order.OrderBO;
import com.xingxi.common.mq.publisher.order.OrderCreatedMqPublisher;
import com.xingxi.common.mq.publisher.order.OrderPayedMqPublisher;
import com.xingxi.common.utils.DateUtils;
import com.xingxi.common.utils.PageUtils;
import com.xingxi.common.utils.StringUtils;
import com.xingxi.common.utils.bean.BeanUtils;
import com.xingxi.common.utils.uuid.IdUtils;
import com.xingxi.master.merchant.domain.MerchantProd;
import com.xingxi.master.merchant.mapper.MerchantProdMapper;
import com.xingxi.master.product.domain.Prod;
import com.xingxi.master.product.domain.ProdAttrPrice;
import com.xingxi.master.product.domain.ProdPic;
import com.xingxi.master.product.domain.ProdVendor;
import com.xingxi.master.product.mapper.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service("OrderServiceI")
class OrderApiServiceImpl implements OrderServiceI {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private ProdPicMapper prodPicMapper;
    @Autowired
    private OrderDeliveryExprMapper orderDeliveryExprMapper;
    @Autowired
    private MerchantProdMapper merchantProdMapper;
    @Autowired
    private ProdMapper prodMapper;
    @Autowired
    private ProdAttrMapper prodAttrMapper;
    @Autowired
    private ProdAttrPriceMapper prodAttrPriceMapper;
    @Autowired
    private ProdVendorMapper prodVendorMapper;
    @Autowired
    private ProdInventoryBillMapper prodInventoryBillMapper;
    @Autowired
    private WxApiProdInventoryMapper wxApiProdInventoryMapper;
    @Autowired
    private WxApiPaymentInfoMapper wxApiPaymentInfoMapper;
    @Autowired
    private WxApiOrderMapper wxApiOrderMapper;

    private final OrderCreatedMqPublisher createdMqPublisher;

    @Value("${spring.application.xingxi.unifiedpay.callbackUrl}")
    private String notifyUrl;

    private WxPayConfig config;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private PaymentInfoMapper paymentInfoMapper;

    @Override
    public OrderQueryResponse list(UserContext userContext, OrderQueryRequest request) {

        SysUser currentUser = userContext.getCurrentUser();
        Long userId = currentUser.getUserId();
        Integer pageNum = request.getPageNum();
        Integer pageSize = request.getPageSize();
        String status = request.getStatus();

        Order condOrder = new Order();
        condOrder.setBuyerId(userId);
        condOrder.setOrderStatus(status);
        condOrder.setDelFlag(EDelFlag.NO.getVal());

        PageUtils.startPage();
        Page<OrderDTO> orderList  = PageHelper.startPage(pageNum, pageSize)
                .doSelectPage(() -> orderMapper.selectOrderList(condOrder));

        if (!orderList.isEmpty()) {
            for (OrderDTO order : orderList) {
                OrderDetail condOderDetail = new OrderDetail();
                condOderDetail.setOrderId(order.getOrderId());
                List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailList(condOderDetail);
                List<OrderDetailDTO> odDtoList = new ArrayList<>();
                for (OrderDetail orderDetail : orderDetailList) {
                    OrderDetailDTO odDto = new OrderDetailDTO();
                    BeanUtils.copyBeanProp(odDto, orderDetail);

                    ProdPic condPic = new ProdPic();
                    condPic.setProdId(orderDetail.getProdId());
                    condPic.setDelFlag(EDelFlag.NO.getVal());
                    odDto.setImageDTOS(prodPicMapper.selectProdPicList(condPic));

                    OrderDeliveryExpr condExp = new OrderDeliveryExpr();
                    condExp.setOrderDetailId(orderDetail.getOrderDetailId());
                    odDto.setOrderDetailExprs(orderDeliveryExprMapper.selectOrderDeliveryExprList(condExp));

                    odDtoList.add(odDto);
                }

                order.setOrderDetailDTOS(odDtoList);
            }
        }

        return null;
    }

    @Override
    public OrderCreateResponse create(UserContext userContext, OrderCreateRequest request) {

        Date orderTime = DateUtils.getNowDate();

        // C端用户信息
        SysUser currentUser = userContext.getCurrentUser();
        Long userId = currentUser.getUserId();

        // 订单信息
        List<OrderItemDTO> orderItemDTOS = request.getOrderItemDTOS();
        UserAddress userAddressDTO = request.getUserAddress();

        String orderNo = IdUtils.snowFlake();
        String orderStatus = EOrderStatus.ORDERED.getVal();

        Order order = new Order();

        order.setOrderNo(orderNo);
        order.setOrderTime(DateUtils.getNowDate());
        order.setOrderStatus(orderStatus);
        order.setBuyerId(userId);

        order.setRecProv(request.getUserAddress().getProvince());
        order.setRecCity(request.getUserAddress().getCity());
        order.setRecDistrict(request.getUserAddress().getDistrict());
        order.setRecAddress(request.getUserAddress().getAddress());
        order.setRecName(request.getUserAddress().getRecipient());
        order.setRecMobile(request.getUserAddress().getMobile());
        order.setRemark(request.getRemark());
        order.setDelFlag(EDelFlag.NO.getVal());
        order.setCreateUser(userId.toString());
        order.setCreateTime(orderTime);
        order.setUpdateUser(userId.toString());
        order.setUpdateTime(orderTime);

        int cnt = orderMapper.insertOrder(order);
        if (cnt <= 0) {
            log.debug("创建订单失败");
            throw new BusinessException("创建订单失败");
        }

        Integer prodQty = 0;
        BigDecimal prodAmt = BigDecimal.ZERO;

        List<OrderDetail> orderDetailItems = new ArrayList<>();
        for (OrderItemDTO orderItemDTO : orderItemDTOS) {
            MerchantProd merchantProd = merchantProdMapper.selectMerchantProdByMercProdId(orderItemDTO.getMercProdId());
            Prod prod = prodMapper.selectProdById(merchantProd.getProdId());
            order.setSellerId(merchantProd.getMercId());

            ProdVendor condPv = new ProdVendor();
            condPv.setDelFlag(EDelFlag.NO.getVal());
            condPv.setProdId(merchantProd.getProdId());
            condPv.setProdAttrId(merchantProd.getProdAttrId());

            List<ProdVendor> prodVendorList = prodVendorMapper.selectProdVendorList(condPv);
            if (prodVendorList.isEmpty()) {
                log.error("商户商品[" + orderItemDTO.getMercProdId() + "]无供应商");
                throw new BusinessException("未查询到商品的供应商");
            }

            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(order.getOrderId());
            orderDetail.setOrderDetailStatus(EOrderDetailStatus.ORDERED.getVal());
            orderDetail.setProdId(merchantProd.getProdId());
            orderDetail.setProdName(prod.getProdName());
            orderDetail.setProdAttrId(merchantProd.getProdAttrId());
            orderDetail.setProdAttrName(prodAttrMapper.selectProdAttrByProdAttrId(merchantProd.getProdAttrId()).getAttrName());
            orderDetail.setVendorId(prodVendorList.get(0).getVendorId());
            orderDetail.setQuantity(orderItemDTO.getCount());

            ProdAttrPrice condPrice = new ProdAttrPrice();
            condPrice.setDelFlag(EDelFlag.NO.getVal());
            condPrice.setProdId(merchantProd.getProdId());
            condPrice.setProdAttrId(merchantProd.getProdAttrId());
            condPrice.setSellerId(merchantProd.getMercId());
            List<ProdAttrPrice> prodAttrPriceList = prodAttrPriceMapper.selectProdAttrPriceList(condPrice);
            if (StringUtils.isEmpty(prodAttrPriceList)) {
                log.error("商户商品[{}]无价格", orderItemDTO.getMercProdId());
                throw new BusinessException("未查询到商品的价格");
            }

            orderDetail.setPrice(prodAttrPriceList.get(0).getPrice());
            orderDetail.setOrderAmount(orderDetail.getPrice().multiply(new BigDecimal(orderDetail.getQuantity())));
            orderDetail.setPayAmount(orderDetail.getOrderAmount());
            orderDetail.setDelFlag(EDelFlag.NO.getVal());
            orderDetail.setCreateUser(userId.toString());
            orderDetail.setCreateTime(orderTime);
            orderDetail.setUpdateUser(userId.toString());
            orderDetail.setUpdateTime(orderTime);

            orderDetailItems.add(orderDetail);

            prodQty = prodQty + orderDetail.getQuantity();
            prodAmt = prodAmt.add(orderDetail.getOrderAmount());

            // 锁库
            ProdInventory prodInventory = wxApiProdInventoryMapper.selectProdInventoryLock(orderDetail.getProdId(), orderDetail.getProdAttrId(), merchantProd.getMercId());
            if (!(prodInventory != null && prodInventory.getAvailQty() > 0)) {
                log.error("商品[{}]库存不足", prod.getProdName());
                throw new BusinessException("商品[" + prod.getProdName() + "]库存不足");
            }

            prodInventory.setAvailQty(orderDetail.getQuantity());
            prodInventory.setUpdateTime(orderTime);
            prodInventory.setUpdateUser(userId.toString());
            cnt = wxApiProdInventoryMapper.updateSubtractProdStock(prodInventory);
            if (cnt <= 0) {
                log.error("扣减库存[{}]失败", prodInventory.getProdInventoryId());
                throw new ApiException("prod.stock.out.error");
            }

            ProdInventoryBill inventoryBill = new ProdInventoryBill();
            inventoryBill.setBillTime(orderTime);
            inventoryBill.setBillType(EInventoryBillType.LOCK.getVal());
            inventoryBill.setQuantity(orderItemDTO.getCount());
            inventoryBill.setInventoryId(prodInventory.getProdInventoryId());
            inventoryBill.setOrderId(order.getOrderId());
            inventoryBill.setOrderNo(orderNo);
            inventoryBill.setDelFlag(EDelFlag.NO.getVal());
            inventoryBill.setCreateUser(userId.toString());
            inventoryBill.setCreateTime(orderTime);
            inventoryBill.setUpdateUser(userId.toString());
            inventoryBill.setUpdateTime(orderTime);

            cnt = prodInventoryBillMapper.insertProdInventoryBill(inventoryBill);
            if (cnt <= 0) {
                log.error("创建订单失败");
                throw new BusinessException("创建订单失败");
            }
        }

        orderDetailMapper.batchInsertOrderDetail(orderDetailItems);
        order.setOrderAmount(prodAmt);
        order.setProdQuantity(prodQty);
        orderMapper.updateOrder(order);

        orderCreatedEvent(order);

        config = wxPayService.getConfig();

        WxPayUnifiedOrderV3Result.JsapiResult result = callWxpay(order,currentUser.getWxOpenId(),order.getOrderAmount().intValue(),currentUser.getLoginIp());

        return null;
    }

    @Override
    public OrderDetailResponse get(UserContext userContext, OrderDetailRequest request) {
        Long orderId = request.getOrderId();
        return new OrderDetailResponse(
                getOrderDTOByOrderId(orderId)
        );
    }
    private OrderDTO getOrderDTOByOrderId(Long orderId){
        OrderDTO rtn = new OrderDTO();
        Order order = wxApiOrderMapper.selectOrderByOrderId(orderId);

        BeanUtils.copyProperties(order,rtn);

        List<OrderDetailDTO> detailDTOList = new ArrayList<>();

        OrderDetail condOrderDetail = new OrderDetail();
        condOrderDetail.setOrderId(orderId);

        List<OrderDetail> orderDetailList = wxApiOrderMapper.selectOrderDetailList(condOrderDetail);

        for (OrderDetail orderDetail : orderDetailList) {
            OrderDetailDTO dto = new OrderDetailDTO();
            BeanUtils.copyProperties(orderDetail,dto);
            detailDTOList.add(dto);
        }

        rtn.setOrderDetailDTOS(detailDTOList);

        return rtn;
    }

    @Override
    public PaymentInfo selectPaymentInfoByOutTradeNo(String outTradeNo) {
        return wxApiPaymentInfoMapper.selectPaymentInfoByOutTradeNo(outTradeNo);
    }

    @Override
    public Order lockOrderByPrescId(Long orderId) {
        return wxApiOrderMapper.lockOrderByPrescId(orderId);
    }

    private void orderCreatedEvent(Order message){
        OrderBO sendOrder = new OrderBO();
        sendOrder.setOrderId(message.getOrderId());
        sendOrder.setOrderNo(message.getOrderNo());
        createdMqPublisher.sendMessage(sendOrder);
    }

    private WxPayUnifiedOrderV3Result.JsapiResult callWxpay(Order order, String openid, int amount, String ip) {
        WxPayUnifiedOrderV3Result.JsapiResult rtn = new WxPayUnifiedOrderV3Result.JsapiResult();

        Date now = DateUtils.getNowDate();
        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        payer.setOpenid(openid);

        WxPayUnifiedOrderV3Request.Amount payAmount = new WxPayUnifiedOrderV3Request.Amount();
        payAmount.setTotal(amount); // 分
        payAmount.setCurrency("CNY");

        WxPayUnifiedOrderV3Request wxrequest = new WxPayUnifiedOrderV3Request();
        //过期时间，15分钟
        wxrequest.setTimeExpire(DateUtils.parseDateToStr(DateUtils.RFC3339, DateUtils.addMinutes(now,15))); // 示例值：2018-06-08T10:34:56+08:00

        wxrequest.setDescription("谷鹿鹿Q");
        wxrequest.setOutTradeNo(order.getOrderNo());
        wxrequest.setAppid(config.getAppId());
        wxrequest.setMchid(config.getMchId());
        wxrequest.setNotifyUrl(notifyUrl);
        wxrequest.setPayer(payer);
        wxrequest.setAmount(payAmount);

        log.info(wxrequest.toString());

        WxPayUnifiedOrderV3Request.SceneInfo sceneInfo = new WxPayUnifiedOrderV3Request.SceneInfo();
        sceneInfo.setPayerClientIp(ip);
        wxrequest.setSceneInfo(sceneInfo);

        try {

            rtn = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, wxrequest);

            PaymentInfo paymentInfo = new PaymentInfo();

            paymentInfo.setOrderId(order.getOrderId());
            paymentInfo.setOrderNo(order.getOrderNo());
            paymentInfo.setPayTime(now);
            paymentInfo.setSellerId(order.getSellerId());
            paymentInfo.setDescription("谷鹿鹿Q");
            paymentInfo.setOutTradeNo(order.getOrderNo());
            paymentInfo.setNotifyUrl(notifyUrl);
            paymentInfo.setAmountTotal(new BigDecimal(amount));
            paymentInfo.setPayerOpenid(openid);
            paymentInfo.setPrepayId(rtn.getPackageValue());
            paymentInfo.setDelFlag(EDelFlag.NO.getVal());
            paymentInfo.setCreateTime(now);
            paymentInfo.setUpdateTime(now);
            paymentInfo.setCreateUser(openid);
            paymentInfo.setUpdateUser(openid);
            paymentInfoMapper.insertPaymentInfo(paymentInfo);

            return rtn;
        } catch (WxPayException e) {
            log.error(e.getMessage());
            throw new RuntimeException("创建支付失败");
        }
    }
}
