package org.springblade.gateway.goods_gateway.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.generator.SnowflakeGenerator;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yeepay.yop.sdk.service.common.response.YopResponse;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springblade.common.utils.BeanPropertyUtil;
import org.springblade.common.utils.IPUtils;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.gateway.active_gateway.service.AppActiveProductService;
import org.springblade.gateway.goods_gateway.constant.HelpGoodsConstant;
import org.springblade.gateway.goods_gateway.exception.HelpGoodsException;
import org.springblade.gateway.goods_gateway.service.AppHelpGoodsService;
import org.springblade.gateway.point_gateway.enums.CmccPayStatus;
import org.springblade.gateway.point_gateway.enums.PointTypeEnum;
import org.springblade.payment.cmcc.exception.CmccRequestException;
import org.springblade.payment.cmcc.request.CmccDectOrderRequest;
import org.springblade.payment.cmcc.request.CmccPlaceOrderRequest;
import org.springblade.payment.cmcc.util.CmccUtil;
import org.springblade.sing.active.entity.ActiveHelpRecord;
import org.springblade.sing.goods.entity.GoodsRecord;
import org.springblade.sing.goods.entity.HelpGoods;
import org.springblade.sing.goods.enums.GoodsRecordType;
import org.springblade.sing.goods.enums.HelpGoodsType;
import org.springblade.sing.goods.service.IGoodsRecordService;
import org.springblade.sing.goods.service.IHelpGoodsService;
import org.springblade.sing.point.entity.CmccPointRecord;
import org.springblade.sing.point.entity.PointRecord;
import org.springblade.sing.point.service.ICmccPointRecordService;
import org.springblade.sing.point.service.IPointRecordService;
import org.springblade.sing.user.entity.LoginUser;
import org.springblade.sing.user.service.ILoginUserService;
import org.springblade.sing.yeepay.entity.YeepayOrder;
import org.springblade.sing.yeepay.enums.YeepayOrderPayStatus;
import org.springblade.sing.yeepay.service.IYeepayOrderService;
import org.springblade.wx.config.ConfigForOfficial;
import org.springblade.yeePay.common.YeePayConst;
import org.springblade.yeePay.common.YeepayApiConstant;
import org.springblade.yeePay.entity.InitOrderDto;
import org.springblade.yeePay.service.YeePayService;
import org.springblade.yeePay.service.YeepaySaasService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Arrays;

/**
 * @Author: Silent
 * @Description
 * @Date: Created in 14:28 2021/11/10
 * @Modified By:
 */
@Service
@Slf4j
public class AppHelpGoodsServiceImpl implements AppHelpGoodsService {
	@Autowired
	private ILoginUserService loginUserService;
	@Autowired
	private IPointRecordService pointRecordService;
	@Autowired
	private IHelpGoodsService helpGoodsService;
	@Autowired
	private ICmccPointRecordService cmccPointRecordService;
	@Autowired
	private IGoodsRecordService goodsRecordService;
	@Autowired
	private AppActiveProductService appActiveProductService;
	@Autowired
	private HttpServletRequest request;
	@Autowired
	private YeepaySaasService yeepaySaasService;
	@Autowired
	private IYeepayOrderService yeepayOrderService;
	@Autowired
	private YeePayConst yeePayConst;
	@Autowired
	private BladeLogger bladeLogger;
	@Autowired
	private YeePayService yeePayService;
	@Autowired
	private ConfigForOfficial configForOfficial;
	@Autowired
	private BladeRedis bladeRedis;

