package com.liquidnet.service.adam.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.service.account.wallet.vo.WalletAccountInfoVo;
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.AdamPayout;
import com.liquidnet.service.adam.entity.AdamPayoutBeneficiary;
import com.liquidnet.service.adam.entity.AdamPayoutPayer;
import com.liquidnet.service.adam.entity.AdamTransaction;
import com.liquidnet.service.adam.incrementer.CustomIdGenerator;
import com.liquidnet.service.adam.mapper.AdamPayoutBeneficiaryMapper;
import com.liquidnet.service.adam.mapper.AdamPayoutMapper;
import com.liquidnet.service.adam.mapper.AdamPayoutPayerMapper;
import com.liquidnet.service.adam.mapper.AdamTransactionMapper;
import com.liquidnet.service.adam.service.IAdamPayoutApiService;
import com.liquidnet.service.adam.service.IAdamPayoutService;
import com.liquidnet.service.adam.service.IAdamTransactionApiService;
import com.liquidnet.service.adam.service.IAdamTransactionService;
import com.liquidnet.service.adam.service.feign.bank.IAdamFeignBankCcPaymentService;
import com.liquidnet.service.adam.service.feign.fin.IAdamFeignAccountService;
import com.liquidnet.service.adam.util.AdamRedisLockUtil;
import com.liquidnet.service.bank.currencycloud.dto.BankCcConversionDto;
import com.liquidnet.service.bank.currencycloud.entity.BankCcPayment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
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.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Slf4j
@Service
public class AdamPayoutApiServiceImpl implements IAdamPayoutApiService {

    @Autowired
    private IAdamPayoutService adamPayoutService;

    @Autowired
    private AdamPayoutMapper adamPayoutMapper;

    @Autowired
    private AdamPayoutPayerMapper adamPayoutPayerMapper;

    @Autowired
    private AdamPayoutBeneficiaryMapper adamPayoutBeneficiaryMapper;

    @Autowired
    private IAdamTransactionService adamTransactionService;

    @Autowired
    private AdamTransactionMapper adamTransactionMapper;

    @Autowired
    private IAdamTransactionApiService adamTransactionApiService;

    @Autowired
    private CustomIdGenerator customIdGenerator;

    // feign
    @Autowired
    private IAdamFeignAccountService adamFeignAccountService;

    @Autowired
    private IAdamFeignBankCcPaymentService adamFeignBankCcPaymentService;


    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AdamResultDto<AdamPayoutDetailDto> createPayout(AdamPayoutDetailParam param) {
        AdamResultDto dto = new AdamResultDto<>();

        AdamResultDto<WalletAccountInfoVo> resultDto = checkParam(param);
        if (!resultDto.isSuccess()) {
            return AdamResultDto.failure(resultDto.getCode(), resultDto.getMessage());
        }
        WalletAccountInfoVo wallet = resultDto.getData();
        // init payment info
        AdamPayoutDetailDto adamPayoutDetailDto = initPayment(param, wallet.getAlias(), AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        AdamPayout adamPayout = adamPayoutDetailDto.getAdamPayout();

        // 执行下发
        ((AdamPayoutApiServiceImpl) AopContext.currentProxy()).doPayout(adamPayout.getId());
        dto.setData(adamPayoutDetailDto);
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AdamResultDto<AdamPayoutDetailDto> editPayout(AdamPayoutDetailParam param) {
        // 校验参数
        AdamResultDto<WalletAccountInfoVo> resultDto = checkParam(param);
        if (!resultDto.isSuccess()) {
            return AdamResultDto.failure(resultDto.getCode(), resultDto.getMessage());
        }

        WalletAccountInfoVo wallet = resultDto.getData();
        AdamPayoutInfoReqParam paymentReq = param.getPayout();
        String comId = paymentReq.getComId();
        String payoutId = paymentReq.getId();
        // 查询 payout
        AdamPayout selectPayout = adamPayoutService.selectById(payoutId, comId);
        if (selectPayout == null || !selectPayout.getStatus().equals(AdamPayoutConstants.StatusEnum.READY_TO_PROCESS.getCode()) && !selectPayout.getStatus().equals(AdamPayoutConstants.StatusEnum.AWAITING_FUNDS.getCode())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006008);
        }
        // 查询 transaction
        AdamTransactionParam paramTrans = new AdamTransactionParam();
        paramTrans.setComId(comId);
        paramTrans.setRelatedId(payoutId);

        AdamTransaction selectTrans = adamTransactionService.selectOne(paramTrans);
        if (selectTrans == null) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006008);
        }

