package com.fzm.mall.server.admin.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.mall.server.admin.block_chain.util.ChainUtil;
import com.fzm.mall.server.admin.constant.MALLGlobalConfig;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.goods_center.entity.vo.GoodsSpuVO;
import com.fzm.mall.server.admin.goods_center.mapper.SpuMapper;
import com.fzm.mall.server.admin.goods_center.model.Spu;
import com.fzm.mall.server.admin.home_page.model.Merchant;
import com.fzm.mall.server.admin.home_page.service.IMerchantService;
import com.fzm.mall.server.admin.mq.MsgProducer;
import com.fzm.mall.server.admin.mq.enums.QueueTTLTypeEnum;
import com.fzm.mall.server.admin.order.entity.dto.TranDto;
import com.fzm.mall.server.admin.order.mapper.PayMapper;
import com.fzm.mall.server.admin.order.model.OrderDetail;
import com.fzm.mall.server.admin.order.model.Pay;
import com.fzm.mall.server.admin.pay.dto.WxPayRefundDTO;
import com.fzm.mall.server.admin.user.mapper.AssetMapper;
import com.fzm.mall.server.admin.user.model.Asset;
import com.fzm.mall.server.admin.user.service.IAssetService;
import com.fzm.mall.server.admin.user_center.model.User;
import com.fzm.mall.server.admin.user_center.service.IUserService;
import com.fzm.mall.server.admin.util.WxPayUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 用户资产信息表 服务实现类
 * </p>
 *
 * @author fzm
 * @since 2021-04-12
 */