	/**
	 * 移动积分兑换道具
	 *
	 * @param cmccPointRecord
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void exchangeProps(CmccPointRecord cmccPointRecord, CmccDectOrderRequest cmccDectOrderRequest, Long productId, YeepayOrder yeepayOrder, String code) {
		//获取道具商品
		HelpGoods helpGoods = helpGoodsService.getById(cmccPointRecord.getHelpGoodsId());
		Assert.notNull(helpGoods, "没有找到该道具");

		//判断道具类型
		if (HelpGoodsType.CASH_EXCHANGE_GOODS.equals(helpGoods.getPayType()) && ObjectUtils.isEmpty(yeepayOrder)) {
			throw new HelpGoodsException("该道具需使用现金支付");
		}

		//判断验证码
		if (HelpGoodsType.POINT_EXCHANGE_GOODS.equals(helpGoods.getPayType()) && helpGoods.getPoint().compareTo(BigDecimal.ZERO) == 0) {
			String codeCache = bladeRedis.get(HelpGoodsConstant.getVerificationCodeKey(cmccPointRecord.getUserId(), productId, cmccPointRecord.getHelpGoodsId()));
			if (StringUtils.isEmpty(codeCache) || StringUtils.isEmpty(code) || !StringUtils.equals(codeCache, code)) {
				throw new HelpGoodsException("验证码错误");
			}
			bladeRedis.del(HelpGoodsConstant.getVerificationCodeKey(cmccPointRecord.getUserId(), productId, cmccPointRecord.getHelpGoodsId()));
		}

		//判断兑换数量(判断兑换次数大于零)
		Assert.isTrue(Integer.valueOf(-1).equals(BigDecimal.ZERO.compareTo(cmccPointRecord.getNum())), "兑换数量必须大于零");

		//判断道具是否上架
		Assert.isTrue(Integer.valueOf(1).equals(helpGoods.getStatus()), "该道具未上架");

		//获取用户信息
		LoginUser loginUser = loginUserService.getById(cmccPointRecord.getUserId());
		Assert.notNull(loginUser, "没有找到该用户");

		//用户兑换道具次数检测，并且添加兑换记录
		userExchangeCountCheck(cmccPointRecord, helpGoods, loginUser, yeepayOrder);

		//先助力
		ActiveHelpRecord activeHelpRecord = new ActiveHelpRecord();
		if (ObjectUtils.isNotEmpty(yeepayOrder)) {
			activeHelpRecord.setYeepayOrderId(yeepayOrder.getId());
		}
		activeHelpRecord.setProductId(productId);
		activeHelpRecord.setUserId(loginUser.getId());
		activeHelpRecord.setHelpGoodsId(helpGoods.getId());
		if (ObjectUtils.isNotEmpty(yeepayOrder)) {
			activeHelpRecord.setIp(yeepayOrder.getIp());
		} else {
			activeHelpRecord.setIp(IPUtils.getRealIp(request));
		}
		BigDecimal num = cmccPointRecord.getNum();
		BigDecimal votePointRate = helpGoods.getVotePointRate();
		activeHelpRecord.setVoteCount(Convert.toLong(num.multiply(votePointRate)));
		activeHelpRecord.setPhone(ObjectUtils.getIfNull(cmccPointRecord.getPhone(), loginUser::getPhone));
		appActiveProductService.activeHelp(activeHelpRecord);

		//助力之后判断是否需要移动积分兑换(防止先兑换之后，助力不成功问题)
		cmccPointExchange(cmccPointRecord, cmccDectOrderRequest, activeHelpRecord, helpGoods, loginUser);
	}

	/**
	 * 用户兑换道具次数检测，并且添加兑换记录
	 *
	 * @param cmccPointRecord
	 * @param helpGoods
	 * @param loginUser
	 */
	@Override
	public void userExchangeCountCheck(CmccPointRecord cmccPointRecord, HelpGoods helpGoods, LoginUser loginUser, YeepayOrder yeepayOrder) {
		//判断用户今天购买该商品次数是否超过限制(-1标识不限制次数)
		if (!helpGoods.getTotal().equals(-1L)) {
			//今日购买次数
			Long count = userHelpGoodsExchangeCount(loginUser.getId(), helpGoods.getId());
			//购买次数>=允许购买的次数
			if ((cmccPointRecord.getNum().longValue() + count) > helpGoods.getTotal()) {
				throw new HelpGoodsException("您兑换次数超过上限");
			}
		}
		//添加道具兑换记录
		GoodsRecord goodsRecord = new GoodsRecord();
		if (ObjectUtils.isNotEmpty(yeepayOrder)) {
			goodsRecord.setYeepayOrderId(yeepayOrder.getId());
		}
		goodsRecord.setUserId(loginUser.getId());
		goodsRecord.setHelpGoodsId(helpGoods.getId());
		goodsRecord.setNum(cmccPointRecord.getNum().longValue());
		goodsRecord.setType(GoodsRecordType.HELP_GOODS_EXCHANGE);
		Assert.isTrue(goodsRecordService.save(goodsRecord), "兑换失败");
	}