        LocalDateTime dateNow = LocalDateTime.now();
        AdamPayoutPayerReqParam payerReq = param.getPayer();
        AdamPayoutBeneficiaryReqParam beneficiaryReq = param.getBeneficiary();

        AdamPayout updatePayout = new AdamPayout();
        updatePayout.setId(payoutId);
        updatePayout.setFinWalletNo(wallet.getWalletNo());
        updatePayout.setCurrency(paymentReq.getCurrency());
        updatePayout.setAmount(new BigDecimal(paymentReq.getAmount()));
        updatePayout.setReference(paymentReq.getReference());
        updatePayout.setReason(paymentReq.getReason());
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate executionDate = LocalDate.parse(paymentReq.getExecutionDate(), fmt);
        updatePayout.setExecutionDate(executionDate);
        updatePayout.setFxWith(paymentReq.getFxWith());
        updatePayout.setFxContractNo(paymentReq.getFxContractNo());
        updatePayout.setFxQuote(paymentReq.getFxQuote());
        updatePayout.setUpdateTime(dateNow);
//        updatePayout.setComId("");
//        updatePayout.setStatus("");
//        updatePayout.setBankPayoutId("");
//        updatePayout.setBankChannel("");
//        updatePayout.setFailureReason("");
//        updatePayout.setCompleteTime(LocalDateTime.now());
//        updatePayout.setCreateTime(LocalDateTime.now());

        adamPayoutMapper.updateById(updatePayout);

        AdamPayoutPayer updatePayer = new AdamPayoutPayer();
        updatePayer.setId(payoutId);
        updatePayer.setSenderDefault(payerReq.getSenderDefault());
        updatePayer.setSenderExisting(payerReq.getSenderExisting());
        updatePayer.setLegalEntityType(payerReq.getLegalEntityType());

        if (AdamPayerConstants.LegalEntityTypeEnum.INDIVIDUAL.getCode().equals(payerReq.getLegalEntityType())) {
            updatePayer.setFirstName(payerReq.getFirstName());
            updatePayer.setLastName(payerReq.getLastName());
            updatePayer.setFullName(payerReq.getFirstName().concat(payerReq.getLastName()));
        } else if (AdamPayerConstants.LegalEntityTypeEnum.COMPANY.getCode().equals(payerReq.getLegalEntityType())) {
            updatePayer.setFirstName(null);
            updatePayer.setLastName(null);
            updatePayer.setFullName(payerReq.getFullName());
        }
        updatePayer.setCountry(payerReq.getCountry());
        updatePayer.setIdType(payerReq.getIdType());
        updatePayer.setIdNumber(payerReq.getIdNumber());
        updatePayer.setAddress(payerReq.getAddress());
        updatePayer.setCity(payerReq.getCity());
        updatePayer.setProvince(payerReq.getProvince());
        updatePayer.setUpdateTime(dateNow);
        adamPayoutPayerMapper.updateById(updatePayer);

        AdamPayoutBeneficiary updateBenefic = new AdamPayoutBeneficiary();
        updateBenefic.setId(payoutId);
        updateBenefic.setLegalEntityType(beneficiaryReq.getLegalEntityType());
        updateBenefic.setExisting(beneficiaryReq.getExisting());
        updateBenefic.setCurrency(beneficiaryReq.getCurrency());
        updateBenefic.setNickName(beneficiaryReq.getNickName());