@Service
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AssetServiceImpl extends ServiceImpl<AssetMapper, Asset> implements IAssetService {

    @Value("${pay.pay-in-chain}")
    private Boolean payInChain;

    @Autowired
    private AssetMapper assetMapper;
    @Autowired
    private IUserService userService;
    @Autowired
    private IMerchantService merchantService;
    @Autowired
    private ChainUtil chainUtil;
    @Autowired
    private MsgProducer producer;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private PayMapper payMapper;
    @Autowired
    private WxPayUtil wxPayUtil;

    /**
     * 商品订单退款-积分还原，商品币还原
     * <p>
     * 审核通过：1.币：根据order_detail的coin和number，将user_asset对应的冻结数量frozen扣除，通过链上方法还给商家
     * 2.积分：根据order_info的amount，将积分还给买家
     * <p>
     * 审核不通过：
     * 1.币：根据order_detail的coin和number，将user_asset对应的冻结数量frozen扣除，加到可用资产amount
     *
     * @param uid
     * @param amount
     * @param merchantId
     * @param orderDetailList
     * @param isApproved
     */
    @Override
    public void integralDeduction(String oid, String uid, BigDecimal amount, String merchantId, List<OrderDetail> orderDetailList, Boolean isApproved) {
        orderDetailCoinRefund(uid, merchantId, orderDetailList, isApproved);

        Pay pay = payMapper.readByOid(oid);
        switch (pay.getPayType()) {
            case 4:
                //积分支付
                intergralRefund(uid, amount, isApproved);
                break;
            case 2:
                //微信支付
                wxPayRefund(pay.getPaidAmount(), pay.getOutBizNo(), isApproved);
                break;
        }
    }

    /**
     * 微信退款
     *
     * @param paidAmount 金额
     * @param outBizNo   原支付交易对应的微信订单号
     */
    private void wxPayRefund(BigDecimal paidAmount, String outBizNo, Boolean isApproved) {
        if (!isApproved) {
            return;
        }
        WxPayRefundDTO data = new WxPayRefundDTO();
        data.init(paidAmount, outBizNo);

        String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
        String bodyJsonString = JSONObject.toJSONString(data);
        wxPayUtil.doHttpPost(url, bodyJsonString);
    }

    /**
     * 审核通过：积分：根据order_info的amount，将积分还给买家
     * <p>
     * 审核不通过：不操作积分
     *
     * @param uid
     * @param amount
     * @param isApproved
     */
    private void intergralRefund(String uid, BigDecimal amount, Boolean isApproved) {
        if (!isApproved) {
            return;
        }
        QueryWrapper<Asset> queryWrapper = new QueryWrapper<Asset>().eq("uid", uid).eq("coin", MALLGlobalConfig.PLATFORM_BASE_INTEGRAL);
        Asset asset = assetMapper.selectOne(queryWrapper);
        asset.setAmount(asset.getAmount().add(amount));

        int update = assetMapper.update(asset, new UpdateWrapper<Asset>().eq("uid", uid).eq("coin", MALLGlobalConfig.PLATFORM_BASE_INTEGRAL));
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }
    }


    /**
     * 审核通过：1.币：根据order_detail的coin和number，将user_asset对应的冻结数量frozen扣除，通过链上方法还给商家
     * 审核不通过：
     * 1.币：根据order_detail的coin和number，将user_asset对应的冻结数量frozen扣除，加到可用资产amount
     *
     * @param uid
     * @param merchantId
     * @param orderDetailList
     * @param isApproved
     */
    private void orderDetailCoinRefund(String uid, String merchantId, List<OrderDetail> orderDetailList, Boolean isApproved) {
        if (isApproved) {
            orderDetailList.forEach(orderDetail -> {
                QueryWrapper<Asset> queryWrapper = new QueryWrapper<Asset>().eq("uid", uid).eq("coin", orderDetail.getCoin());
                Asset asset = assetMapper.selectOne(queryWrapper);
                BigDecimal frozen = asset.getFrozen();
                BigDecimal unfreezeNum = new BigDecimal(orderDetail.getNumber());
                BigDecimal result = frozen.subtract(unfreezeNum);
                frozen = result.compareTo(BigDecimal.ZERO) > 0 ? result : BigDecimal.ZERO;
                asset.setFrozen(frozen);

                int update = assetMapper.update(asset, new UpdateWrapper<Asset>().eq("uid", uid).eq("coin", orderDetail.getCoin()));
                if (update == 0) {
                    throw new MyException(MallResponseError.UPDATE_FAIL);
                }

                refundOrderDetailCoin(uid, merchantId, orderDetail);
            });
        } else {
            orderDetailList.forEach(orderDetail -> {
                QueryWrapper<Asset> queryWrapper = new QueryWrapper<Asset>().eq("uid", uid).eq("coin", orderDetail.getCoin());
                Asset asset = assetMapper.selectOne(queryWrapper);
                BigDecimal frozen = asset.getFrozen();
                BigDecimal unfreezeNum = new BigDecimal(orderDetail.getNumber());
                BigDecimal result = frozen.subtract(unfreezeNum);
                frozen = result.compareTo(BigDecimal.ZERO) > 0 ? result : BigDecimal.ZERO;
                asset.setFrozen(frozen);

                asset.setAmount(asset.getAmount().add(unfreezeNum));

                int update = assetMapper.update(asset, new UpdateWrapper<Asset>().eq("uid", uid).eq("coin", orderDetail.getCoin()));
                if (update == 0) {
                    throw new MyException(MallResponseError.UPDATE_FAIL);
                }
            });
        }
    }

    private void refundOrderDetailCoin(String uid, String merchantId, OrderDetail orderDetail) {
        //根据开关判断是否走链，默认不走链
        if (payInChain) {
            User user = userService.getOne(new QueryWrapper<User>().eq("uid", uid));
            Merchant merchant = merchantService.getOne(new QueryWrapper<Merchant>().eq("merchant_id", merchantId));
            TranDto tranDto = new TranDto();
            tranDto.setFromAddr(user.getAddress());
            tranDto.setToAddr(merchant.getTokenIssueAddr());
            tranDto.setCoin(orderDetail.getCoin());
            //一个商品对应一个币，此处构造不存在精度丢失问题
            tranDto.setAmount(new BigDecimal(orderDetail.getNumber()));
            tranDto.setTableId(orderDetail.getOid());
            tranDto.setTableType(TranDto.TABLE_TYPE_ORDER_INFO);

            //判断商品是否为NFT
            GoodsSpuVO goodsSpu = spuMapper.readByCoin(orderDetail.getCoin());
            goodsSpu.initTokenId();
            String hash;
            if (goodsSpu.getCommodityPass().equals(Spu.COMMODITY_PASS_OUT)) {
                long[] tokenIds = new long[]{goodsSpu.getTokenId()};
                long[] amounts = new long[]{tranDto.getAmount().longValue()};
                hash = chainUtil.evmExec(tokenIds, amounts, tranDto.getFromAddr(), tranDto.getToAddr());
                tranDto.setCoinType(TranDto.COIN_TYPE_NFT_OUT);
            } else {
                hash = chainUtil.evmTransfer(tranDto.getToAddr(), tranDto.getFromAddr(), goodsSpu.getTokenId(), tranDto.getAmount().longValue());
                tranDto.setCoinType(TranDto.COIN_TYPE_NFT_IN);
            }
            tranDto.setHash(hash);
            producer.sendTTL(JSON.toJSONString(tranDto), QueueTTLTypeEnum.ORDER_REFUND_COINS_TTL, 5 * 1000);
        }
    }
}