	/**
	 * 移动积分兑换
	 *
	 * @param cmccPointRecord
	 * @param cmccDectOrderRequest
	 * @param helpGoods
	 * @param loginUser
	 */
	@Override
	public void cmccPointExchange(CmccPointRecord cmccPointRecord, CmccDectOrderRequest cmccDectOrderRequest, ActiveHelpRecord activeHelpRecord, HelpGoods helpGoods, LoginUser loginUser) {
		//对接移动积分接口
		if (HelpGoodsType.POINT_EXCHANGE_GOODS.equals(helpGoods.getPayType()) && ObjectUtils.isNotEmpty(helpGoods.getPoint()) && helpGoods.getPoint().compareTo(BigDecimal.ZERO) > 0) {
			Assert.notNull(cmccDectOrderRequest.getOutOrderId(), "订单号不能为空");

			//查询移动订单记录
			CmccPointRecord oldCmccPointRecord = cmccPointRecordService.getOne(Wrappers.<CmccPointRecord>lambdaQuery().eq(CmccPointRecord::getOutOrderId, cmccDectOrderRequest.getOutOrderId()));
			Assert.notNull(oldCmccPointRecord, "没有找到移动支付订单");

			//判断是否是同一个道具
			if (ObjectUtils.notEqual(helpGoods.getId(), oldCmccPointRecord.getHelpGoodsId())) {
				throw new HelpGoodsException("您兑换的道具和您的订单不相同");
			}

			//判断数量是否相同
			if (ObjectUtils.notEqual(cmccPointRecord.getNum(), oldCmccPointRecord.getNum())) {
				throw new HelpGoodsException("您兑换的道具和您的订单数量不相同");
			}

			//添加移动积分兑换记录
			PointRecord pointRecord = new PointRecord();
			pointRecord.setPoint(oldCmccPointRecord.getPoint());
			pointRecord.setActiveId(activeHelpRecord.getActiveId());
			pointRecord.setActiveProductId(activeHelpRecord.getProductId());
			pointRecord.setCmccId(oldCmccPointRecord.getId());
			pointRecord.setHelpGoodsId(helpGoods.getId());
			pointRecord.setUserId(loginUser.getId());
			pointRecord.setPointType(PointTypeEnum.CMCC_POINT_EXCHANGE);
			Assert.isTrue(pointRecordService.save(pointRecord), "兑换失败");

			//设置状态为支付成功
			oldCmccPointRecord.setPayStatus(CmccPayStatus.SUCCESS);
			//设置活动
			oldCmccPointRecord.setActiveId(activeHelpRecord.getActiveId());
			//设置作品
			oldCmccPointRecord.setActiveProductId(activeHelpRecord.getActiveId());

			//更新支付状态
			Assert.isTrue(cmccPointRecordService.updateById(oldCmccPointRecord));

			//移动支付逻辑
			cmccDectOrderRequest.setMobile(oldCmccPointRecord.getPhone());
			try {
				CmccUtil.dectOrder(cmccDectOrderRequest);
			} catch (CmccRequestException e) {
				//支付失败逻辑
				throw new HelpGoodsException(e.getMessage());
			}
		}
	}

	/**
	 * 用户今日道具兑换次数
	 *
	 * @param userId      用户ID
	 * @param helpGoodsId 道具ID
	 * @return
	 */
	@Override
	public Long userHelpGoodsExchangeCount(Long userId, Long helpGoodsId) {
		Long count = MapUtil.getLong(goodsRecordService.getMap(Wrappers.<GoodsRecord>query()
			.select(new StringBuilder("ifnull(sum(")
				.append(BeanPropertyUtil.getFieldNameToUnder(GoodsRecord::getNum))
				.append("),0) as count")
				.toString())
			.lambda()
			.eq(GoodsRecord::getUserId, userId)
			.eq(GoodsRecord::getHelpGoodsId, helpGoodsId)
			.eq(GoodsRecord::getType, GoodsRecordType.HELP_GOODS_EXCHANGE.name())
			.last(new StringBuilder("and date_format(")
				.append(BeanPropertyUtil.getFieldNameToUnder(PointRecord::getCreateTime))
				.append(",\"%Y-%m-%d\")=\"")
				.append(DateUtil.format(DateUtil.now(), "yyyy-MM-dd").trim()).append("\"").toString())), "count");
		return count;
	}