        if (AdamBeneficiaryConstants.LegalEntityTypeEnum.INDIVIDUAL.getCode().equals(beneficiaryReq.getLegalEntityType())) {
            updateBenefic.setFirstName(beneficiaryReq.getFirstName());
            updateBenefic.setLastName(beneficiaryReq.getLastName());
            updateBenefic.setFullName(beneficiaryReq.getFirstName().concat(beneficiaryReq.getLastName()));
        } else if (AdamBeneficiaryConstants.LegalEntityTypeEnum.COMPANY.getCode().equals(beneficiaryReq.getLegalEntityType())) {
            updateBenefic.setFirstName(null);
            updateBenefic.setLastName(null);
            updateBenefic.setFullName(beneficiaryReq.getFullName());
        }
        updateBenefic.setCountry(beneficiaryReq.getCountry());
        updateBenefic.setAddress(beneficiaryReq.getAddress());
        updateBenefic.setCity(beneficiaryReq.getCity());
        updateBenefic.setPostCode(beneficiaryReq.getPostCode());
        updateBenefic.setProvince(beneficiaryReq.getProvince());
        updateBenefic.setBankCountry(beneficiaryReq.getBankCountry());
        updateBenefic.setBankAccountNo(beneficiaryReq.getBankAccountNo());
        updateBenefic.setPaymentMethod(beneficiaryReq.getPaymentMethod());
        updateBenefic.setPaymentMethodDetail(beneficiaryReq.getPaymentMethodDetail());
        updateBenefic.setPaymentMethodUse(beneficiaryReq.getPaymentMethodUse());
        updateBenefic.setUpdateTime(dateNow);
        adamPayoutBeneficiaryMapper.updateById(updateBenefic);

        AdamTransaction updateTrans = new AdamTransaction();
        updateTrans.setId(selectTrans.getId());
        updateTrans.setCurrency(updatePayout.getCurrency());
        updateTrans.setFundAmount(updatePayout.getAmount());
        updateTrans.setRelatedReference(updatePayout.getReference());
        updateTrans.setFinWalletNo(updatePayout.getFinWalletNo());
        updateTrans.setFinWalletName(wallet.getAlias());
        updateTrans.setUpdateTime(dateNow);
//        transaction.setId(String.valueOf(customIdGenerator.nextId(transaction)));
//        updateTransaction.setNumber(paymentId);
//        updateTransaction.setComId(selectPayout.getComId());
//        updateTransaction.setType(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
//        updateTransaction.setStatus(selectPayout.getStatus());
//        updateTransaction.setFundDirection(AdamTransactionConstants.FundsDirectionEnum.DEBIT.getCode());
//        updateTransaction.setRelatedId(paymentId);
//        updateTransaction.setBankTransactionId("");
//        updateTransaction.setBankChannel(updatePayout.getBankChannel());
//        updateTransaction.setFinWalletTransactionId("");
//        updateTransaction.setFinWalletAvailableBalance(new BigDecimal("0"));
//        updateTransaction.setFinWalletTotalBalance(new BigDecimal("0"));

        adamTransactionMapper.updateById(updateTrans);

        // 异步执行下发
        ((AdamPayoutApiServiceImpl) AopContext.currentProxy()).asyncDoPayout(payoutId);

