package com.liquidnet.service.adam.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.service.account.wallet.dto.WalletEditParam;
import com.liquidnet.service.account.wallet.dto.WalletEditResult;
import com.liquidnet.service.account.wallet.dto.WalletRegisterResult;
import com.liquidnet.service.adam.common.AdamErrorCode;
import com.liquidnet.service.adam.constant.*;
import com.liquidnet.service.adam.dto.AdamAccountCreateDto;
import com.liquidnet.service.adam.dto.AdamAccountCreateParam;
import com.liquidnet.service.adam.dto.AdamAccountParam;
import com.liquidnet.service.adam.dto.AdamAccountWalletParam;
import com.liquidnet.service.adam.dto.base.AdamResultDto;
import com.liquidnet.service.adam.entity.*;
import com.liquidnet.service.adam.incrementer.CustomIdGenerator;
import com.liquidnet.service.adam.mapper.AdamAccountWalletMapper;
import com.liquidnet.service.adam.service.*;
import com.liquidnet.service.adam.service.feign.fin.IAdamFeignAccountService;
import com.liquidnet.service.adam.service.feign.bank.IAdamFeignBankCcAccountService;
import com.liquidnet.service.adam.service.feign.bank.IAdamFeignBankCcBalanceService;
import com.liquidnet.service.adam.util.AdamRedisLockUtil;
import com.liquidnet.service.bank.currencycloud.dto.BankCcAccountCreateDto;
import com.liquidnet.service.bank.currencycloud.dto.BankCcAccountCreateParam;
import com.liquidnet.service.bank.currencycloud.dto.BankCcAccountParam;
import com.liquidnet.service.bank.currencycloud.dto.BankCcContactParam;
import com.liquidnet.service.bank.currencycloud.entity.BankCcBalance;
import com.netflix.discovery.converters.Auto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
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.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 公司账户 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2020-10-13
 */
@Service
@Slf4j
public class AdamAccountApiServiceImpl implements IAdamAccountApiService {

    @Autowired
    IAdamFeignBankCcAccountService adamFeignBankCcAccountService;

    @Autowired
    IAdamFeignBankCcBalanceService adamFeignBankCcBalanceService;

    @Autowired
    IAdamFeignAccountService adamFeignAccountService;

    @Autowired
    CustomIdGenerator customIdGenerator;

    @Autowired
    IAdamComInfoService adamComInfoService;

    @Autowired
    IAdamUserService adamUserService;

    @Autowired
    IAdamSysCountryService sysCountryService;

    @Autowired
    IAdamAccountWalletService adamAccountWalletService;

    @Autowired
    AdamAccountWalletMapper adamAccountWalletMapper;

    @Autowired
    IAdamBankCcAccountService adamBankCcAccountService;

    @Autowired
    IAdamAccountService adamAccountService;

    @Autowired
    @Lazy
    IAdamAccountAsyncService adamAccountAsyncService;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    IAdamComProfileService adamComProfileService;

