package com.liquidnet.service.adam.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.common.AdamErrorCode;
import com.liquidnet.service.adam.constant.*;
import com.liquidnet.service.adam.dto.*;
import com.liquidnet.service.adam.dto.base.AdamResultDto;
import com.liquidnet.service.adam.entity.AdamAccount;
import com.liquidnet.service.adam.entity.AdamAccountWallet;
import com.liquidnet.service.adam.entity.AdamFunding;
import com.liquidnet.service.adam.entity.AdamTransaction;
import com.liquidnet.service.adam.incrementer.CustomIdGenerator;
import com.liquidnet.service.adam.service.*;
import com.liquidnet.service.adam.util.AdamRedisLockUtil;
import com.liquidnet.service.bank.currencycloud.entity.BankCcTransaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

/**
 * <p>
 * 充值信息表 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2020-10-30
 */
@Slf4j
@Service
public class AdamFundingApiServiceImpl implements IAdamFundingApiService {

    @Autowired
    private IAdamFundingService adamFundingService;

    @Autowired
    private IAdamTransactionApiService adamTransactionApiService;

    @Autowired
    private IAdamTransactionService adamTransactionService;

    @Autowired
    private IAdamAccountWalletService adamAccountWalletService;

    @Autowired
    private IAdamAccountService adamAccountService;

    @Autowired
    private IAdamPayoutApiService adamPayoutApiService;

    @Autowired
    private CustomIdGenerator customIdGenerator;