        // 只是为了前端显示数据使用
        updatePayout.setCreateTime(selectPayout.getCreateTime());
        updatePayout.setStatus(selectPayout.getStatus());
        AdamPayoutDetailDto dto = new AdamPayoutDetailDto();
        dto.setAdamPayout(updatePayout);
        dto.setAdamPayoutPayer(updatePayer);
        dto.setAdamPayoutBeneficiary(updateBenefic);
        dto.setTransaction(updateTrans);
        return AdamResultDto.success(dto);
    }

    @Override
    @Transactional
    public AdamResultDto cancelledPayout(String comId, String payoutId) {
        // 查询 payout
        AdamPayout payout = adamPayoutService.selectById(payoutId);
        log.info("adam cancelled payout,payoutId=[{}],comId=[{}],payOut=[{}]", payoutId, comId, JSON.toJSONString(payout, SerializerFeature.WriteMapNullValue));
        if (payout == null || !payout.getStatus().equals(AdamPayoutConstants.StatusEnum.READY_TO_PROCESS.getCode()) && !payout.getStatus().equals(AdamPayoutConstants.StatusEnum.AWAITING_FUNDS.getCode())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006007);
        }

        // 更新payout
        AdamPayout payoutUpdate = new AdamPayout();
        payoutUpdate.setStatus(AdamPayoutConstants.StatusEnum.CANCELLED.getCode());
        payoutUpdate.setUpdateTime(LocalDateTime.now());

        LambdaUpdateWrapper<AdamPayout> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AdamPayout::getId, payoutId)
                .eq(AdamPayout::getComId, comId);
        adamPayoutMapper.update(payoutUpdate, updateWrapper);

        // 更新transaction
        AdamTransaction transactionUp = new AdamTransaction();
        transactionUp.setStatus(payoutUpdate.getStatus());
        transactionUp.setUpdateTime(LocalDateTime.now());

        LambdaUpdateWrapper<AdamTransaction> updateWrapper1 = new LambdaUpdateWrapper<>();
        updateWrapper1.eq(AdamTransaction::getRelatedId, payoutId)
                .eq(AdamTransaction::getComId, comId)
                .eq(AdamTransaction::getType, AdamTransactionConstants.TypeEnum.PAYOUT.getCode())
        ;
        adamTransactionMapper.update(transactionUp, updateWrapper1);

        return AdamResultDto.success();
    }

    @Override
    @Async
    public void asyncDoPayout(String payoutId) {
        this.doPayout(payoutId);
    }

    @Override
    @Async
    public void asyncAwaitingFundsDoPayout(String finWalletNo) {

        LambdaQueryWrapper<AdamPayout> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(!StringUtils.isEmpty(finWalletNo), AdamPayout::getFinWalletNo, finWalletNo)
                .eq(AdamPayout::getStatus, AdamPayoutConstants.StatusEnum.AWAITING_FUNDS.getCode())
                .orderByAsc(AdamPayout::getCreateTime);

        List<AdamPayout> list = adamPayoutMapper.selectList(queryWrapper);
        for (AdamPayout payout : list) {
            String payoutId = payout.getId();
            AdamResultDto adamResultDto = this.doPayout(payoutId);
            if (!adamResultDto.isSuccess()) {
                // 判断当前时间与下发执行时间的差,超过10个工作日更新状态失败
                int i = (int) ChronoUnit.DAYS.between(payout.getExecutionDate(), LocalDate.now()) + 1;
                if (i >= 14) {
                    AdamTransactionParam param = new AdamTransactionParam();
                    param.setRelatedId(payoutId);
                    param.setType(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
                    AdamTransaction transaction = adamTransactionService.selectOne(param);

                    AdamPayout upPayout = new AdamPayout();
                    upPayout.setId(payoutId);
                    upPayout.setStatus(AdamPayoutConstants.StatusEnum.FAILED.getCode());
                    upPayout.setFailureReason("Insufficient amount in 10 working days");
                    AdamTransaction upTrans = new AdamTransaction();
                    upTrans.setId(transaction.getId());
                    upTrans.setStatus(upPayout.getStatus());
                    upTrans.setFailureReason(upPayout.getFailureReason());
                    adamTransactionApiService.updatePayout(upTrans, upPayout);

                    log.info("asyncAwaitingFundsDoPayout 10个工作日金额不足失败 payoutId=[{}],adamResultDto=[{}]", payoutId, JSON.toJSONString(adamResultDto));
                }
            }

        }
    }

    /**
     * 执行下发
     */
    @Override
    @Transactional
    public AdamResultDto doPayout(String payoutId) {
        String logFx = "do payout,id=[" + payoutId + "].";
        AdamResultDto resultDto = AdamResultDto.success();
        // 校验payment
        AdamPayout selectPayment = adamPayoutMapper.selectById(payoutId);
        if (selectPayment == null) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006001);
        }
        String selectPaymentStatus = selectPayment.getStatus();
        // 最终状态直接返回
        if (AdamPayoutConstants.StatusEnum.CANCELLED.getCode().equals(selectPaymentStatus) ||
                AdamPayoutConstants.StatusEnum.FAILED.getCode().equals(selectPaymentStatus) ||
                AdamPayoutConstants.StatusEnum.COMPLETED.getCode().equals(selectPaymentStatus)) {
            return AdamResultDto.success(selectPayment);
        }
        // 获取锁
        String payoutLockKey = AdamRedisConstants.PAYOUT_LOCK + payoutId;
        boolean b = AdamRedisLockUtil.tryLock(payoutLockKey, 1, 60);
        if (!b) {
            return AdamResultDto.success();
        }
        try {
            // 校验执行日期,更新值为远期下发
            LocalDate executionDate = selectPayment.getExecutionDate();
            if (executionDate.compareTo(LocalDate.now()) > 0) {
                if (!AdamPayoutConstants.SpotForwardEnum.FORWARD.getCode().equals(selectPayment.getSpotForward())) {
                    AdamPayout upPayout = new AdamPayout();
                    upPayout.setId(selectPayment.getId());
                    upPayout.setSpotForward(AdamPayoutConstants.SpotForwardEnum.FORWARD.getCode());
                    adamPayoutService.updateById(upPayout);
                }
                return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006003);
            }
            // 查询资金账户wallet
            AdamResultDto<WalletAccountInfoVo> query = adamFeignAccountService.getWallet(selectPayment.getComId(), selectPayment.getFinWalletNo());
            if (!query.isSuccess()) {
                return query;
            }
            // 校验资金账户状态
            WalletAccountInfoVo wallet = query.getData();
            if (!(wallet.getStatus() == 1) || (!(wallet.getPayStatus() == 1) && !(wallet.getPayStatus() == 2))) {
                return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006001);
            }
            // 查询transaction
            AdamTransactionParam selectTransParam = new AdamTransactionParam();
            selectTransParam.setType(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
            selectTransParam.setRelatedId(payoutId);
            AdamTransaction selectTrans = adamTransactionService.selectOne(selectTransParam);

            // 准备更新 payment & transaction
            AdamPayout upPayout = new AdamPayout();
            AdamTransaction upTrans = new AdamTransaction();
            upPayout.setId(selectPayment.getId());
            upTrans.setId(selectTrans.getId());

            // 校验资金账户金额是否充足
            BigDecimal balanceAvailable = wallet.getBalanceAvailable();
            if (balanceAvailable.compareTo(selectPayment.getAmount()) < 0) {
                upPayout.setStatus(AdamPayoutConstants.StatusEnum.AWAITING_FUNDS.getCode());
                upTrans.setStatus(upPayout.getStatus());
                //  更新状态到待充值
                adamTransactionApiService.updatePayout(upTrans, upPayout);
                return AdamResultDto.failure(AdamErrorCode.ADAM_WALLET_005003);
            }

            // 查询待处理状态,或等待充值状态向资金账户发送
            if (AdamPayoutConstants.StatusEnum.READY_TO_PROCESS.getCode().equals(selectPaymentStatus) || AdamPayoutConstants.StatusEnum.AWAITING_FUNDS.getCode().equals(selectPaymentStatus)) {
                AdamResultDto<String> stringAdamResultDto = adamTransactionApiService.pushFinTransaction(selectTrans);
                if (!stringAdamResultDto.isSuccess()) {
                    return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006005);
                }
                // 向渠道下发
                if (AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode().equals(selectPayment.getBankChannel())) {
                    return this.createCcPayout(selectPayment.getId(), selectTrans.getId());
                }
            }
        } catch (Exception e) {
            log.error(logFx + "error", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } finally {
            // 释放锁
            AdamRedisLockUtil.unlock(payoutLockKey);
        }

        return resultDto;
    }

    @Override
    public AdamResultDto<BankCcConversionDto> createCcPayout(String payoutId, String transactionId) {
        AdamResultDto<BankCcPayment> bankCcPaymentResult = adamFeignBankCcPaymentService.createPayment(payoutId);
        if (!bankCcPaymentResult.isSuccess()) {
            AdamPayout upPayout = new AdamPayout();
            AdamTransaction upTrans = new AdamTransaction();
            upPayout.setId(payoutId);
            upTrans.setId(transactionId);
            upPayout.setStatus(AdamPayoutConstants.StatusEnum.FAILED.getCode());
            upPayout.setUpdateTime(LocalDateTime.now());
            upPayout.setFailureReason(bankCcPaymentResult.getMessage());
            upTrans.setStatus(upPayout.getStatus());
            upTrans.setUpdateTime(upPayout.getUpdateTime());
            upTrans.setFailureReason(upPayout.getFailureReason());

            AdamTransaction transaction = adamTransactionService.selectById(transactionId);
            transaction.setStatus(upTrans.getStatus());
            // 资金账户推送交易信息
            AdamResultDto<String> dto = adamTransactionApiService.pushFinTransaction(transaction);

            if (dto.isSuccess()) {
                upTrans.setFinWalletTransactionId(dto.getData());
            }

            adamTransactionApiService.updatePayout(upTrans, upPayout);
            return AdamResultDto.success();

        } else {
            return this.bankCcPayout(bankCcPaymentResult.getData());
        }

    }

    @Override
    public AdamResultDto bankCcPayout(BankCcPayment param) {
        String payoutId = param.getUniqueRequestId();
        AdamPayout payout = adamPayoutService.selectById(payoutId);
        String status = payout.getStatus();
        String byBankStatus = AdamPayoutConstants.StatusEnum.getByBankStatus(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode(), param.getStatus());

        // 数据库最终状态直接返回
        if (AdamPayoutConstants.StatusEnum.COMPLETED.getCode().equals(status) || AdamPayoutConstants.StatusEnum.FAILED.getCode().equals(status) || AdamPayoutConstants.StatusEnum.CANCELLED.getCode().equals(status)) {
            return AdamResultDto.success();
        }

        AdamTransactionParam transactionParam = new AdamTransactionParam();
        transactionParam.setRelatedId(payoutId);
        transactionParam.setType(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
        AdamTransaction transaction = adamTransactionService.selectOne(transactionParam);

        AdamPayout p = new AdamPayout();
        LocalDateTime now = LocalDateTime.now();
        p.setId(payoutId);
        p.setBankPayoutId(param.getId());
        p.setStatus(byBankStatus);
        p.setFailureReason(param.getFailureReason());
        p.setUpdateTime(now);
        if (AdamPayoutConstants.StatusEnum.COMPLETED.getCode().equals(byBankStatus)) {
            p.setCompleteTime(now);
        }
        transaction.setStatus(p.getStatus());
        transaction.setCompleteTime(p.getCompleteTime());
        transaction.setUpdateTime(p.getUpdateTime());
        transaction.setFailureReason(p.getFailureReason());

        adamTransactionApiService.updatePayout(transaction, p);

        //  CC最终状态再一次给资金账户推送交易信息
        if (AdamPayoutConstants.StatusEnum.COMPLETED.getCode().equals(byBankStatus) || AdamPayoutConstants.StatusEnum.FAILED.getCode().equals(byBankStatus)) {
            adamTransactionApiService.pushFinTransaction(transaction);
        }
        return AdamResultDto.success();

    }

    /**
     * 校验参数
     */
    private AdamResultDto<WalletAccountInfoVo> checkParam(AdamPayoutDetailParam param) {
        AdamPayoutInfoReqParam payoutReq = param.getPayout();

        // 校验下发金额
        if (StringUtils.isEmpty(payoutReq.getAmount()) || new BigDecimal(payoutReq.getAmount()).compareTo(BigDecimal.ZERO) <= 0) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006009);
        }
        // 校验下发币种和收款人币种是否一致
        AdamPayoutBeneficiaryReqParam beneficiary = param.getBeneficiary();

        // 单一下发校验,下发币种与收款人币种是否一致
        if (!beneficiary.getCurrency().equals(payoutReq.getCurrency())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006010);
        }

        // 查询资金账户wallet
        AdamResultDto<WalletAccountInfoVo> query = adamFeignAccountService.getWallet(payoutReq.getComId(), payoutReq.getFinWalletNo());
        if (!query.isSuccess()) {
            return query;
        }

        // 校验资金账户wallet状态
        WalletAccountInfoVo wallet = query.getData();
        if (wallet == null || !(wallet.getStatus() == 1) || (!(wallet.getPayStatus() == 1) && !(wallet.getPayStatus() == 2))) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_WALLET_005002);
        }
        // 校验下发币种与wallet币种是否一致
        if (!wallet.getCurrency().equals(payoutReq.getCurrency())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006004);
        }

        // 校验下发执行日期
        if (StringUtils.isEmpty(payoutReq.getExecutionDate())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_PAYOUT_006002);
        }
        return query;
    }

    /**
     * init payment info
     *
     * @param param       create payment request info
     * @param walletName  wallet info
     * @param bankChannel bank channel
     */
    private AdamPayoutDetailDto initPayment(AdamPayoutDetailParam param, String walletName, String bankChannel) {

        AdamPayoutDetailDto retDto = new AdamPayoutDetailDto();
        LocalDateTime createTime = LocalDateTime.now();

        AdamPayoutInfoReqParam paymentReq = param.getPayout();
        AdamPayoutBeneficiaryReqParam beneficiaryReq = param.getBeneficiary();
        AdamPayoutPayerReqParam payerReq = param.getPayer();
        String paymentId = adamTransactionApiService.generateNumber(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
        AdamPayout payout = new AdamPayout();
        payout.setId(paymentId);
        payout.setFinWalletNo(paymentReq.getFinWalletNo());
        payout.setComId(paymentReq.getComId());
        payout.setUserId(paymentReq.getUserId());
        payout.setStatus(AdamPayoutConstants.StatusEnum.READY_TO_PROCESS.getCode());
        payout.setCurrency(paymentReq.getCurrency());
        payout.setAmount(new BigDecimal(paymentReq.getAmount()));
        payout.setReference(paymentReq.getReference());
        payout.setReason(paymentReq.getReason());
        payout.setBankPayoutId("");
        payout.setBankChannel(bankChannel);
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate executionDate = LocalDate.parse(paymentReq.getExecutionDate(), fmt);
        payout.setExecutionDate(executionDate);
        payout.setFxWith(paymentReq.getFxWith());
        payout.setFxContractNo(paymentReq.getFxContractNo());
        payout.setFxQuote(paymentReq.getFxQuote());
        payout.setSpotForward(AdamPayoutConstants.SpotForwardEnum.SPOT.getCode());
        payout.setCreateTime(createTime);
        adamPayoutMapper.insert(payout);

        AdamPayoutPayer payer = new AdamPayoutPayer();
        payer.setId(paymentId);
        payer.setSenderDefault(payerReq.getSenderDefault());
        payer.setSenderExisting(payerReq.getSenderExisting());
        payer.setLegalEntityType(payerReq.getLegalEntityType());

        if (AdamPayerConstants.LegalEntityTypeEnum.INDIVIDUAL.getCode().equals(payerReq.getLegalEntityType())) {
            payer.setFirstName(payerReq.getFirstName());
            payer.setLastName(payerReq.getLastName());
            payer.setFullName(payerReq.getFirstName().concat(payerReq.getLastName()));
        } else if (AdamPayerConstants.LegalEntityTypeEnum.COMPANY.getCode().equals(payerReq.getLegalEntityType())) {
            payer.setFirstName(null);
            payer.setLastName(null);
            payer.setFullName(payerReq.getFullName());
        }
        payer.setCountry(payerReq.getCountry());
        payer.setIdType(payerReq.getIdType());
        payer.setIdNumber(payerReq.getIdNumber());
        payer.setAddress(payerReq.getAddress());
        payer.setCity(payerReq.getCity());
        payer.setProvince(payerReq.getProvince());
        payer.setCreateTime(createTime);
        adamPayoutPayerMapper.insert(payer);

        AdamPayoutBeneficiary beneficiary = new AdamPayoutBeneficiary();
        beneficiary.setId(paymentId);
        beneficiary.setLegalEntityType(beneficiaryReq.getLegalEntityType());
        beneficiary.setExisting(beneficiaryReq.getExisting());
        beneficiary.setCurrency(beneficiaryReq.getCurrency());
        beneficiary.setNickName(beneficiaryReq.getNickName());

        if (AdamBeneficiaryConstants.LegalEntityTypeEnum.INDIVIDUAL.getCode().equals(beneficiaryReq.getLegalEntityType())) {
            beneficiary.setFirstName(beneficiaryReq.getFirstName());
            beneficiary.setLastName(beneficiaryReq.getLastName());
            beneficiary.setFullName(beneficiaryReq.getFirstName().concat(beneficiaryReq.getLastName()));
        } else if (AdamBeneficiaryConstants.LegalEntityTypeEnum.COMPANY.getCode().equals(beneficiaryReq.getLegalEntityType())) {
            beneficiary.setFirstName(null);
            beneficiary.setLastName(null);
            beneficiary.setFullName(beneficiaryReq.getFullName());
        }
        beneficiary.setCountry(beneficiaryReq.getCountry());
        beneficiary.setAddress(beneficiaryReq.getAddress());
        beneficiary.setCity(beneficiaryReq.getCity());
        beneficiary.setPostCode(beneficiaryReq.getPostCode());
        beneficiary.setProvince(beneficiaryReq.getProvince());
        beneficiary.setBankCountry(beneficiaryReq.getBankCountry());
        beneficiary.setBankAccountNo(beneficiaryReq.getBankAccountNo());
        beneficiary.setPaymentMethod(beneficiaryReq.getPaymentMethod());
        beneficiary.setPaymentMethodDetail(beneficiaryReq.getPaymentMethodDetail());
        beneficiary.setPaymentMethodUse(beneficiaryReq.getPaymentMethodUse());
        beneficiary.setCreateTime(createTime);
        adamPayoutBeneficiaryMapper.insert(beneficiary);

        AdamTransaction transaction = new AdamTransaction();
        transaction.setId(String.valueOf(customIdGenerator.nextId(transaction)));
        transaction.setNumber(paymentId);
        transaction.setComId(payout.getComId());
        transaction.setType(AdamTransactionConstants.TypeEnum.PAYOUT.getCode());
        transaction.setStatus(payout.getStatus());
        transaction.setCurrency(payout.getCurrency());
        transaction.setFundAmount(payout.getAmount());
        transaction.setFundDirection(AdamTransactionConstants.FundsDirectionEnum.DEBIT.getCode());
        transaction.setRelatedId(paymentId);
        transaction.setRelatedReference(payout.getReference());
        transaction.setBankTransactionId("");
        transaction.setBankChannel(payout.getBankChannel());
        transaction.setFinWalletNo(payout.getFinWalletNo());
        transaction.setFinWalletName(walletName);
        transaction.setFinWalletTransactionId("");
        transaction.setFinWalletAvailableBalance(null);
        transaction.setFinWalletTotalBalance(null);
        transaction.setCreateTime(LocalDateTime.now());
        adamTransactionService.insert(transaction);

        retDto.setAdamPayout(payout);
        retDto.setAdamPayoutPayer(payer);
        retDto.setAdamPayoutBeneficiary(beneficiary);
        retDto.setTransaction(transaction);

        return retDto;
    }

}
