package com.crm.ucard.service.impl;

import com.alibaba.fastjson.JSON;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.constant.PayConstant;
import com.crm.rely.backend.core.dto.base.BaseResultDto;
import com.crm.rely.backend.core.dto.wasabi.WasabiBalanceInfoDto;
import com.crm.rely.backend.core.dto.wasabi.WasabiDepositDto;
import com.crm.rely.backend.core.entity.ucard.UcardDepositEntity;
import com.crm.rely.backend.core.entity.ucard.UcardPayCallbackEntity;
import com.crm.rely.backend.core.entity.wasabi.WasabiBalanceEntity;
import com.crm.rely.backend.core.entity.wasabi.WasabiBalanceInfoEntity;
import com.crm.rely.backend.core.enums.CardStatusTypeEnum;
import com.crm.rely.backend.core.pojo.table.ucard.*;
import com.crm.rely.backend.exception.ServiceException;
import com.crm.rely.backend.service.MqSendService;
import com.crm.rely.backend.util.GetRandom;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
import com.crm.rely.backend.util.MapUtil;
import com.crm.ucard.config.WasabiConfig;
import com.crm.ucard.dao.repository.*;
import com.crm.ucard.model.ResultData;
import com.crm.ucard.service.*;
import com.crm.ucard.util.ObjectIdUtil;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Connection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@Service
public class UcardWalletServiceImpl implements UcardWalletService {