    @Override
    public AdamResultDto bankCcFunding(BankCcTransaction param) {
        AdamAccountParam selectAccount = new AdamAccountParam();

        selectAccount.setBankId(param.getAccountId());
        selectAccount.setBankChannel(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        AdamAccount account = adamAccountService.selectOne(selectAccount);

        AdamResultDto resultDto = AdamResultDto.success();
        AdamFundingDto adamFunding = new AdamFundingDto();
        adamFunding.setId("");
        adamFunding.setFinWalletNo("");
        adamFunding.setComId(account.getComId());
        adamFunding.setStatus(AdamFundingConstants.StatusEnum.getByBankStatus(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode(), param.getStatus()));
        adamFunding.setCurrency(param.getCurrency());
        adamFunding.setAmount(param.getAmount());
        adamFunding.setReference(param.getRelatedEntityShortReference());
        adamFunding.setReason(param.getReason());
        adamFunding.setBankFundingId(param.getRelatedEntityId());
        adamFunding.setBankChannel(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        adamFunding.setCompleteTime(DateUtil.asLocalDateTime(param.getCompletedAt()));
        adamFunding.setBankTransactionId(param.getId());
        adamFunding.setBankWalletId(param.getBalanceId());
        funding(adamFunding);
        return resultDto;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public AdamResultDto funding(AdamFundingDto param) {
        String logFx = "funding,bankFundingId=[" + param.getBankFundingId() + "].";
        log.info(logFx + "param=[{}]", JSON.toJSONString(param, SerializerFeature.WriteMapNullValue));
        AdamResultDto<AdamFundingDetailDto> resultDto = AdamResultDto.success();
        // 获取锁,渠道 + 渠道id
        String fundingLockKey = AdamRedisConstants.FUNDING_LOCK + param.getBankChannel().toLowerCase() + "_" + param.getBankFundingId();
        boolean b = AdamRedisLockUtil.tryLock(fundingLockKey, 1, 60);
        if (!b) {
            return AdamResultDto.success();
        }
        try {
            // 查询funding记录
            AdamFunding selectParam = new AdamFunding();
            selectParam.setBankChannel(param.getBankChannel());
            selectParam.setBankFundingId(param.getBankFundingId());
            AdamFunding adamFunding = adamFundingService.selectOne(selectParam);

            AdamTransaction adamTransaction;
            // 新增funding记录
            boolean initB = false;
            if (adamFunding == null) {
                resultDto = initFunding(param);
                if (!resultDto.isSuccess()) {
                    log.warn(logFx + "init funding warn,initFunding resultDto=[{}]", JSON.toJSONString(resultDto, SerializerFeature.WriteMapNullValue));
                    return resultDto;
                }

                adamFunding = resultDto.getData().getAdamFunding();
                adamTransaction = resultDto.getData().getTransaction();
                initB = true;
            } else {
                // 查询transaction
                AdamTransactionParam selectTransParam = new AdamTransactionParam();
                selectTransParam.setType(AdamTransactionConstants.TypeEnum.FUNDING.getCode());
                selectTransParam.setRelatedId(adamFunding.getId());
                adamTransaction = adamTransactionService.selectOne(selectTransParam);
            }
            String paramStatus = param.getStatus();
            String selectStatus = adamFunding.getStatus();
            // 向资金账户发送
            if (initB || AdamFundingConstants.StatusEnum.PROCESSING.getCode().equals(selectStatus) && !paramStatus.equals(selectStatus)) {

                adamTransaction.setStatus(param.getStatus());

                AdamResultDto<String> stringAdamResultDto = adamTransactionApiService.pushFinTransaction(adamTransaction);
                if (!stringAdamResultDto.isSuccess()) {
                    return AdamResultDto.failure(AdamErrorCode.ADAM_FUNDING_007001);
                }
                AdamTransaction t = new AdamTransaction();
                t.setId(adamTransaction.getId());
                t.setFinWalletTransactionId(stringAdamResultDto.getData());
                t.setStatus(adamTransaction.getStatus());
                t.setCompleteTime(LocalDateTime.now());

                AdamFunding f = new AdamFunding();
                f.setId(adamFunding.getId());
                f.setStatus(adamTransaction.getStatus());
                f.setCompleteTime(t.getCompleteTime());

                adamTransactionApiService.updateFunding(t, f);
            }
            //
            if (AdamFundingConstants.StatusEnum.COMPLETED.getCode().equals(paramStatus) && !paramStatus.equals(selectStatus)) {
                // 同一条充值订单第一次推送成功,去执行当前钱包异步下发
                adamPayoutApiService.asyncAwaitingFundsDoPayout(adamFunding.getFinWalletNo());
            }

        } catch (Exception e) {
            log.error(logFx + "error", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } finally {
            // 释放锁
            AdamRedisLockUtil.unlock(fundingLockKey);
        }

        return resultDto;

    }

    private AdamResultDto<AdamFundingDetailDto> initFunding(AdamFundingDto param) {
        // 新增funding记录
        AdamAccountWalletParam selectWalletParam = new AdamAccountWalletParam();
        selectWalletParam.setComId(param.getComId());
        selectWalletParam.setBankChannel(param.getBankChannel());
        selectWalletParam.setCurrency(param.getCurrency());
        selectWalletParam.setState(AdamAccountConstants.StateEnum.S2.getCode());
        List<AdamAccountWallet> walletList = adamAccountWalletService.selectList(selectWalletParam);
        if (CollectionUtils.isEmpty(walletList)) {
            // cc充值的币种账户不存在
            return AdamResultDto.failure(AdamErrorCode.ADAM_ACCOUNT_004001);
        }
        LocalDateTime now = LocalDateTime.now();
        AdamAccountWallet adamAccountWallet = walletList.get(0);

        if (StringUtils.isEmpty(adamAccountWallet.getBankWalletId())) {
            AdamAccountWallet up = new AdamAccountWallet();
            up.setId(adamAccountWallet.getId());
            up.setBankWalletId(param.getBankWalletId());
            up.setUpdateTime(now);
            adamAccountWalletService.updateById(up);
        }

        String finWalletName = adamAccountWallet.getName();
        AdamFunding adamFunding = new AdamFunding();
        adamFunding.setId(adamTransactionApiService.generateNumber(AdamTransactionConstants.TypeEnum.FUNDING.getCode()));
        adamFunding.setFinWalletNo(adamAccountWallet.getFinWalletNo());
        adamFunding.setComId(adamAccountWallet.getComId());
        adamFunding.setStatus(param.getStatus());
        adamFunding.setCurrency(param.getCurrency());
        adamFunding.setAmount(param.getAmount());
        adamFunding.setReference(param.getReference());
        adamFunding.setReason(param.getReason());
        adamFunding.setBankFundingId(param.getBankFundingId());
        adamFunding.setBankChannel(param.getBankChannel());
        adamFunding.setCompleteTime(param.getCompleteTime());
        int i = adamFundingService.insert(adamFunding);

        AdamTransaction adamTransaction = new AdamTransaction();
        adamTransaction.setId(String.valueOf(customIdGenerator.nextId(adamTransaction)));
        adamTransaction.setNumber(adamFunding.getId());
        adamTransaction.setComId(adamFunding.getComId());
        adamTransaction.setType(AdamTransactionConstants.TypeEnum.FUNDING.getCode());
        adamTransaction.setStatus(adamFunding.getStatus());
        adamTransaction.setCurrency(adamFunding.getCurrency());
        adamTransaction.setFundAmount(adamFunding.getAmount());
        adamTransaction.setFundDirection(AdamTransactionConstants.FundsDirectionEnum.CREDIT.getCode());
        adamTransaction.setRelatedId(adamFunding.getId());
        adamTransaction.setRelatedReference(adamFunding.getReference());
        adamTransaction.setBankTransactionId(param.getBankTransactionId());
        adamTransaction.setBankChannel(adamFunding.getBankChannel());
        adamTransaction.setFinWalletNo(adamFunding.getFinWalletNo());
        adamTransaction.setFinWalletName(finWalletName);
        adamTransaction.setFinWalletTransactionId("");
        adamTransaction.setFinWalletAvailableBalance(new BigDecimal("0"));
        adamTransaction.setFinWalletTotalBalance(new BigDecimal("0"));
        adamTransaction.setCreateTime(now);
        int i1 = adamTransactionService.insert(adamTransaction);

        AdamFundingDetailDto dto = new AdamFundingDetailDto();
        dto.setAdamFunding(adamFunding);
        dto.setTransaction(adamTransaction);
        return AdamResultDto.success(dto);
    }

}