    @Autowired
    IAdamWalletAccessService adamWalletAccessService;

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public AdamResultDto<AdamAccountWallet> createAccountWallet(AdamAccountCreateParam param) {
        // 参数错误校验
        if (param == null || StringUtils.isEmpty(param.getCurrency()) || StringUtils.isEmpty(param.getBankChannel())) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030001);
        }
        if (!StringUtils.isEmpty(param.getName()) && param.getName().length() > 32) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_WALLET_005005);
        }

        String logFx = "com_create_wallet.comId=[" + param.getComId() + "].";
        log.info(logFx + "param=[{}]", JSON.toJSONString(param));
        // 加锁,comid + bankchannel + bankcurrency
        String lockKey = AdamRedisConstants.WALLET_LOCK + param.getComId() + param.getBankChannel() + param.getCurrency();
        try {
            boolean b = AdamRedisLockUtil.tryLock(lockKey, 0, 30);
            if (!b) {
                return AdamResultDto.failure(AdamErrorCode.ADAM001_REPEAT_SUBMIT);
            }

            AdamComInfo adamComInfo = adamComInfoService.selectById(param.getComId());
            if (adamComInfo == null) {
                return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030001);
            }
            AdamUser userParam = new AdamUser();
            userParam.setComId(param.getComId());
            userParam.setId(param.getUserId());
            AdamUser adamUser = adamUserService.selectOne(userParam);
            if (adamUser == null) {
                return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030001);
            }

            // 公司状态校验
            if (!AdamComConstants.ComStateEnum.S8.getCode().equals(adamComInfo.getState())) {
                return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030002);
            }
            AdamSysCountry sysCountry = new AdamSysCountry();
            sysCountry.setCurrencyAlphabeticCode(param.getCurrency());
            List<AdamSysCountry> sysCountryList = sysCountryService.selectList(sysCountry);
            if (CollectionUtils.isEmpty(sysCountryList)) {
                return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030003);
            }
            // cc 渠道创建账户
            if (AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode().equals(param.getBankChannel())) {
                // create adam account
                AdamAccountCreateDto adamAccountCreateDto = createAdamAccount(param, adamComInfo.getId(), false);
                // wallet 正常状态直接返回
                if (AdamAccountConstants.StateEnum.S2.getCode().equals(adamAccountCreateDto.getAdamAccountWallet().getState())) {
                    return AdamResultDto.success(adamAccountCreateDto.getAdamAccountWallet());
                } else if (StringUtils.isEmpty(adamAccountCreateDto.getAdamAccount().getBankId()) && !adamComInfo.getRegistrationCountryCode().equals(sysCountryList.get(0).getCode())) {
                    // 首次创建账户时,cc渠道只能选择注册国家的币种
                    return AdamResultDto.failure(AdamErrorCode.ADAM_COM_0030004);
                } else if (!adamComInfo.getRegistrationCountryCode().equals(sysCountryList.get(0).getCode())) {
                    // cc渠道已有账户,创建其它币种钱包
                    return createFinAccountWallet(adamComInfo, adamAccountCreateDto.getAdamAccountWallet());
                } else {
                    // 预创建的的状态才可以去cc创建 account
                    AdamResultDto<BankCcAccountCreateDto> resultDto = createCcAccount(adamAccountCreateDto.getAdamAccount(), param, adamComInfo, adamUser);
                    return AdamResultDto.result(resultDto.getCode(), resultDto.getMessage(), null);
                }
            }

        } catch (Exception e) {
            log.error(logFx + "error", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } finally {
            AdamRedisLockUtil.unlock(lockKey);
        }
        return AdamResultDto.success();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public AdamResultDto pushNotifyBankCcBalance(BankCcBalance balance) {
        if (balance == null) {
            return AdamResultDto.failure(AdamErrorCode.ADAM001_PARAM_ERROR);
        }
        // query adma account
        AdamAccountParam adamAccountParam = new AdamAccountParam();
        adamAccountParam.setBankId(balance.getAccountId());
        adamAccountParam.setBankChannel(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        AdamAccount adamAccount = adamAccountService.selectOne(adamAccountParam);
        if (adamAccount == null) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_ACCOUNT_004001);
        }

        // query adma account wallet
        AdamAccountWalletParam param = new AdamAccountWalletParam();
        param.setAccountId(adamAccount.getId());
        param.setBankChannel(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        param.setCurrency(balance.getCurrency());

        // account wallet 预创建状态
        param.setState(AdamAccountConstants.StateEnum.S1.getCode());
        AdamAccountWallet adamAccountWallet = adamAccountWalletService.selectOne(param);
        if (adamAccountWallet == null) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_ACCOUNT_004003);
        }

        // account wallet 正常状态
        param.setState(AdamAccountConstants.StateEnum.S2.getCode());
        AdamAccountWallet adamAccountWalletS2 = adamAccountWalletService.selectOne(param);
        if (adamAccountWalletS2 != null) {
            return AdamResultDto.success();
        }

        // 资金账户中台创建账户
        AdamComInfo adamComInfo = adamComInfoService.selectById(adamAccount.getComId());
        adamAccountWallet.setBankWalletId(balance.getId());
        return createFinAccountWallet(adamComInfo, adamAccountWallet);
    }

    private AdamResultDto<AdamAccountWallet> createFinAccountWallet(AdamComInfo adamComInfo, AdamAccountWallet adamAccountWallet) {
        if (!StringUtils.isEmpty(adamAccountWallet.getFinWalletNo())) {
            return AdamResultDto.success(adamAccountWallet);
        }
        // adam 业务标识
        AdamResultDto<WalletRegisterResult> resultDto = adamFeignAccountService.register(adamComInfo.getId(), adamComInfo.getName(), adamAccountWallet.getName(), adamAccountWallet.getCurrency());
        if (!resultDto.isSuccess()) {
            return AdamResultDto.failure(resultDto.getCode(), resultDto.getMessage());
        }
        String finId = resultDto.getData().getFinId();
        String finWalletNo = resultDto.getData().getWalletNo();
        // upadte adma account wallet
        AdamAccountWallet updateWallet = new AdamAccountWallet();
        updateWallet.setId(adamAccountWallet.getId());
        updateWallet.setFinWalletNo(finWalletNo);
        updateWallet.setBankWalletId(adamAccountWallet.getBankWalletId());
        updateWallet.setState(AdamAccountConstants.StateEnum.S2.getCode());
        adamAccountWalletService.updateById(updateWallet);
        // upadte adma account
        AdamAccount updateAccout = new AdamAccount();
        updateAccout.setId(adamAccountWallet.getAccountId());
        updateAccout.setFinId(finId);
        adamAccountService.updateById(updateAccout);

        // Wallet Access Permission automatic allocation
        AdamComProfile adamComProfile = adamComProfileService.query("newWalletAssignToAllUser", adamComInfo.getId());
        List<AdamUser> adamUserList = adamUserService.queryListForAdminis(adamComInfo.getId());
        List<AdamWalletAccess> addWalletAccessList = Lists.newLinkedList();
        adamUserList.forEach(r -> {
            if (AdamUserConstants.UserRoleEnum.Administrator.name().equals(r.getRole())
                    || (null != adamComProfile && "Yes".equalsIgnoreCase(adamComProfile.getItemVal()))) {
                AdamWalletAccess newObj = AdamWalletAccess.getNew();
                newObj.setComId(adamComInfo.getId());
                newObj.setUserId(r.getId());
                newObj.setFinWalletNo(updateWallet.getFinWalletNo());
                addWalletAccessList.add(newObj);
            }
        });
        if (!CollectionUtils.isEmpty(addWalletAccessList)) {
            adamWalletAccessService.saveBatch(addWalletAccessList);
        }

        return AdamResultDto.success(updateWallet);
    }

    @Override
    @Transactional
    public AdamResultDto deleteWallet(String comId, String finWalletNo) {
        int i = adamAccountWalletService.deleteWallet(comId, finWalletNo);
        if (i != 1) {
            return AdamResultDto.failure(AdamErrorCode.ADAM001_OPERATION_FAILED);
        }
        WalletEditParam param = new WalletEditParam();
        param.setWalletNo(finWalletNo);
        param.setStatus(2);
        WalletEditResult edit = adamFeignAccountService.edit(param);
        log.info("deleteWallet,AdamAccountWalletResult=[{}],WalletEditResult = [{}]", i, JSON.toJSONString(edit));
        if (!edit.isSuccess()) {
            AdamResultDto.failure(edit.getCode(), edit.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return AdamResultDto.success();
    }

    @Override
    public AdamResultDto updateWalletName(String comId, String finWalletNo, String name) {
        if (!StringUtils.isEmpty(name) && name.length() > 32) {
            return AdamResultDto.failure(AdamErrorCode.ADAM_WALLET_005005);
        }

        AdamAccountWallet wallet = new AdamAccountWallet();
        wallet.setName(name);

        AdamAccountWalletParam param = new AdamAccountWalletParam();
        param.setComId(comId);
        param.setFinWalletNo(finWalletNo);
        int i = adamAccountWalletService.update(wallet, param);
        if (i != 1) {
            return AdamResultDto.failure(AdamErrorCode.ADAM001_OPERATION_FAILED);
        }
        WalletEditParam walletEditParam = new WalletEditParam();
        walletEditParam.setWalletNo(finWalletNo);
        walletEditParam.setAlias(name);

        WalletEditResult edit = adamFeignAccountService.edit(walletEditParam);
        log.info("updateWalletName,AdamAccountWalletResult=[{}],WalletEditResult = [{}]", i, JSON.toJSONString(edit));
        if (!edit.isSuccess()) {
            AdamResultDto.failure(edit.getCode(), edit.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return AdamResultDto.success();
    }

    /**
     * create adam account
     *
     * @param multicurrency 渠道是否多币种
     */
    private AdamAccountCreateDto createAdamAccount(AdamAccountCreateParam param, String comId, boolean multicurrency) {
        AdamAccountCreateDto dto = new AdamAccountCreateDto();

        AdamAccountParam adamAccountParam = new AdamAccountParam();
        adamAccountParam.setComId(comId);
        adamAccountParam.setBankChannel(AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode());
        AdamAccount adamAccount = adamAccountService.selectOne(adamAccountParam);

        if (adamAccount == null) {
            // 初始化adam的渠道账户
            adamAccount = new AdamAccount();
            adamAccount.setId(String.valueOf(customIdGenerator.nextId(adamAccount)));
            adamAccount.setComId(comId);
            adamAccount.setState(AdamAccountConstants.StateEnum.S2.getCode());
            adamAccount.setFinId("");
            adamAccount.setBankId("");
            adamAccount.setBankChannel(param.getBankChannel());
            adamAccount.setCreateTime(LocalDateTime.now());
            adamAccountService.insert(adamAccount);
        }

        QueryWrapper<AdamAccountWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AdamAccountWallet::getAccountId, adamAccount.getId())
                .eq(AdamAccountWallet::getCurrency, param.getCurrency())
                .eq(AdamAccountWallet::getBankChannel, param.getBankChannel())
                .ne(AdamAccountWallet::getState, AdamAccountConstants.StateEnum.S3.getCode())
        ;
        AdamAccountWallet adamAccountWallet = adamAccountWalletMapper.selectOne(queryWrapper);

        if (adamAccountWallet == null || multicurrency) {
            // 初始化adam account wallet
            adamAccountWallet = new AdamAccountWallet();
            adamAccountWallet.setId(String.valueOf(customIdGenerator.nextId(adamAccountWallet)));
            adamAccountWallet.setAccountId(adamAccount.getId());
            adamAccountWallet.setComId(comId);
            adamAccountWallet.setCurrency(param.getCurrency());
            adamAccountWallet.setName(param.getName());
            adamAccountWallet.setState(AdamAccountConstants.StateEnum.S1.getCode());
            adamAccountWallet.setFinWalletNo("");
            adamAccountWallet.setBankWalletId("");
            adamAccountWallet.setBankChannel(param.getBankChannel());
            adamAccountWallet.setCreateTime(LocalDateTime.now());
            adamAccountWalletService.insert(adamAccountWallet);
        }
        dto.setAdamAccount(adamAccount);
        dto.setAdamAccountWallet(adamAccountWallet);
        return dto;
    }

    /**
     * create cc account
     *
     * @param adamAccount adam account
     * @param param       create cc account param
     * @param adamComInfo create cc account param
     * @param adamUser    create cc account param
     */
    private AdamResultDto<BankCcAccountCreateDto> createCcAccount(AdamAccount adamAccount, AdamAccountCreateParam param, AdamComInfo adamComInfo, AdamUser adamUser) {
        AdamResultDto<BankCcAccountCreateDto> responseDto = AdamResultDto.success();
        String ccContractId = null;
        String ccAccountId = adamAccount.getBankId();
        if (AdamBankConstants.BankChannelEnum.CURRENCY_CLOUD.getCode().equals(adamAccount.getBankChannel()) &&
                StringUtils.isEmpty(ccAccountId)) {
            // 初始化cc账户参数
            BankCcAccountCreateParam createParam = new BankCcAccountCreateParam();
            // cc account
            BankCcAccountParam accountParam = new BankCcAccountParam();
            accountParam.setUniqueRequestId(adamComInfo.getId());
            accountParam.setAccountName(adamComInfo.getName());
            accountParam.setLegalEntityType("company");
            accountParam.setStreet(adamComInfo.getAddressStreet());
            accountParam.setCity(adamComInfo.getAddressCity());
            accountParam.setPostalCode(adamComInfo.getAddressPostCode());
            accountParam.setCountry(adamComInfo.getRegistrationCountryCode());
            accountParam.setStateOrProvince(adamComInfo.getAddressProvince());
            // cc contact
            BankCcContactParam contactParam = new BankCcContactParam();
            contactParam.setFirstName(adamUser.getFirstName());
            contactParam.setLastName(adamUser.getLastName());
            contactParam.setPhoneNumber(adamUser.getPhoneNumber());
            contactParam.setLoginId(adamComInfo.getId());
            createParam.setBankCcAccountParam(accountParam);
            createParam.setBankCcContactParam(contactParam);
            // 调用feign接口,创建CC账户
            responseDto = adamFeignBankCcAccountService.create(createParam);
            log.info("feign bank api create account,responseDto=[{}]", JSON.toJSONString(responseDto));
            if (!responseDto.isSuccess()) {
                return responseDto;
            }
            ccContractId = responseDto.getData().getBankCcContact().getId();
            ccAccountId = responseDto.getData().getBankCcAccount().getId();

            // insert adam cc account
            AdamBankCcAccount adamCcAccount = new AdamBankCcAccount();
            adamCcAccount.setId(adamAccount.getComId());
            adamCcAccount.setContactId(ccContractId);
            adamCcAccount.setBankId(ccAccountId);
            adamCcAccount.setCreateTime(LocalDateTime.now());
            adamBankCcAccountService.insert(adamCcAccount);

            // update adam account
            AdamAccount updateAdamAccount = new AdamAccount();
            updateAdamAccount.setId(adamAccount.getId());
            updateAdamAccount.setBankId(ccAccountId);
            adamAccountService.updateById(updateAdamAccount);
        }
        if (StringUtils.isEmpty(ccContractId)) {
            AdamBankCcAccount adamCcAccount = adamBankCcAccountService.selectById(adamAccount.getComId());
            ccContractId = adamCcAccount.getContactId();
        }
        // 异步 cc balance info
        adamAccountAsyncService.ccAsyncRetrieveBalance(param.getCurrency(), ccContractId);
        return responseDto;
    }

}
