package com.tzy.service.impl;

import com.tzy.app.domain.AppUserPoint;
import com.tzy.app.domain.order.OrderListDTO;
import com.tzy.app.dto.order.OrderSubmitDTO;
import com.tzy.app.dto.order.PrizeOrderParam;
import com.tzy.app.mapper.OrderListMapper;
import com.tzy.common.annotation.RedisLock;
import com.tzy.common.config.QuerySql;
import com.tzy.common.constant.ActConstans;
import com.tzy.common.constant.Constants;
import com.tzy.common.constant.MqConstans;
import com.tzy.common.dto.OutDTO;
import com.tzy.common.dto.UserLimitConfig;
import com.tzy.common.exception.ServiceException;
import com.tzy.common.utils.DateUtils;
import com.tzy.common.utils.StringUtils;
import com.tzy.common.utils.bean.DozerUtils;
import com.tzy.common.utils.bean.JSONTools;
import com.tzy.coupon.card.service.IAppUserCardRecordService;
import com.tzy.framework.util.RedisUtils;
import com.tzy.member.benefits.service.IMemberBenefitsService;
import com.tzy.pojo.item.OrderList;
import com.tzy.pojo.item.Sku;
import com.tzy.pojo.item.Spu;
import com.tzy.service.MallOrderService;
import com.tzy.service.SkuService;
import com.tzy.sportcard.api.bean.act.DrawActPrizeDTO;
import com.tzy.sportcard.api.bean.goods.SimpleSku;
import com.tzy.sportcard.api.domain.AppUserInfoDto;
import com.tzy.sportcard.api.domain.ShippingAddressDto;
import com.tzy.sportcard.api.mapper.AppActPrizeRecordMapper;
import com.tzy.sportcard.api.mapper.MineApiMapper;
import com.tzy.sportcard.api.service.MineApiService;
import com.tzy.sportcard.group.domain.CardGroupOrderInfo;
import com.tzy.sportcard.group.service.ICardGroupOrderInfoService;
import com.tzy.system.domain.TzyShippingAddress;
import com.tzy.system.service.ITzyShippingAddressService;
import com.tzy.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.teasoft.bee.osql.PreparedSql;
import org.teasoft.bee.osql.SuidRich;
import org.teasoft.bee.osql.transaction.Transaction;
import org.teasoft.honey.osql.core.SessionFactory;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MallOrderServiceImpl implements MallOrderService {
	@Autowired
	private ITzyShippingAddressService shippingAddressService;
	@Autowired
	private RedisUtils redisUtils;
	@Autowired
	private SkuService skuService;
	@Autowired
	private PreparedSql preparedSql;
	@Autowired
	private MineApiService mineApiService;
	@Autowired
	private ICardGroupOrderInfoService orderInfoService;
	@Autowired
	private SuidRich suidRich;
	@Resource
	private MineApiMapper mineApiMapper;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Resource
	private OrderListMapper orderListMapper;
	@Autowired
	private IAppUserCardRecordService appUserCardRecordService;
	@Resource
	private IMemberBenefitsService memberBenefitService;
	@Resource
	private AppActPrizeRecordMapper actPrizeRecordMapper;

	private void buildOrderAddress(Object addrId, CardGroupOrderInfo orderInfo) {
		if (addrId != null) {
			TzyShippingAddress shippingAddress = shippingAddressService.selectTzyShippingAddressById(Long.valueOf(addrId.toString()));
			if (null == shippingAddress) {
				throw new ServiceException(500, "收货地址不存在!");
			}
			//添加收货地址
			orderInfo.setShippingAddressId(shippingAddress.getId().longValue());
			orderInfo.setShippingAddressLinkname(shippingAddress.getLinkname());
			orderInfo.setShippingAddress(shippingAddress.getAddress() + shippingAddress.getAddressMore());
			orderInfo.setShippingAddressPhone(shippingAddress.getPhone());
		} else {
			ShippingAddressDto shippingAddressDto = new ShippingAddressDto();
			shippingAddressDto.setUserId(orderInfo.getUserId().intValue());
			List<ShippingAddressDto> shippingAddressDtos = mineApiService.searchShippingAddress(shippingAddressDto);
			if (!shippingAddressDtos.isEmpty()) {
				ShippingAddressDto shippingAddressd = shippingAddressDtos.get(0);
				orderInfo.setShippingAddressId(shippingAddressd.getId().longValue());
				orderInfo.setShippingAddressLinkname(shippingAddressd.getLinkname());
				orderInfo.setShippingAddress(shippingAddressd.getAddress() + shippingAddressd.getAddressMore());
				orderInfo.setShippingAddressPhone(shippingAddressd.getPhone());
			}
		}

	}

	/**
	 * 单件商品限购判断
	 *
	 * @param userId
	 * @param sku
	 * @param purchaseCount
	 */
	private void checkLimitQty(Integer userId, Sku sku, Integer purchaseCount) {
		Integer orderQuotaMin = sku.getOrderQuotaMin();
		if (orderQuotaMin > 0 && purchaseCount < orderQuotaMin) {
			redisUtils.del("group_order_lock_" + userId);
			throw new ServiceException(500, "商品" + orderQuotaMin + "件起售!");
		}
		Integer orderQuotaMax = sku.getOrderQuotaMax();
		if (orderQuotaMax > 0 && purchaseCount > orderQuotaMax) {
			redisUtils.del("group_order_lock_" + userId);
			throw new ServiceException(500, "超出限购数量,单笔限购" + orderQuotaMax + "件!");
		}
		Integer userQuotaMax = sku.getUserQuotaMax();
		if (userQuotaMax > 0) {
			String sql = QuerySql.getQuerySql("com.tzy.pojo.item.Sku.getOrderCount");
			String limitSql = "";
			if (StringUtils.isNotEmpty(sku.getLimitType())) {
				switch (sku.getLimitType()) {
					case "day":
						limitSql = " and o2.create_time>=current_date";
						break;
					case "week":
						limitSql = " and o2.create_time>=(select date_trunc('week', now()))";
						break;
					case "month":
						limitSql = " and o2.create_time>=(select date_trunc('month', now()))";
						break;
				}
			}
			String s = preparedSql.selectFun(sql + limitSql, new Object[]{userId, sku.getId()});
			Integer buyCount = Integer.valueOf(s);
			if ((buyCount + purchaseCount) > userQuotaMax) {
				redisUtils.del("group_order_lock_" + userId);
				throw new ServiceException(500, "超出限购数量,限购" + userQuotaMax + "件!");
			}
		}

		if (StringUtils.isNotEmpty(sku.getUserLimitConfig())) {
			//抢购限制
			UserLimitConfig limitConfig = JSONTools.jsonStr2obj(sku.getUserLimitConfig(), UserLimitConfig.class);
			String sql = QuerySql.getQuerySql("com.tzy.pojo.item.Sku.getOrderCount");
			sql += "and  (now() - o2.create_time::timestamp < interval '" + limitConfig.getLimitTimes() + " " + limitConfig.getLimitType() + "')";
			String s = preparedSql.selectFun(sql, new Object[]{userId, sku.getId()});
			Integer buyCount = Integer.valueOf(s);
			if (buyCount >= limitConfig.getLimitNum()) {
				redisUtils.del("group_order_lock_" + userId);
				throw new ServiceException(500, "购买失败,请重试!");
			}
		}
	}

	@Override
	public void cancelOrder(CardGroupOrderInfo orderInfo) {
		editCancelOrderStatus(orderInfo);
		if (StringUtils.isEmpty(orderInfo.getOrderSubType()) || ActConstans.GROUP_SALE_CODE.equals(orderInfo.getOrderSubType())) {
			List<OrderList> orderLists = selectByOrder(orderInfo.getId());
			orderLists.stream().filter(ol -> {
				// 库存调整前的订单不处理
				SimpleSku sku = getSimpleSku(ol.getSkuId());
				return sku.getInitStockTime() == null || orderInfo.getCreateTime().after(sku.getInitStockTime());
			}).forEach(this::incrSkuStock);
			addPointByDir(orderInfo, true, Constants.POINT_TYPE_COMMON_RETURN);
		} else if (Constants.ORDER_SUB_TYPE_PRIZE.equals(orderInfo.getOrderSubType())) {
			actPrizeRecordMapper.updateStatusByBaseOrderId(orderInfo.getId(), 0);
		}else if (Constants.AUCTION_ORDER_COMMON_AMOUNT.equals(orderInfo.getOrderSubType())){
			// 抢拍订单
			List<OrderList> orderLists = selectByOrder(orderInfo.getId());
			orderLists.forEach(order ->{
				SimpleSku updateSku = getSimpleSku(order.getSkuId());
				updateSku.setId(updateSku.getId());
				updateSku.setSkuStock(updateSku.getSkuStock() + 1);
				suidRich.update(updateSku);
			});
		}
	}

	private SimpleSku getSimpleSku(Long skuId) {
		SimpleSku cond = new SimpleSku();
		cond.setId(skuId);
		return suidRich.selectOne(cond);
	}

	private void editCancelOrderStatus(CardGroupOrderInfo orderInfo) {
		CardGroupOrderInfo updateOrder = new CardGroupOrderInfo();
		updateOrder.setId(orderInfo.getId());
		updateOrder.setStatus(orderInfo.getStatus().intValue() == 202 ? 202L : 201L);
		updateOrder.setExpireTime(new Date());
		updateOrder.setServeStatus(201);
		orderInfoService.updateCardGroupOrderInfo(updateOrder);
	}

	@Override
	public void cancelDrawOrder(CardGroupOrderInfo orderInfo) {
		CardGroupOrderInfo updateOrder = new CardGroupOrderInfo();
		updateOrder.setId(orderInfo.getId());
		updateOrder.setStatus(201L);
		orderInfoService.updateCardGroupOrderInfo(updateOrder);
		if (Constants.ORDER_SUB_TYPE_PRIZE.equals(orderInfo.getOrderSubType())) {
			actPrizeRecordMapper.updateStatusByBaseOrderId(orderInfo.getId(), 0);
		}
	}

	@Override
	public void cancelDrawOrderNew(Long orderId) {
		rabbitTemplate.convertAndSend(MqConstans.QUEUE_DRAW_ORDER_EXPIRED, orderId);
	}

	/**
	 * 根据订单
	 *
	 * @param orderInfos
	 * @param inDir      true 增加 false 减少
	 */
	public void addPointByDir(CardGroupOrderInfo orderInfos, boolean inDir, String type) {
		mineApiService.addUserPoint(orderInfos, inDir, type);
	}

	private void incrSkuStock(OrderList orderList) {
		String lockKey = "MALL_BUY_LOCK_" + orderList.getSkuId();
		boolean isLock = false;
		try {
			isLock = redisUtils.tryLock(lockKey, TimeUnit.SECONDS, 30L);
			if (isLock) {
				skuService.updateSkuStockV2(orderList.getSkuId());
				long incr = redisUtils.incr(Constants.SKU_STOCK_PRE + orderList.getSkuId(), orderList.getQty());
				log.info("商品库存修改,skuId:{},缓存库存:{}",orderList.getSkuId(),incr);
				redisUtils.unlock(lockKey);
				isLock = false;
			}
		} catch (Exception e) {
			log.error("订单no:{}释放库存错误", orderList.getOrderNo(), e);
		} finally {
			try {
				if (isLock) {
					redisUtils.unlock(lockKey);
				}
			} catch (Exception e) {
				log.error("订单no:{}释放锁异常", orderList.getOrderNo(), e);
			}
		}
	}

	@Override
	public List<OrderList> selectByOrder(Long orderId) {
		OrderList condition = new OrderList();
		condition.setOrderId(orderId);
		return suidRich.select(condition);
	}

	@Override
	@RedisLock(lockKey="order_submit_lock+#{userId}",waitTime=5,leaseTime = 30)
	public OutDTO buyOfPoint(Integer userId, Integer skuId, Object addressId, Integer purchaseCount) {
		String userKey = Constants.GROUP_ORDER_LOCK + userId;
		redisUtils.set(userKey, 1, 10);
		String lockKey = "MALL_BUY_LOCK_" + skuId;
		boolean isLock;
		Transaction transaction = SessionFactory.getTransaction();
		try {
			isLock = redisUtils.tryLock(lockKey, TimeUnit.SECONDS, 15L);
			if (isLock) {
				Sku sku = skuService.selectOne(skuId);

				checkLimitQty(userId, sku, purchaseCount);
				if (!Constants.SKU_TYPE_POINT.equals(sku.getType())) {
					redisUtils.del(userKey);
					return OutDTO.error(500, "非积分兑换产品!");
				}
				if (sku.getSkuStock() < purchaseCount) {
					redisUtils.del(userKey);
					return OutDTO.error(500, "库存不足!");
				}
				AppUserInfoDto user = mineApiMapper.searchUserInfoById(userId);

				long point = sku.getPoint() * 100 * purchaseCount; // 用户兑换需要的积分
				Spu spuCond = new Spu();
				spuCond.setId(sku.getSpuId());
				Spu spu = suidRich.selectOne(spuCond);
				String userPointType;
				int discountPoint = 0; // 折扣积分
				if (StringUtils.isEmpty(spu.getPointType()) || Constants.POINT_TYPE_COMMON.equals(spu.getPointType())) {
					userPointType = Constants.POINT_TYPE_COMMON_SUB;
					// 如果是会员折扣商品
					String memBenefitsType = spu.getMemBenefitsType();
					if (Strings.isNotEmpty(memBenefitsType) && memBenefitsType.contains("goods_discount")) {
						// 如果有会员折扣
						BigDecimal discount = memberBenefitService.querySimpleBenefits(userId).getMemberDiscount();
						if (discount != null && discount.compareTo(BigDecimal.ZERO) > 0) {
							BigDecimal divide = new BigDecimal(10);
							if (discount.compareTo(new BigDecimal(10)) > 0) { // 折扣大于10 除以100
								divide = new BigDecimal(100);
							}
							discount = discount.divide(divide, 2, RoundingMode.DOWN);
							//discount = discount.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
							// 计算折后价格 四舍五入
							BigDecimal originalBigDecimal = new BigDecimal(sku.getPoint() * purchaseCount);
							BigDecimal discountedPoint = originalBigDecimal.multiply(discount).setScale(0, RoundingMode.HALF_UP);
							originalBigDecimal = originalBigDecimal.multiply(new BigDecimal(100));
							discountedPoint = discountedPoint.multiply(new BigDecimal(100));
							discountPoint = (originalBigDecimal.subtract(discountedPoint)).intValue();
							point = discountedPoint.longValue();
						}
					}
					if (user.getPoint() < point) {
						log.debug("用户:{},积分:{},需要积分:{}", user.getId(), user.getPoint(), point);
						redisUtils.del(userKey);
						return OutDTO.error500("积分不足!");
					}
				} else {
					Long pointByType = getUserPointByType(user.getId(), spu.getPointType());
					userPointType = spu.getPointType();
					if (pointByType < point) {
						log.debug("用户:{},积分:{},需要积分:{}", user.getId(), pointByType, point);
						redisUtils.del(userKey);
						return OutDTO.error500("积分不足!");
					}
				}

				if (new Date().before(sku.getSoldTime())) {
					redisUtils.del(userKey);
					throw new ServiceException(500, "还未开始【" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sku.getSoldTime()) + "】");
				}

				CardGroupOrderInfo orderInfo = new CardGroupOrderInfo();
				orderInfo.setOrderType(Constants.ORDER_TYPE_SHOP);
				orderInfo.setUserId(user.getId().longValue());
				orderInfo.setPurchaseCount(purchaseCount.longValue());
				orderInfo.setMerchantId(sku.getMerchantId());
				orderInfo.setDiscountPoint(discountPoint); // 设置折扣积分
				buildOrderAddress(addressId, orderInfo);
				OrderList orderList = new OrderList();
				orderList.setSkuPoint(sku.getPoint());
				orderList.setSkuCode(sku.getCode());
				orderList.setSkuId(sku.getId());
				orderList.setSpuId(sku.getSpuId());
				orderList.setSkuName(sku.getSkuName());
				orderList.setSkuImg(sku.getCarouselImgUrl());
				orderList.setQty(purchaseCount.longValue());
				orderList.setBrandId(sku.getBrandId());
				orderList.setBrandName(sku.getBrandName());
				orderList.setSecondCatName(sku.getSecondCatName());
				//orderList.setPointDeduction(purchaseCount*sku.getPoint()*100);
				orderList.setPointDeduction(point); // 积分兑换
				orderList.setDiscountPoint(discountPoint); // 折扣积分
				orderList.setStatus(100L);
				orderList.setRetailPrice(BigDecimal.ZERO);
				orderList.setActPrice(BigDecimal.ZERO);
				orderList.setAccountsPayable(BigDecimal.ZERO);
				// 直播时长
				orderList.setNeedLive(sku.getLiveMaxTime());
				//是否允许自提

				orderList.setPickUpSelf(spu.getPickUp());
				orderList.setChecklistBaseId(spu.getCheckListId());

				orderInfo.setAccountsPayable(BigDecimal.ZERO);
				orderInfo.setActualPayment(BigDecimal.ZERO);
				orderInfo.setPoint(orderList.getPointDeduction().intValue());
				orderInfo.setOrderNo(RandomUtil.getRandom(RandomUtil.MALL_USER_ORDER));
				orderInfo.setAppUserNickName(user.getNickname());
				orderInfo.setAppUserAvatar(user.getAvatar());
				boolean isCoupon = Constants.SKU_ACT_TYPE_COUPON.equals(sku.getActType());
				if (isCoupon) {
					orderInfo.setStatus(301L);
				} else {
					orderInfo.setStatus(103L);
				}
				transaction.begin();
				orderInfo.setPaymentStatus("支付成功");
				orderInfo.setPaymentTime(new Date());
				orderInfo.setPaymentType(Constants.SKU_TYPE_POINT);
				Long orderId = orderInfoService.insertCardGroupOrderInfo(orderInfo);
				//扣除积分
				addPointByDir(orderInfo, false, userPointType);
				orderList.setOrderId(orderId);
				orderList.setOrderNo(orderInfo.getOrderNo());
				orderList.setCreateTime(DateUtils.getTimestampNow());
				orderList.setSpuName(spu.getSpuName());
				suidRich.insert(orderList);
				//减少库存和售出数量
				Sku updateSku = new Sku();
				updateSku.setId(sku.getId());
				updateSku.setSkuStock(sku.getSkuStock() - purchaseCount);
				//updateSku.setRealStock(sku.getRealStock()-purchaseCount);
				//售卖数量改为触发器自动修改
				//updateSku.setSoldStock(sku.getSoldStock() + purchaseCount);
				suidRich.update(updateSku);
				if (isCoupon) {
					appUserCardRecordService.addUserCoupon(sku.getCouponId(), userId, "积分兑换");
				}
				transaction.commit();
				//orderListMapper.updateSpuSoldQty(orderList.getSpuId());
				redisUtils.unlock(lockKey);
				redisUtils.del(userKey);
				redisUtils.del(Constants.ACT_USER_TOKEN_CACHE + user.getId());
				return OutDTO.ok().put("order", orderInfo);
			}
		} catch (ServiceException e) {
			log.error("订单生成错误", e);
			transaction.rollback();
			redisUtils.del(userKey);
			return OutDTO.error(e.getCode(), e.getMessage());
		} catch (Exception e) {
			log.error("订单生成错误", e);
			transaction.rollback();
		} finally {
			try {
				redisUtils.unlock(lockKey);
			} catch (Exception e) {
				log.error("订单锁释放异常", e);
			}
		}
		redisUtils.del(userKey);
		return OutDTO.error(10041, "购买失败,请重试");
	}

	public Long getUserPointByType(Integer userId, String pointType) {
		AppUserPoint condition = new AppUserPoint();
		condition.setUserId(userId);
		condition.setType(pointType);
		List<AppUserPoint> userPointList = suidRich.select(condition);
		if (userPointList.isEmpty()) {
			return 0L;
		}
		return userPointList.get(0).getPoint();
	}

	@Override
	public List<OrderList> getOrderListByOrderId(Long orderId) {
		OrderList condition = new OrderList();
		condition.setOrderId(orderId);
		return suidRich.select(condition);
	}

	@Override
	@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
	public OrderSubmitDTO submitPrizeOrder(PrizeOrderParam prizeOrderParam) {
		AppUserInfoDto user = prizeOrderParam.getUser();
		List<DrawActPrizeDTO> drawPrizes = prizeOrderParam.getDrawPrizes();
		int purchaseCount = drawPrizes.stream().mapToInt(DrawActPrizeDTO::getTotalNum).sum();
		BigDecimal shippingCost = prizeOrderParam.getShippingCost();
		Sku sku = skuService.selectSkuByIdAndStatus(drawPrizes.get(0).getPrizeId(), null);
		CardGroupOrderInfo orderInfo = new CardGroupOrderInfo().setOrderType(Constants.ORDER_TYPE_SHOP).setOrderSubType(Constants.ORDER_SUB_TYPE_PRIZE)
				.setUserId(user.getId().longValue()).setAppUserNickName(user.getNickname()).setAppUserAvatar(user.getAvatar()).setStatus(100L)
				.setPurchaseCount((long) purchaseCount).setAccountsPayable(shippingCost).setActualPayment(shippingCost).setMerchantId(sku.getMerchantId())
				.setMerchantName(sku.getMerchantName()).setShippingCost(shippingCost).setOrderNo(RandomUtil.getRandom(RandomUtil.MALL_USER_ORDER))
				.setExpireTime(new Date(System.currentTimeMillis() + 1800 * 1000));
		buildOrderAddress(prizeOrderParam.getAddressId(), orderInfo);
		Long orderId = orderInfoService.insertCardGroupOrderInfo(orderInfo);
		List<OrderListDTO> orderLists = drawPrizes.stream().map(prize -> buildOrderListByPrize(prize, orderInfo)).collect(Collectors.toList());
		orderListMapper.insertOrderList(orderLists);
		actPrizeRecordMapper.updatePrizeShipStatus(user.getId(), orderId, prizeOrderParam.getPrizeIdList(), prizeOrderParam.getActTypes());
		log.info("新增奖品运费订单,订单no:{}", orderInfo.getOrderNo());
		rabbitTemplate.convertAndSend(MqConstans.EXCHANGE_DELAYED_ORDER_EXPIRED, MqConstans.ROUTING_KEY_ORDER_EXPIRED, orderId, message -> {
			message.getMessageProperties().setHeader("x-delay", 1800000);
			return message;
		});
		OrderSubmitDTO submitDTO = DozerUtils.map(orderInfo, OrderSubmitDTO.class);
		return submitDTO;
	}

	private OrderListDTO buildOrderListByPrize(DrawActPrizeDTO prize, CardGroupOrderInfo order) {
		Sku sku = skuService.selectSkuByIdAndStatus(prize.getPrizeId(), null);
		return new OrderListDTO().setSkuId(sku.getId()).setSpuId(sku.getSpuId()).setSkuName(sku.getSkuName()).setSkuImg(sku.getCarouselImgUrl())
				.setQty(prize.getTotalNum().longValue()).setSkuCode(sku.getCode()).setStatus(100L).setRetailPrice(BigDecimal.ZERO).setMerchantId(sku.getMerchantId())
				.setActPrice(BigDecimal.ZERO).setAccountsPayable(BigDecimal.ZERO).setBrandId(sku.getBrandId()).setBrandName(sku.getBrandName())
				.setSecondCatName(sku.getSecondCatName()).setOrderId(order.getId()).setOrderNo(order.getOrderNo());
	}
}