	/**
	 * 创建道具订单
	 *
	 * @param cmccPointRecord
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void createPropsOrder(CmccPointRecord cmccPointRecord, CmccPlaceOrderRequest cmccPlaceOrderRequest) {
		//获取道具id
		HelpGoods helpGoods = helpGoodsService.getById(cmccPointRecord.getHelpGoodsId());
		Assert.notNull(helpGoods, "没有找到该道具");

		//判断道具类型
		Assert.isTrue(HelpGoodsType.POINT_EXCHANGE_GOODS.equals(helpGoods.getPayType()), "该道具不允许积分支付");

		//判断兑换数量(判断兑换次数大于零)
		Assert.isTrue(Integer.valueOf(-1).equals(BigDecimal.ZERO.compareTo(cmccPointRecord.getNum())), "兑换数量必须大于零");

		//判断道具是否上架
		Assert.isTrue(Integer.valueOf(1).equals(helpGoods.getStatus()), "该道具未上架");

		//判断道具是否需要积分支付
		if (BigDecimal.ZERO.equals(helpGoods.getPoint())) {
			throw new HelpGoodsException("该道具不需要积分支付");
		}

		//设置状态为待支付
		cmccPointRecord.setPayStatus(CmccPayStatus.WAIT);
		//生成随机订单号
		cmccPointRecord.setOutOrderId(IdUtil.randomUUID());
		//设置积分
		cmccPointRecord.setPoint(cmccPointRecord.getNum().multiply(helpGoods.getPoint()));

		//设置商品属性
		CmccPlaceOrderRequest.ProductItem productItem = new CmccPlaceOrderRequest.ProductItem();
		productItem.setNum(cmccPointRecord.getNum().toString());
		productItem.setProductId(helpGoods.getCmccGoodsId());

		//设置商品列表
		cmccPlaceOrderRequest.setProductItemList(JSONObject.toJSONString(Arrays.asList(productItem)));
		cmccPlaceOrderRequest.setMobile(cmccPointRecord.getPhone());
		//移动积分价值需要 x 1.2(我不知道为啥，可以为了赚中间差距)
		cmccPlaceOrderRequest.setPoints(cmccPointRecord.getNum().multiply(helpGoods.getPoint()).toString());

		Assert.isTrue(cmccPointRecordService.save(cmccPointRecord), "创建订单失败");

		// TODO 移动订单创建逻辑
		//设置请求属性
		cmccPlaceOrderRequest.setOutOrderId(cmccPointRecord.getOutOrderId());
		try {
			CmccUtil.placeOrder(cmccPlaceOrderRequest);
		} catch (CmccRequestException e) {
			throw new HelpGoodsException(e.getMessage());
		}
	}

	/**
	 * 创建现金道具订单
	 *
	 * @param yeepayOrder
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void createCashPropsOrder(YeepayOrder yeepayOrder) {
		//获取道具id
		HelpGoods helpGoods = helpGoodsService.getById(yeepayOrder.getHelpGoodsId());
		Assert.notNull(helpGoods, "没有找到该道具");

		//判断道具类型
		Assert.isTrue(HelpGoodsType.CASH_EXCHANGE_GOODS.equals(helpGoods.getPayType()), "该道具不允许现金支付");

		//判断兑换数量(判断兑换次数大于零)
		Assert.isTrue(Integer.valueOf(-1).equals(BigDecimal.ZERO.compareTo(BigDecimal.valueOf(yeepayOrder.getNum()))), "兑换数量必须大于零");

		//判断道具是否上架
		Assert.isTrue(Integer.valueOf(1).equals(helpGoods.getStatus()), "该道具未上架");

		//判断道具是否需要现金支付
		if (BigDecimal.ZERO.equals(helpGoods.getAmount())) {
			throw new HelpGoodsException("该道具不需要现金支付");
		}

		//设置数据
		//金额
		BigDecimal amount = helpGoods.getAmount().multiply(BigDecimal.valueOf(yeepayOrder.getNum()));
		//回调地址
		String callbackUrl = new StringBuilder(yeePayConst.getServiceUrl())
			.append("/app/help/goods/callbackUrl").toString();
		yeepayOrder.setId(new SnowflakeGenerator().next());
		yeepayOrder.setPayStatus(YeepayOrderPayStatus.待付款);
		yeepayOrder.setActiveId(helpGoods.getActiveId());
		yeepayOrder.setAmount(amount);
		yeepayOrder.setIp(IPUtils.getRealIp(request));
		yeepayOrder.setHelpGoodsName(helpGoods.getName());
		yeepayOrder.setCallbackUrl(callbackUrl);
		yeepayOrder.setCreateTime(DateUtil.now());

		// 请求易宝
		try {
			InitOrderDto initOrderDto = InitOrderDto
				.builder()
				.orderId(yeepayOrder.getId().toString())
				.orderAmount(amount.doubleValue())
				.goodsName(helpGoods.getName())
				.channel(YeepayApiConstant.channelType.WECHAT.name())
				.scene(YeepayApiConstant.sceneType.OFFLINE.name())
				.payWay(YeepayApiConstant.payWayType.WECHAT_OFFIACCOUNT.name())
				.userIp(yeepayOrder.getIp())
				.userId(yeepayOrder.getOpenId())
				.appId(configForOfficial.getAppId())
				//支付订单有效期为一天
				.expiredTime(DateUtil.format(cn.hutool.core.date.DateUtil.offsetDay(DateUtil.now(), 1), DateUtil.PATTERN_DATETIME))
				.notifyUrl(callbackUrl)
				.build();
			//设置易宝配置内容
			initOrderDto.setParentMerchantNo(yeePayConst.getPlatformServiceNo());
			initOrderDto.setMerchantNo(yeePayConst.getMerchantNo());

			bladeLogger.info("创建现金支付道具订单参数", JSONObject.toJSONString(initOrderDto));
			YopResponse yopResponse = yeepaySaasService.aggPrepay(initOrderDto);

			//返回数据
			String stringResult = yopResponse.getStringResult();
			JSONObject jsonObject = JSONObject.parseObject(stringResult);
			if (!StringUtils.equals(jsonObject.getString("code"), "00000")) {
				bladeLogger.error("创建道具订单错误", jsonObject.getString("message"));
				log.error("创建道具订单错误:{}", jsonObject.getString("message"));
				throw new HelpGoodsException("创建订单失败");
			}
			yeepayOrder.setUniqueOrderNo(jsonObject.getString(BeanPropertyUtil.getFieldName(YeepayOrder::getUniqueOrderNo)));
			yeepayOrder.setQrCodeUrl(jsonObject.getString(BeanPropertyUtil.getFieldName(YeepayOrder::getQrCodeUrl)));
			yeepayOrder.setBankOrderId(jsonObject.getString(BeanPropertyUtil.getFieldName(YeepayOrder::getBankOrderId)));
			yeepayOrder.setPrePayTn(jsonObject.getString(BeanPropertyUtil.getFieldName(YeepayOrder::getPrePayTn)));

			//保存订单
			Assert.isTrue(yeepayOrderService.save(yeepayOrder), "创建订单失败");
		} catch (Exception e) {
			throw new HelpGoodsException(e.getMessage());
		}
	}

	/**
	 * 处理现金道具订单
	 *
	 * @param request
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void handlerCasePropsOrder(HttpServletRequest request) {

		JSONObject jsonObject = yeePayService.deCodeNotifyData(request.getParameterMap().get("cipherText")[0]);
		log.info("易宝道具订单支付回调支付解密回调密文{}", jsonObject.toJSONString());
		bladeLogger.info("易宝道具订单支付回调支付解密回调密文{}", jsonObject.toJSONString());

		//根据回调信息，获取订单
		Long orderId = jsonObject.getLong(BeanPropertyUtil.getFieldName(InitOrderDto::getOrderId));
		YeepayOrder yeepayOrder = yeepayOrderService.getById(orderId);

		//设置订单状态
		yeepayOrder.setPayStatus(YeepayOrderPayStatus.付款成功);
		//设置回调密文
		yeepayOrder.setCipherText(request.getParameterMap().get("cipherText")[0]);
		Assert.isTrue(yeepayOrderService.updateById(yeepayOrder), "修改订单状态错误");

		//根据订单，进行投票
		CmccPointRecord cmccPointRecord = new CmccPointRecord();
		cmccPointRecord.setHelpGoodsId(yeepayOrder.getHelpGoodsId());
		cmccPointRecord.setUserId(yeepayOrder.getUserId());
		cmccPointRecord.setNum(BigDecimal.valueOf(yeepayOrder.getNum()));
		//投票
		exchangeProps(cmccPointRecord, null, yeepayOrder.getProductId(), yeepayOrder, null);
	}
}
