package com.bizzan.bitrade.service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.NotifyType;
import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.dao.MemberDepositDao;
import com.bizzan.bitrade.dao.UserPurseDao;
import com.bizzan.bitrade.constant.WalletType;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.ContractMoneyType;
import com.bizzan.bitrade.entity.ContractPositionType;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.entity.MemberContractWallet;
import com.bizzan.bitrade.entity.MemberDeposit;
import com.bizzan.bitrade.entity.MemberTransaction;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.StakingWallet;
import com.bizzan.bitrade.entity.UserPurse;
import com.bizzan.bitrade.entity.WalletTransRecord;
import com.bizzan.bitrade.service.Base.BaseService;
import com.bizzan.bitrade.service.LocaleMessageSourceService;
import com.bizzan.bitrade.util.DateUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.util.RedisManager;
import com.bizzan.bitrade.vo.MemberContractPositionWithCut;
import com.bizzan.bitrade.vo.MemberContractWalletVo;
import com.bizzan.bitrade.vo.SpotWalletVo;
import com.bizzan.bitrade.vo.TradingAccount;
import com.querydsl.core.types.Predicate;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class UserPurseService implements WalletService {

	@Autowired
	private UserPurseDao userPurseDao;
	@Autowired
	private MemberTransactionService transactionService;
	@Autowired
	private MemberService memberService;
	@Autowired
	private CoinService coinService;
	@Autowired
	private SysConfigService sysConfigService;
    @Autowired
    private MemberContractWalletService memberContractWalletService;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private WalletTransRecordService walletTransRecordService;
    @Autowired
    private MemberHistoryService memberHistoryService;
	@Autowired
	private RedisManager redisManager;
	@Autowired
	private LocaleMessageSourceService msService;
	
	@Transactional(rollbackFor = Exception.class)
	public void transferIn(Long id, BigDecimal amount) {
		userPurseDao.increaseBalance(id, amount);
	}
	@Transactional(rollbackFor = Exception.class)
	public boolean transferOut(Long id, BigDecimal amount) {
		return userPurseDao.decreaseBalance(id, amount) > 0;
	}

	/**
	 * 资金账户划入交易账户（币币 → 交易账户），原子操作
	 */
	@Transactional(rollbackFor = Exception.class)
	public MessageResult transferSpotToTrading(Long memberId, BigDecimal amount) {
		MemberWallet spotWallet = memberWalletService.findByCoinUnitAndMemberId("USDT", memberId);
		if (spotWallet == null) {
			return MessageResult.error(msService.getMessage("WALLET_NOT_OUT"));
		}
		if (spotWallet.getBalance().compareTo(amount) < 0) {
			return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
		}
		int updated = memberWalletService.deductBalance(spotWallet, amount);
		if (updated != 1) {
			return MessageResult.error(msService.getMessage("TRANSFER_FAILED"));
		}
		userPurseDao.increaseBalance(memberId, amount);

		WalletTransRecord record = new WalletTransRecord();
		record.setAmount(amount);
		record.setUnit("USDT");
		record.setSource(WalletType.SPOT);
		record.setTarget(WalletType.SWAP);
		record.setMemberId(memberId);
		walletTransRecordService.save(record);
		return MessageResult.success();
	}

	/**
	 * 交易账户划出到资金账户（交易账户 → 币币），原子操作
	 */
	@Transactional(rollbackFor = Exception.class)
	public MessageResult transferTradingToSpot(Long memberId, BigDecimal amount) {
		UserPurse userPurse = findOne(memberId);
		if (userPurse == null) {
			return MessageResult.error(msService.getMessage("WALLET_NOT_OUT"));
		}
		if (userPurse.getCurrentPositionList() != null && !userPurse.getCurrentPositionList().isEmpty()) {
			return MessageResult.error(msService.getMessage("TRANSFER_WITH_POSITION"));
		}
		if (userPurse.getBalance().compareTo(amount) < 0) {
			return MessageResult.error(msService.getMessage("WALLET_NOT_AMOUNT_OUT"));
		}
		MemberWallet spotWallet = memberWalletService.findByCoinUnitAndMemberId("USDT", memberId);
		if (spotWallet == null) {
			return MessageResult.error(msService.getMessage("WALLET_NOT_IN"));
		}
		boolean ok = userPurseDao.decreaseBalance(memberId, amount) > 0;
		if (!ok) {
			return MessageResult.error(msService.getMessage("TRANSFER_FAILED"));
		}
		memberWalletService.increaseBalance(spotWallet.getId(), amount);

		WalletTransRecord record = new WalletTransRecord();
		record.setAmount(amount);
		record.setUnit("USDT");
		record.setSource(WalletType.SWAP);
		record.setTarget(WalletType.SPOT);
		record.setMemberId(memberId);
		walletTransRecordService.save(record);
		return MessageResult.success();
	}
	
	public void updateIsSupervised(Long id, int isSupervised) {
		userPurseDao.updateIsSupervised(id, isSupervised);
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void clearOpenFee(Long id, ContractCoin coin, BigDecimal margin, BigDecimal openFee) {
		userPurseDao.clearOpenFee(id, margin, openFee);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), openFee);

		transactionService.recordContractFee(id, coin.getBaseSymbol(), openFee, 0);

		memberContractWalletService.thawUsdtBalance(id, coin, margin.add(openFee));
	}

	@Transactional(rollbackFor = Exception.class)
	public void clearOpenFee(Long id, ContractCoin coin, BigDecimal margin, BigDecimal openFee, Long time) {
		userPurseDao.clearOpenFee(id, margin, openFee);

		transactionService.recordContractFee(id, coin.getBaseSymbol(), openFee, 0, new Date(time));

		memberContractWalletService.thawUsdtBalance(id, coin, margin.add(openFee));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void clearContractCloseFee(Long id, ContractCoin coin, BigDecimal amount) {
		userPurseDao.clearCloseFee(id, amount);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		
		transactionService.recordContractFee(id, coin.getBaseSymbol(), amount, 1);
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void clearProfitOrLoss(Long id, ContractCoin coin, BigDecimal amount) {
		userPurseDao.increaseBalance(id, amount);
		//if(amount.compareTo(BigDecimal.ZERO)>0)
		//	contractCoinService.increaseTotalProfit(coin.getId(), amount);
		//else
		//	contractCoinService.increaseTotalLoss(coin.getId(), BigDecimal.ZERO.subtract(amount));
		
		transactionService.recordProfitOrLoss(id, coin.getBaseSymbol(), amount);
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void clearContractOvernightFee(Long id, ContractCoin coin, BigDecimal amount, BigDecimal rebateFee) {
		int count = userPurseDao.clearContractOvernightFee(id, amount.subtract(rebateFee));
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		if(count!=0) {
			transactionService.recordContractOvernightFee(id, coin.getBaseSymbol(), amount);
			if(rebateFee.compareTo(BigDecimal.ZERO) != 0)
				transactionService.recordContractOvernightFee(id, coin.getBaseSymbol(), BigDecimal.ZERO.subtract(rebateFee));
		}
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void clearContractOvernightFee(Long id, ContractCoin coin, BigDecimal amount) {
		int count = userPurseDao.clearContractOvernightFee(id, amount);
		//contractCoinService.increaseTotalCloseFee(coin.getId(), amount);
		if(count!=0)
			transactionService.recordContractOvernightFee(id, coin.getBaseSymbol(), amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void restoreMinusBalance(Long id) {
		userPurseDao.restoreMinusBalance(id);
		
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void zeroBalance(Long id) {
		BigDecimal balance = userPurseDao.findOne(id).getBalance();
		userPurseDao.zeroBalance(id);
		transactionService.recordContractBlastFee(id, balance);
		
	}
	
	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginAndFee(Long id, ContractCoin contractCoin, BigDecimal amount) {
		Member member = memberService.findOne(id);
		if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return false;
        }
		if (member.getIsMoneyLock()!=null && member.getIsMoneyLock()==1) {
            return false;
        }

		int successCount = userPurseDao.freezeMargin(id, amount);
		if(successCount == 0)
			return false;

		memberContractWalletService.freezeUsdtBalance(id, contractCoin, amount);
		return true;
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginDirectly(Long id, ContractCoin coin, BigDecimal principal, BigDecimal fee) {
		return freezeMarginDirectly(id, coin, principal, fee, null);
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean freezeMarginDirectly(Long id, ContractCoin coin, BigDecimal principal, BigDecimal fee, Long openTime) {
		Member member = memberService.findOne(id);
		if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
			return false;
		}
		if (member.getIsMoneyLock() != null && member.getIsMoneyLock() == 1) {
			return false;
		}
		BigDecimal required = principal.add(fee);
		int result = userPurseDao.freezeBalanceDirect(id, required, principal);
		if (result == 0) {
			UserPurse purse = userPurseDao.findOne(id);
			log.warn("【批量开仓】保证金不足, memberId={}, 应冻结={}, 实际余额={}",
					id, required, purse != null ? purse.getBalance() : "查询失败");
			return false;
		}
		if (openTime != null) {
			transactionService.recordContractFee(id, coin.getBaseSymbol(), fee, 0, new Date(openTime));
		} else {
			transactionService.recordContractFee(id, coin.getBaseSymbol(), fee, 0);
		}
		return true;
	}
	
	//解冻保证金
	@Transactional(rollbackFor = Exception.class)
	public void thawMargin(Long id, ContractCoin contractCoin, BigDecimal amount) {
		userPurseDao.thawMargin(id, amount);
		
		memberContractWalletService.thawUsdtBalance(id, contractCoin, amount);
	}
	
	//释放已使用保证金
	@Transactional(rollbackFor = Exception.class)
	public void releaseMargin(Long id, BigDecimal amount) {
		userPurseDao.releaseMargin(id, amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void increaseBalance(Long id, BigDecimal amount) {
		userPurseDao.increaseBalance(id, amount);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public int modifyLeverage(Long id, BigDecimal leverage, BigDecimal marginDifference) {
		return userPurseDao.modifyLeverage(id, leverage, marginDifference);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public int adjustMargin(Long id, BigDecimal marginDifference) {
		return userPurseDao.adjustMargin(id, marginDifference);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void modifyPositionType(Long id, ContractPositionType positionType) {
		userPurseDao.modifyPositionType(id, positionType);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void modifyMoneyType(Long id, ContractMoneyType moneyType) {
		userPurseDao.modifyMoneyType(id, moneyType);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void modifyConfig(Long id, BigDecimal leverage, ContractPositionType positionType, ContractMoneyType moneyType) {
		userPurseDao.modifyConfig(id, leverage, positionType, moneyType);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void updateEUsdtAddress(Long id, String address) {
		userPurseDao.updateEUsdtAddress(id, address);
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void updateTUsdtAddress(Long id, String address) {
		userPurseDao.updateTUsdtAddress(id, address);
	}
	
	public UserPurse findOne(Long id) {
		UserPurse userPurse = userPurseDao.findOne(id);
		if(userPurse == null) {
			userPurse = new UserPurse();
			userPurse.setId(id);
			userPurse.setBalance(BigDecimal.ZERO);
			userPurse.setFrozenBalance(BigDecimal.ZERO);
			userPurse.setFrozenMargin(BigDecimal.ZERO);
			userPurse.setLeverage(BigDecimal.TEN);
			userPurse.setPositionType(ContractPositionType.INTEGRAL);
			userPurse.setMoneyType(ContractMoneyType.COIN);
			userPurseDao.save(userPurse);
		}
		return userPurse;
	}
	
	public UserPurse create(Long id) {
		UserPurse userPurse = new UserPurse();
		userPurse.setId(id);
		userPurse.setBalance(BigDecimal.ZERO);
		userPurse.setFrozenBalance(BigDecimal.ZERO);
		userPurse.setFrozenMargin(BigDecimal.ZERO);
		userPurse.setLeverage(BigDecimal.TEN);
		userPurse.setPositionType(ContractPositionType.INTEGRAL);
		userPurse.setMoneyType(ContractMoneyType.COIN);
		userPurseDao.save(userPurse);
		return userPurse;
	}

	/**
	 * 确保该用户拥有 USDT 的 MemberWallet 行；缺失则自动创建一行零余额钱包并落库。
	 * 用于规避「老用户或异常用户无 USDT 钱包导致 spotWallet 为 null」引发的 NPE。
	 * 返回值保证非 null。
	 */
	public MemberWallet ensureUsdtMemberWallet(Long memberId) {
		com.bizzan.bitrade.entity.Coin usdt = coinService.findByUnit("USDT");
		if (usdt == null) return null;
		MemberWallet wallet = memberWalletService.findByCoinAndMemberId(usdt, memberId);
		if (wallet != null) return wallet;
		try {
			wallet = new MemberWallet();
			wallet.setCoin(usdt);
			wallet.setMemberId(memberId);
			wallet.setBalance(BigDecimal.ZERO);
			wallet.setFrozenBalance(BigDecimal.ZERO);
			wallet.setAddress("");
			wallet = memberWalletService.save(wallet);
			log.info("[ensureUsdtMemberWallet] auto created USDT wallet for memberId={}", memberId);
			return wallet;
		} catch (Exception e) {
			// 并发或唯一约束导致的重复插入：重新查一次
			log.warn("[ensureUsdtMemberWallet] create failed memberId={}, err={}, refetching", memberId, e.getMessage());
			return memberWalletService.findByCoinAndMemberId(usdt, memberId);
		}
	}

	/** 将 USDT 钱包余额包装为 SpotWalletVo；钱包行缺失时自动创建。返回值保证非 null。 */
	public SpotWalletVo buildSpotWalletVo(Long memberId) {
		MemberWallet memberWallet = ensureUsdtMemberWallet(memberId);
		SpotWalletVo spot = new SpotWalletVo();
		if (memberWallet != null) {
			spot.setBalance(memberWallet.getBalance() != null ? memberWallet.getBalance() : BigDecimal.ZERO);
			spot.setFrozenBalance(memberWallet.getFrozenBalance() != null ? memberWallet.getFrozenBalance() : BigDecimal.ZERO);
		} else {
			spot.setBalance(BigDecimal.ZERO);
			spot.setFrozenBalance(BigDecimal.ZERO);
		}
		return spot;
	}

	public TradingAccount findOneVO(Long id) {
		UserPurse userPurse = findOne(id);
		TradingAccount vo = new TradingAccount();
		BeanUtils.copyProperties(userPurse, vo);
		List<MemberContractPositionWithCut> positionList = new ArrayList<>();
		for(MemberContractPosition postion:userPurse.getCurrentPositionList()) {
			MemberContractPositionWithCut positionWithCut = new MemberContractPositionWithCut();
			BeanUtils.copyProperties(postion, positionWithCut);
			positionList.add(positionWithCut);
		}
		vo.setCurrentPositionWithCutList(positionList);
		vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
		List<MemberContractWalletVo> walletVoList = new ArrayList<>();
		for(MemberContractWallet wallet:memberContractWalletService.findEnabledByMemberId(userPurse.getId())) {
			MemberContractWalletVo walletVo = new MemberContractWalletVo();
			BeanUtils.copyProperties(wallet, walletVo);
			walletVoList.add(walletVo);
		}
		vo.setWalletList(walletVoList);
		vo.setTodayRevenue(this.getTodayRevenue(id));
		vo.setCurrentDate(DateUtil.getDate());
		vo.setTodayInitialCapital(this.getBalanceBeforToday(userPurse.getId()));
		//log.info("getTodayInitialCapital={}", vo.getTodayInitialCapital());
		MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coinService.findByUnit("USDT"), userPurse.getId());
		SpotWalletVo spotwalelt = new SpotWalletVo();
		if(memberWallet!=null) {
			spotwalelt.setBalance(memberWallet.getBalance());
			spotwalelt.setFrozenBalance(memberWallet.getFrozenBalance());
		}
		vo.setSpotWallet(spotwalelt);

		vo.setSpotWallet(buildSpotWalletVo(userPurse.getId()));

		// 合约账户权益 + 资金账户折合 USDT + 质押账户折合 USDT
		vo.setCashBalance(computeCashBalance(userPurse.getId(), vo));

		return vo;
	}

	/**
	 * 计算用户全账户总资产（USDT 估值）
	 * = 合约账户当前权益 + 资金账户各币种折合 USDT + 质押账户各币种折合 USDT
	 * 跟单账户和交易所账户由各自 Monitor 维护，此处不纳入（避免循环依赖）
	 *
	 * @param memberId 用户ID
	 * @param vo       已填充合约权益的 TradingAccount
	 * @return cashBalance（总资产估值）
	 */
	private BigDecimal computeCashBalance(Long memberId, TradingAccount vo) {
		// 1. 合约账户当前权益（balance + frozenBalance + frozenMargin，不含持仓浮盈）
		BigDecimal cash = vo.getStaticBalance() != null ? vo.getStaticBalance() : BigDecimal.ZERO;

		// 2. 资金账户：遍历所有币种钱包，按现货价格折合 USDT
		cash = cash.add(sumFundWalletsUsdt(memberId));

		// 3. 质押账户：遍历所有质押钱包，按现货价格折合 USDT
		cash = cash.add(sumStakingWalletsUsdt(memberId));

		return cash;
	}

	/**
	 * 汇总用户资金账户（member_wallet）各币种余额折合 USDT
	 *
	 * @param memberId 用户ID
	 * @return 资金账户总 USDT 估值
	 */
	private BigDecimal sumFundWalletsUsdt(Long memberId) {
		BigDecimal total = BigDecimal.ZERO;
		List<MemberWallet> wallets = memberWalletService.findAllByMemberId(memberId);
		for (MemberWallet mw : wallets) {
			BigDecimal bal    = mw.getBalance()       != null ? mw.getBalance()       : BigDecimal.ZERO;
			BigDecimal frozen = mw.getFrozenBalance() != null ? mw.getFrozenBalance() : BigDecimal.ZERO;
			// 余额均为 0 时跳过
			if (bal.compareTo(BigDecimal.ZERO) <= 0 && frozen.compareTo(BigDecimal.ZERO) <= 0) {
				continue;
			}
			String unit = mw.getCoin() != null ? mw.getCoin().getUnit() : null;
			if (unit == null) {
				continue;
			}
			if ("USDT".equalsIgnoreCase(unit)) {
				// USDT 直接计入
				total = total.add(bal).add(frozen);
			} else {
				// 其他币种：从 Redis 读取现货价格折合 USDT
				BigDecimal price = redisManager.getSpotPriceUsdt(unit);
				if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
					total = total.add(bal.add(frozen).multiply(price));
				}
			}
		}
		return total;
	}

	/**
	 * 汇总用户质押账户（staking_wallet）各币种余额折合 USDT
	 *
	 * @param memberId 用户ID
	 * @return 质押账户总 USDT 估值
	 */
	private BigDecimal sumStakingWalletsUsdt(Long memberId) {
		BigDecimal total = BigDecimal.ZERO;
		List<StakingWallet> stakingWallets = memberWalletService.getStakingWallets(memberId);
		for (StakingWallet sw : stakingWallets) {
			// 质押钱包只有锁定余额，无可用余额
			BigDecimal locked = sw.getLockedBalance() != null ? sw.getLockedBalance() : BigDecimal.ZERO;
			if (locked.compareTo(BigDecimal.ZERO) <= 0) {
				continue;
			}
			String unit = sw.getCoinUnit();
			if (unit == null) {
				continue;
			}
			if ("USDT".equalsIgnoreCase(unit)) {
				total = total.add(locked);
			} else {
				BigDecimal price = redisManager.getSpotPriceUsdt(unit);
				if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
					total = total.add(locked.multiply(price));
				}
			}
		}
		return total;
	}
	
	public Integer getIsSupervised(Long id) {
		return userPurseDao.getIsSupervised(id);
	}
	
	public BigDecimal getBalanceById(Long id) {
		return userPurseDao.getBalanceById(id);
	}
	
	public ConcurrentHashMap<Long, TradingAccount> findAllWithPositions() {
		ConcurrentHashMap<Long, TradingAccount> tradingAccountMap = new ConcurrentHashMap<Long, TradingAccount>();
		List<UserPurse> userPurseList = userPurseDao.findAllWithPositions();
		for(UserPurse userPurse: userPurseList) {
			TradingAccount vo = new TradingAccount();
			BeanUtils.copyProperties(userPurse, vo);
			List<MemberContractPositionWithCut> positionList = new ArrayList<>();
			for(MemberContractPosition postion:userPurse.getCurrentPositionList()) {
				MemberContractPositionWithCut positionWithCut = new MemberContractPositionWithCut();
				BeanUtils.copyProperties(postion, positionWithCut);
				positionList.add(positionWithCut);
			}
			vo.setCurrentPositionWithCutList(positionList);
			vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
			List<MemberContractWalletVo> walletVoList = new ArrayList<>();
			for(MemberContractWallet wallet:memberContractWalletService.findEnabledByMemberId(userPurse.getId())) {
				MemberContractWalletVo walletVo = new MemberContractWalletVo();
				BeanUtils.copyProperties(wallet, walletVo);
				walletVoList.add(walletVo);
			}
			vo.setWalletList(walletVoList);
			vo.setTodayRevenue(this.getTodayRevenue(userPurse.getId()));
			
			MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coinService.findByUnit("USDT"), userPurse.getId());
			SpotWalletVo spotwalelt = new SpotWalletVo();
			if(memberWallet!=null) {
				spotwalelt.setBalance(memberWallet.getBalance());
				spotwalelt.setFrozenBalance(memberWallet.getFrozenBalance());
			}
			vo.setSpotWallet(spotwalelt);


			vo.setSpotWallet(buildSpotWalletVo(userPurse.getId()));

			//vo.setCurrentDate(DateUtil.getDate());
			//vo.setTodayInitialCapital(this.getBalanceBeforToday(userPurse.getId()));
			//log.info("positions={}", vo.getCurrentPositionList());
			tradingAccountMap.put(vo.getId(), vo);
		}
		return tradingAccountMap;
	}
	
	public BigDecimal getTodayRevenue(Long id) {
		List<Integer> types = new ArrayList<Integer>();
		types.add(TransactionType.RECHARGE.getOrdinal());
		types.add(TransactionType.WITHDRAW.getOrdinal());
		types.add(TransactionType.ADMIN_RECHARGE.getOrdinal());
		types.add(TransactionType.TRANSFER_ACCOUNTS.getOrdinal());
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
    	String start_date = sdf.format(new Date());
    	String end_date = start_date + " 23:59:59";
		return transactionService.sumAmountByMemberIdAndNotTypesAndTime(start_date, end_date, id, types);
	}
	
	/**
	 * 计算今日收益率基准分母（昨日末总资产估值）
	 * = 合约账户历史累计净流入 + 昨日末质押余额快照
	 *
	 * @param id 用户ID（UserPurse主键，等同于memberId）
	 * @return 基准资产估值，用于 todayRevenueRate 分母
	 */
	public BigDecimal getBalanceBeforToday(Long id) {
		// 1. 合约账户历史累计净流入（原有逻辑）
		BigDecimal contractBase = memberHistoryService.sumBalanceByMemberId(id);
		// 2. 昨日末质押余额快照（最新一条 member_history 中的 staking_balance）
		BigDecimal stakingBase = memberHistoryService.getLatestStakingBalance(id);
		return contractBase.add(stakingBase);
	}
	
	public TradingAccount findOneVOFromRedis(Long id) {
		Object object = redisManager.getTradingAccount(id);
		if(object != null) {
			try {
				String jsonStr = object.toString();
				TradingAccount tradingAccount = JSON.parseObject(jsonStr, TradingAccount.class);
				if(tradingAccount!=null && tradingAccount.getId()!=null) {
					if (tradingAccount.getCurrentCapital() == null || tradingAccount.getCurrentCapital().compareTo(BigDecimal.ZERO) == 0) {
						BigDecimal dbBalance = userPurseDao.getBalanceById(id);
						log.info("[contract-asset-new] from Redis: memberId={}, redisCurrentCapital={}, redisBalance={}, dbUserPurseBalance={}",
								id, tradingAccount.getCurrentCapital(), tradingAccount.getBalance(), dbBalance);
					}
					return tradingAccount;
				}
			} catch (Exception e) {
				log.info("tradingAccount format error, id={}", id);
			}
		}
		TradingAccount tradingAccount = findOneVO(id);
		if (tradingAccount.getCurrentCapital() == null || tradingAccount.getCurrentCapital().compareTo(BigDecimal.ZERO) == 0) {
			BigDecimal dbBalance = userPurseDao.getBalanceById(id);
			log.info("[contract-asset-new] from DB findOneVO: memberId={}, currentCapital={}, taBalance={}, taFrozenBalance={}, taFrozenMargin={}, dbUserPurseSum={}",
					id, tradingAccount.getCurrentCapital(), tradingAccount.getBalance(), tradingAccount.getFrozenBalance(),
					tradingAccount.getFrozenMargin(), dbBalance);
		}
		redisManager.saveTradingAccount(id, JSON.toJSONString(tradingAccount));
		return tradingAccount;
	}
	
	public void deleteVOFromRedis(Long id) {
		redisManager.removeTradingAccount(id);
	}
	
	public List<UserPurse> findByIds(List<Long> ids) {
		return userPurseDao.findByIds(ids);
	}
	
	public Page<TradingAccount> findAllVo(Predicate predicate, Pageable pageable, Map<String, BigDecimal> currentPriceMap) {
		Page<UserPurse> all = findAll(predicate, pageable);
		List<UserPurse> userPurseList = all.getContent();
		List<TradingAccount> retList = new ArrayList<TradingAccount>();
		for(UserPurse userPurse: userPurseList) {
			TradingAccount vo = new TradingAccount();
			BeanUtils.copyProperties(userPurse, vo);
			List<MemberContractPositionWithCut> positionList = new ArrayList<>();
			for(MemberContractPosition postion:userPurse.getCurrentPositionList()) {
				MemberContractPositionWithCut positionWithCut = new MemberContractPositionWithCut();
				BeanUtils.copyProperties(postion, positionWithCut);
				positionList.add(positionWithCut);
			}
			vo.setCurrentPositionWithCutList(positionList);
			vo.setBlastMarginRate(sysConfigService.get().getContractMarginRate());
			//vo.setWalletList(memberContractWalletService.findAllByMemberId(userPurse.getId()));
			vo.setCashBalance(memberWalletService.sumBalanceByMemberId(userPurse.getId()));
			vo.setUsername(memberService.findUserNameById(userPurse.getId()));
			vo.setCurrentPriceMap(currentPriceMap);
			retList.add(vo);
		}
		Page<TradingAccount> newAll = new PageImpl<TradingAccount>(retList, pageable, all.getTotalElements());
		return newAll;
	}
	
	public Page<UserPurse> findAll(Predicate predicate, Pageable pageable) {
		return userPurseDao.findAll(predicate, pageable);
	}
	
	public BigDecimal sumBalanceByMemberIds(List<Long> ids) {
		return userPurseDao.sumBalanceByMemberIds(ids);
	}
	
	public long countWithBalanceByMemberIds(List<Long> ids) {
		return userPurseDao.countWithBalanceByMemberIds(ids);
	}
	
	public BigDecimal sumBalance() {
		return userPurseDao.sumBalance();
	}
	
	public List<BigInteger> findIdListByIsSupervised() {
		return userPurseDao.findIdListByIsSupervised();
	}
}