    @Autowired
    private UcardWalletRepository ucardWalletRepository;
    @Autowired
    private UcardWalletRecordRepository ucardWalletRecordRepository;
    @Autowired
    private MqSendService mqSendService;
    @Autowired
    private CardOperateRepository cardOperateRepository;
    @Autowired
    private CardMerchantUserRepository merchantUsereRepository;
    @Autowired
    private UcardCardRepository cardRepository;
    @Autowired
    private SysUcardConfigService sysUcardConfigService;
    @Autowired
    private UcardCardService ucardCardService;
    @Autowired
    private CardWithdrawRepository withdrawRepository;
    @Autowired
    private WasabiDocumentApiService wasabiDocumentApiService;
    @Autowired
    private UcardEmailService ucardEmailService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto ucardWalletDeposit(UcardDepositEntity entity) throws Exception {
        if (Strings.isNullOrEmpty(entity.getSign())) {
            return BaseResultDto.error("signature is empty");
        }
        String sign = getSign(entity);
        if (!entity.getSign().equals(sign)) {
            return BaseResultDto.error("signature error");
        }
        CardMerchantUserTable userTable = merchantUsereRepository.findByCId(entity.getCId());
        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setOperateUser(entity.getCId());
        cardOperateTable.setOperateType(10);
        cardOperateTable.setAddTime(new Date());
        cardOperateTable.setUniqueId(userTable.getUniqueId());

        UcardPayCallbackEntity callbackEntity = new UcardPayCallbackEntity();
        callbackEntity.setOrderNo(entity.getOrderNo());
        callbackEntity.setInternalOrderNo(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        callbackEntity.setAmount(new BigDecimal(entity.getAmount()).negate());
        callbackEntity.setCallBackUrl(entity.getCallBackUrl());

        // type 1:卡。2:钱包
        if (StringUtils.isNotEmpty(entity.getType()) && "1".equals(entity.getType())){
            // 卡 根据卡号查询余额。判断余额是否充足
            UcardCardTable cardTable = cardRepository.getByCardNumber(entity.getCardNumber());
            WasabiBalanceInfoEntity infoEntity = new WasabiBalanceInfoEntity();
            infoEntity.setCardNo(cardTable.getCardNo());
            WasabiBalanceInfoDto infoDto;
            try {
                infoDto = ucardCardService.getBalanceInfo(infoEntity).getData();
            } catch (Exception e){
                cardOperateTable.setStatus(Constants.FAIL);
                cardOperateRepository.save(cardOperateTable);

                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("103");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
                log.error("ucard deposit error:{}", e.getMessage());
                return BaseResultDto.success();
            }

            if (infoDto.getAmount().compareTo(new BigDecimal(entity.getAmount())) < 0) {
                cardOperateTable.setStatus(Constants.FAIL);
                cardOperateRepository.save(cardOperateTable);

                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("104");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
                return BaseResultDto.success();
            }
            // 调用三方扣款接口进行扣款
            WasabiConfig config = getWasabiConfig();
            WasabiBalanceEntity balanceEntity = new WasabiBalanceEntity();
            balanceEntity.setCardNo(cardTable.getCardNo());
            balanceEntity.setMerchantOrderNo(ObjectIdUtil.genSnowFlakeIdStr(config.getMerchantTag()));
            balanceEntity.setAmount(new BigDecimal(entity.getAmount()));
            balanceEntity.setCId(entity.getCId());
            ResultData<WasabiDepositDto> resultData = wasabiDocumentApiService.withdraw(balanceEntity, config);
            if (!resultData.isSuccess()) {
                cardOperateTable.setStatus(Constants.FAIL);
                cardOperateRepository.save(cardOperateTable);

                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("103");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
                log.error("ucard deposit error:{}", resultData.getMsg());
                return BaseResultDto.success();
            }
            // 记录扣款订单
            CardWithdrawTable withdrawTable = new CardWithdrawTable();
            WasabiDepositDto dto = resultData.getData();
            BeanUtils.copyProperties(dto, withdrawTable);
            withdrawTable.setCardNumber(cardTable.getCardNumber());
            withdrawTable.setFee(BigDecimal.ZERO);
            withdrawTable.setUniqueId(cardTable.getUniqueId());
            withdrawTable.setType("2");
            withdrawTable.setInternalOrderNo(callbackEntity.getInternalOrderNo());
            withdrawTable.setCallbackUrl(callbackEntity.getCallBackUrl());
            withdrawTable.setAmount(callbackEntity.getAmount());
            withdrawTable.setDepositOrderNo(callbackEntity.getOrderNo());
            withdrawTable.setAddTime(new Date());
            withdrawRepository.save(withdrawTable);

            cardOperateTable.setStatus(dto.getStatus());
            cardOperateRepository.save(cardOperateTable);

            if (CardStatusTypeEnum.SUCCESS.getCode().equals(dto.getStatus())){
                callbackEntity.setStatus(2);
                callbackEntity.setApproveDesc("");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
            } else if (CardStatusTypeEnum.FAIL.getCode().equals(dto.getStatus())){
                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("103");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
            }
        } else if ("2".equals(entity.getType())){
            // 钱包
            UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(entity.getCId());
            if (walletTable == null) {
                cardOperateTable.setStatus(Constants.FAIL);
                cardOperateRepository.save(cardOperateTable);

                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("102");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
                return BaseResultDto.success();
            }
            if(walletTable.getBalance().compareTo(new BigDecimal(entity.getAmount()).add(entity.getFee()))<0){
                cardOperateTable.setStatus(Constants.FAIL);
                cardOperateRepository.save(cardOperateTable);

                callbackEntity.setStatus(3);
                callbackEntity.setApproveDesc("104");
                mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
                return BaseResultDto.success();
            }

            walletTable.setBalance(walletTable.getBalance().subtract(new BigDecimal(entity.getAmount())));
            ucardWalletRepository.save(walletTable);

            UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
            recordTable.setWalletId(walletTable.getId());
            recordTable.setCId(walletTable.getCId());
            recordTable.setUniqueId(walletTable.getUniqueId());
            recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
            recordTable.setOrderNo(entity.getOrderNo());
            recordTable.setAmount(new BigDecimal(entity.getAmount()).negate());
            recordTable.setFee(entity.getFee());
            recordTable.setType(1);
            recordTable.setStatus(2);
            recordTable.setCallBackUrl(entity.getCallBackUrl());
            recordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(recordTable);

            cardOperateTable.setStatus(Constants.SUCCESS);
            cardOperateRepository.save(cardOperateTable);

            ucardEmailService.sendWalletWithdrawalSuccessEmail(recordTable.getAmount(), recordTable.getFee(), recordTable.getAddTime(), recordTable.getUniqueId());
            callbackEntity.setStatus(2);
            callbackEntity.setApproveDesc("");
            mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
        }
        return BaseResultDto.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto ucardWalletWithdraw(UcardDepositEntity entity) throws Exception {
        if (Strings.isNullOrEmpty(entity.getSign())) {
            return BaseResultDto.error("signature is empty");
        }
        String sign = getSign(entity);
        if (!entity.getSign().equals(sign)) {
            return BaseResultDto.error("signature error");
        }
        UcardWalletTable walletTable = ucardWalletRepository.findFirstByCId(entity.getCId());

        UcardPayCallbackEntity callbackEntity = new UcardPayCallbackEntity();
        callbackEntity.setCallBackUrl(entity.getCallBackUrl());
        callbackEntity.setInternalOrderNo(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        callbackEntity.setOrderNo(entity.getOrderNo());
        callbackEntity.setAmount(new BigDecimal(entity.getAmount()));
        callbackEntity.setStatus(2);
        callbackEntity.setApproveDesc("");
        if (walletTable == null) {
            callbackEntity.setStatus(3);
            callbackEntity.setApproveDesc("102");
            mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
            return BaseResultDto.success(Constants.SUCCESS,PayConstant.WALLET_IS_EMPTY);
        }
        walletTable.setBalance(walletTable.getBalance().add(new BigDecimal(entity.getAmount())));
        ucardWalletRepository.save(walletTable);

        UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
        recordTable.setWalletId(walletTable.getId());
        recordTable.setCId(walletTable.getCId());
        recordTable.setUniqueId(walletTable.getUniqueId());
        recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        recordTable.setOrderNo(entity.getOrderNo());
        recordTable.setAmount(new BigDecimal(entity.getAmount()));
        recordTable.setFee(entity.getFee());
        recordTable.setType(2);
        recordTable.setStatus(2);
        recordTable.setCallBackUrl(entity.getCallBackUrl());
        recordTable.setAddTime(new Date());
        ucardWalletRecordRepository.save(recordTable);

        CardMerchantUserTable userTable = merchantUsereRepository.findByCId(walletTable.getCId());
        CardOperateTable cardOperateTable = new CardOperateTable();
        cardOperateTable.setOperateUser(walletTable.getCId());
        cardOperateTable.setOperateType(11);
        cardOperateTable.setStatus(Constants.SUCCESS);
        cardOperateTable.setAddTime(new Date());
        cardOperateTable.setUniqueId(userTable.getUniqueId());
        cardOperateRepository.save(cardOperateTable);
        //发送邮件
        ucardEmailService.sendWalletRechargeSuccessEmail(recordTable, userTable);
        mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,callbackEntity);
        return BaseResultDto.success();
    }

    @JmsListener(destination = Constants.UCARD_DEPOSIT_CALLBACK)
    @Transactional(rollbackFor = Exception.class)
    public void callback(String content) throws Exception {
        log.info(String.format("UCARD_CARD_DEPOSIT_CALLBACK,content:%s", content));
        UcardPayCallbackEntity entity = JSON.parseObject(content, UcardPayCallbackEntity.class);
        if (null == entity) {
            log.error("UCARD_CARD_DEPOSIT_CALLBACK,callbackEntity is null");
            return;
        }
        if (entity.getAmount().compareTo(new BigDecimal(0)) < 0){
            entity.setAmount(entity.getAmount().negate());
        }
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("orderNo", entity.getOrderNo());
        map.put("internalOrderNo", entity.getInternalOrderNo());
        map.put("amount", entity.getAmount());
        map.put("status", entity.getStatus());
        map.put("callBackUrl", entity.getCallBackUrl());
        map.put("approveDesc", entity.getApproveDesc());

        String signString = MapUtil.getStringSortByKey(MapUtil.convertToStringMap(map));
        String resign = MD5Util.getMD5(signString);
        map.put("sign",resign);

        Connection.Response response = HttpUtil.post(entity.getCallBackUrl(), JSON.toJSONString(map));
        log.info(String.format("url:%s,json:%s,body:%s", entity.getCallBackUrl() ,JSON.toJSON(map), response.body()));

        BaseResultDto requestEntity = JSON.parseObject(response.body(),BaseResultDto.class);
        if (requestEntity == null || requestEntity.getCode()!=200) {
            mqSendService.send(Constants.UCARD_DEPOSIT_CALLBACK,entity,10 * 60 * 1000);
        }
    }

    private String getSign(UcardDepositEntity entity) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("cId", String.valueOf(entity.getCId()));
        map.put("orderNo",entity.getOrderNo());
        map.put("amount", entity.getAmount());
        map.put("fee", String.valueOf(entity.getFee()));
        map.put("callBackUrl", entity.getCallBackUrl());
        map.put("withdrawInfoType", String.valueOf(entity.getWithdrawInfoType()));
        map.put("type", entity.getType());
        map.put("cardNumber", entity.getCardNumber());
        String sign = MD5Util.getMD5(MapUtil.getStringSortByKey(map));
        log.info(String.format("结果加密处理--请求orderNo:%s,sign string:%s", entity.getOrderNo(), sign));

        return sign;
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("orderNo", "12445");
        map.put("amount", "100");
        map.put("callBackUrl", "callBackUrl");

        String signString = MapUtil.getStringSortByKey(MapUtil.convertToStringMap(map));
        String resign = MD5Util.getMD5(signString);
        map.put("sign",resign);
        Connection.Response response = HttpUtil.post("http://192.168.0.25:8004/ucard/withdraw/callback", JSON.toJSONString(map));
        log.info(String.format("url:%s,json:%s,body:%s", "http://192.168.0.25:8700/ucard/wallet/deposit", JSON.toJSON(map), response.body()));
    }
    public WasabiConfig getWasabiConfig() throws Exception {
        SysUcardConfigTable sysUcardConfigTable = sysUcardConfigService.getByCode(Constants.WASABI_CARD);
        if (sysUcardConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        WasabiConfig wasabiConfig = JSON.parseObject(sysUcardConfigTable.getValue(), WasabiConfig.class);
        return wasabiConfig;
    }
}
