package com.bizzan.bitrade.controller;

import static com.bizzan.bitrade.constant.SysConstant.SESSION_MEMBER;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.consumer.SpotListener;
import com.bizzan.bitrade.engine.ContractCoinMatch;
import com.bizzan.bitrade.engine.ContractCoinMatchFactory;
import com.bizzan.bitrade.engine.FollowAccountMonitor;
import com.bizzan.bitrade.engine.TradingAccountMonitor;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.ContractMoneyType;
import com.bizzan.bitrade.entity.ContractOrderDirection;
import com.bizzan.bitrade.entity.ContractOrderEntrust;
import com.bizzan.bitrade.entity.ContractPositionStatus;
import com.bizzan.bitrade.entity.ContractPositionType;
import com.bizzan.bitrade.entity.FollowAccount;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.entity.MemberContractWallet;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.StakingWallet;
import com.bizzan.bitrade.entity.SysConfig;
import com.bizzan.bitrade.entity.UserPurse;
import com.bizzan.bitrade.entity.transform.AuthMember;
import com.bizzan.bitrade.service.CoinService;
import com.bizzan.bitrade.service.ContractCoinService;
import com.bizzan.bitrade.service.ContractOrderEntrustService;
import com.bizzan.bitrade.service.ExchangeWalletService;
import com.bizzan.bitrade.service.FundingRateHistoryService;
import com.bizzan.bitrade.service.LocaleMessageSourceService;
import com.bizzan.bitrade.service.MemberContractPositionService;
import com.bizzan.bitrade.service.MemberContractWalletService;
import com.bizzan.bitrade.service.MemberService;
import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.service.MemberTransactionService;
import com.bizzan.bitrade.service.MemberWalletService;
import com.bizzan.bitrade.service.RedisLock;
import com.bizzan.bitrade.service.SpotWalletSnapshotService;
import com.bizzan.bitrade.service.StakingService;
import com.bizzan.bitrade.service.SysConfigService;
import com.bizzan.bitrade.service.UserPurseService;
import com.bizzan.bitrade.util.DateUtil;
import com.bizzan.bitrade.util.InnerInvokeUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vo.AccountInfo;
import com.bizzan.bitrade.vo.ExchangeAccount;
import com.bizzan.bitrade.vo.MemberContractPositionWithCut;
import com.bizzan.bitrade.vo.MemberContractWalletVo;
import com.bizzan.bitrade.service.FundAssetService;
import com.bizzan.bitrade.vo.FundAssetListResp;
import com.bizzan.bitrade.vo.SpotWalletVo;
import com.bizzan.bitrade.vo.TradingAccount;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Api(value = "WalletNewController" , tags = "交易账户接口")
@RestController
@RequestMapping("wallet-new")
public class WalletNewController {

	@Autowired
	private UserPurseService userPurseService;
	@Autowired
    private TradingAccountMonitor tradingAccountMonitor;
    @Autowired
    private LocaleMessageSourceService msService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private MemberContractPositionService memberContractPositionService;
    @Autowired
    private MemberContractWalletService memberContractWalletService;
    @Autowired
    private ContractCoinService contractCoinService;
    @Autowired
    private ContractCoinMatchFactory contractCoinMatchFactory;
	@Autowired
	private RedisLock redisLock;
	@Autowired
    private FollowAccountMonitor followAccountMonitor;
	@Autowired
	private SysConfigService sysConfigService;
	@Autowired
	private ExchangeWalletService exchangeWalletService;
	@Autowired
	private SpotWalletSnapshotService spotWalletSnapshotService;
	@Autowired
	private SpotListener spotListener;
	@Autowired
	private MemberWalletService memberWalletService;
	@Autowired
	private MemberTransactionService memberTransactionService;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private FundAssetService fundAssetService;
	@Autowired
	private StakingService stakingService;

	@ApiOperation(value="根据memberId获取合约账户权益", notes="供trade项目等内部调用，返回TradingAccount实体，接收方解析balance+frozenBalance+frozenMargin计算权益")
	@GetMapping("contract-asset-new")
	public MessageResult<Map<String, String>> getContractAssetByMemberId(@RequestParam("memberId") Long memberId) {
        MessageResult<Map<String, String>> result = MessageResult.success("success");
        Map<String, String> map = new HashMap<>();
        map.put("memberId",memberId.toString());
        try {
			TradingAccount tradingAccount = tradingAccountMonitor.get(memberId);
			if (tradingAccount == null) {
				map= buildEmptyTradingAccount(map);
			}else {
                map.put("balance", tradingAccount.getBalance().toPlainString());
                map.put("frozenBalance", tradingAccount.getFrozenBalance().toPlainString());
                map.put("frozenMargin", tradingAccount.getFrozenMargin().toPlainString());
            }
			result.setData(map);
			return result;
		} catch (Exception e) {
			log.warn("[contract-asset-new] memberId={}, error={}", memberId, e.getMessage());
			result.setData(buildEmptyTradingAccount(map));
			return result;
		}
	}

	/** 构建空TradingAccount，balance/frozenBalance/frozenMargin均为0，供接收方解析 */
	private  Map<String, String> buildEmptyTradingAccount(Map<String, String> map) {
        map.put("balance", "0");
        map.put("frozenBalance","0");
        map.put("frozenMargin", "0");
		return map;
	}

	@ApiOperation(value="获取交易账户信息", notes="资产页面使用")
	@GetMapping("get")
	public MessageResult<TradingAccount> get(@SessionAttribute(SESSION_MEMBER) AuthMember authMember) {
		TradingAccount tradingAccount = tradingAccountMonitor.get(authMember.getId());
		if(tradingAccount==null) {
			log.info("get account null, try to reload from db, memberId: {}", authMember.getId());
			tradingAccountMonitor.updateAccount(authMember.getId());
			tradingAccount = tradingAccountMonitor.get(authMember.getId());
		}
		if(tradingAccount==null) {
			log.info("get account still null after reload, memberId: {}", authMember.getId());
			MessageResult<TradingAccount> result = MessageResult.success("success");
	        result.setData(null);
	        return result;
		}
		tradingAccount.genWalletList();
		TradingAccount vo = new TradingAccount();
		BeanUtils.copyProperties(tradingAccount, vo);
		vo.setCurrentPriceMap(tradingAccountMonitor.getPriceMapAtPresent());
		
		List<MemberContractWalletVo> walletList = tradingAccount.getWalletList();
		for(MemberContractWalletVo wallet: walletList) {
			ContractCoinMatch match = contractCoinMatchFactory.getContractCoinMatch(wallet.getContractCoin().getSymbol());
			if(match == null)
				continue;
			List<ContractOrderEntrust> orderList = match.findRunningOpenOrdersByMemberId(authMember.getId());
			BigDecimal frozenMargin = BigDecimal.ZERO;
			for(ContractOrderEntrust order : orderList) {
				frozenMargin = frozenMargin.add(order.getPrincipalAmount().add(order.getOpenFee()));
			}
			wallet.setUsdtFrozenBalance(frozenMargin.setScale(2, BigDecimal.ROUND_DOWN));
			wallet.setUsdtBalance(wallet.getUsdtBalance().setScale(2, BigDecimal.ROUND_DOWN));
			wallet.setUsdtBuyPrincipalAmount(wallet.getUsdtBuyPrincipalAmount().setScale(2, BigDecimal.ROUND_DOWN));
			//wallet.setContractCoin(null);
		}
		
		BigDecimal cashBalance = tradingAccount.getCurrentCapital();
		
		List<AccountInfo> accountInfoList = new ArrayList<>();
		vo.setAccountInfoList(accountInfoList);
		AccountInfo swapAccountInfo = new AccountInfo();
		accountInfoList.add(swapAccountInfo);
		swapAccountInfo.setName(msService.getMessage("TRADING_ACCOUNT_NAME"));
		swapAccountInfo.setBalance(tradingAccount.getAvailableBalance());
		swapAccountInfo.setFrozenBalance(tradingAccount.getCurrentMargin());
		swapAccountInfo.setFrozenMargin(tradingAccount.getFrozenMargin());
		swapAccountInfo.setCurrentCapital(cashBalance);
		
		//MemberWallet memberWallet = walletService.findByCoinAndMemberId(coinService.findByUnit("USDT"), tradingAccount.getId());
    	//if(memberWallet!=null)
    	//	cashBalance = cashBalance.add(memberWallet.getBalance().add(memberWallet.getFrozenBalance()));
		SpotWalletVo spotWalletVo = tradingAccount.getSpotWallet();
		if (spotWalletVo == null) {
			// 缓存中的 tradingAccount 可能尚未填充 spotWallet（老用户/钱包行缺失场景）。
			// 通过 userPurseService 自动建表并同步回内存对象，避免 NPE 同时治本。
			spotWalletVo = userPurseService.buildSpotWalletVo(authMember.getId());
			tradingAccount.setSpotWallet(spotWalletVo);
		}
		// 资金账户：遍历所有币种钱包，余额 > 0 的按现货行情折合为 USDT 汇总
		BigDecimal fundAvailableUsdt = BigDecimal.ZERO;
		BigDecimal fundFrozenUsdt = BigDecimal.ZERO;
		Map<String, BigDecimal> spotPriceMap = spotListener.getCurrentPriceMap();
		List<MemberWallet> allMemberWallets = memberWalletService.findAllByMemberId(authMember.getId());
		for (MemberWallet mw : allMemberWallets) {
			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().getUnit();
			if ("USDT".equalsIgnoreCase(unit)) {
				// USDT 直接计入，无需换算
				fundAvailableUsdt = fundAvailableUsdt.add(bal);
				fundFrozenUsdt    = fundFrozenUsdt.add(frozen);
			} else {
				// 其他币种：优先查现货内存行情，无行情时 fallback 到 Redis（如 IBIT 等非现货交易对）
				BigDecimal price = spotPriceMap.get(unit + "/USDT");
				if (price == null || price.compareTo(BigDecimal.ZERO) <= 0) {
					price = getRedisSpotPrice(unit);
				}
				if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
					fundAvailableUsdt = fundAvailableUsdt.add(bal.multiply(price));
					fundFrozenUsdt    = fundFrozenUsdt.add(frozen.multiply(price));
				}
			}
		}
		BigDecimal fundTotalUsdt = fundAvailableUsdt.add(fundFrozenUsdt);
		cashBalance = cashBalance.add(fundTotalUsdt);

		FollowAccount followAccount = followAccountMonitor.get(authMember.getId());
    	if(followAccount!=null) {
    		BigDecimal followCapital = followAccount.getCurrentCapital();
    		cashBalance = cashBalance.add(followCapital);
    		vo.setFollowBalance(followCapital);
    		AccountInfo followAccountInfo = new AccountInfo();
    		accountInfoList.add(followAccountInfo);
    		followAccountInfo.setName(msService.getMessage("FOLLOW_ACCOUNT_NAME"));
    		followAccountInfo.setBalance(followAccount.getBalance());
    		followAccountInfo.setFrozenBalance(followAccount.getFrozenBalance());
    		followAccountInfo.setFrozenMargin(followAccount.getFrozenMargin());
    		followAccountInfo.setCurrentCapital(followCapital);
    	} else {
    		vo.setFollowBalance(BigDecimal.ZERO);
    	}

    	AccountInfo capitalAccountInfo = new AccountInfo();
		accountInfoList.add(capitalAccountInfo);
		capitalAccountInfo.setName(msService.getMessage("CASH_ACCOUNT_NAME"));
		capitalAccountInfo.setBalance(fundAvailableUsdt);
		capitalAccountInfo.setBalanceUsdt(fundAvailableUsdt);
		capitalAccountInfo.setFrozenBalance(fundFrozenUsdt);
		capitalAccountInfo.setFrozenBalanceUsdt(fundFrozenUsdt);
		capitalAccountInfo.setFrozenMargin(BigDecimal.ZERO);
		capitalAccountInfo.setCurrentCapital(fundTotalUsdt);

		// 质押账户：仅统计锁定余额（无可用余额，解锁后直接归还资金账户）
		List<StakingWallet> stakingWallets = memberWalletService.getStakingWallets(authMember.getId());
		BigDecimal stakingLocked     = BigDecimal.ZERO;  // 锁定原始数量（多币种累加，仅供展示参考）
		BigDecimal stakingLockedUsdt = BigDecimal.ZERO;  // 锁定折合 USDT
		BigDecimal stakingReleased = BigDecimal.ZERO;  // 已释放总量（原币数量，如 iBit，不做 USDT 折算）
		for (StakingWallet sw : stakingWallets) {
			BigDecimal locked = sw.getLockedBalance() != null ? sw.getLockedBalance() : BigDecimal.ZERO;
			stakingLocked = stakingLocked.add(locked);
			String swUnit = sw.getCoinUnit();
			BigDecimal released = stakingService.getUserReleasedAmount(authMember.getId(), swUnit);
			stakingReleased = stakingReleased.add(released);
			// 按币种折算 USDT：优先用现货行情，无行情时从 Redis ExchangeNew2:latestPrice 取价（仅用于锁定余额估值）
			if ("USDT".equalsIgnoreCase(swUnit)) {
				stakingLockedUsdt = stakingLockedUsdt.add(locked);
			} else {
				BigDecimal swPrice = spotPriceMap.get(swUnit + "/USDT");
				if (swPrice == null || swPrice.compareTo(BigDecimal.ZERO) <= 0) {
					// 现货行情无此币种，fallback 到 Redis（key 格式：ibitusdt，全小写）
					swPrice = getRedisSpotPrice(swUnit);
				}
				if (swPrice != null && swPrice.compareTo(BigDecimal.ZERO) > 0) {
					stakingLockedUsdt = stakingLockedUsdt.add(locked.multiply(swPrice));
				}
			}
		}
		AccountInfo stakingAccountInfo = new AccountInfo();
		accountInfoList.add(stakingAccountInfo);
		stakingAccountInfo.setName(msService.getMessage("STAKING_ACCOUNT_NAME"));
		stakingAccountInfo.setBalance(BigDecimal.ZERO);            // 无可用余额
		stakingAccountInfo.setBalanceUsdt(BigDecimal.ZERO);
		stakingAccountInfo.setFrozenBalance(stakingLocked);        // 锁定原始数量
		stakingAccountInfo.setFrozenBalanceUsdt(stakingLockedUsdt);// 锁定折合 USDT
		stakingAccountInfo.setReleasedTotal(stakingReleased);      // 已释放总量（iBit 等原币）
		stakingAccountInfo.setFrozenMargin(BigDecimal.ZERO);
		stakingAccountInfo.setCurrentCapital(stakingLockedUsdt);   // 权益使用 USDT 估值
		cashBalance = cashBalance.add(stakingLockedUsdt);          // 总资产用 USDT 估值

    	BigDecimal currentTodayRevenue = BigDecimal.ZERO;
    	
    	SysConfig sysConfig = sysConfigService.get();
    	if(sysConfig.getEnableExchange() == 1) {
	    	ExchangeAccount exchangeAccount = exchangeWalletService.findExchangeAccountFromRedis(authMember.getId());
	    	exchangeAccount.setPriceMap(spotListener.getCurrentPriceMap());
	    	exchangeAccount.setClearPriceMap(spotListener.getClearPriceMap());
	    	BigDecimal exchangeValue = exchangeAccount.getCurrentValue();
	    	AccountInfo exchangeAccountInfo = new AccountInfo();
			accountInfoList.add(exchangeAccountInfo);
			exchangeAccountInfo.setName(msService.getMessage("EXCHANGE_ACCOUNT_NAME"));
			exchangeAccountInfo.setBalance(exchangeValue);
			exchangeAccountInfo.setFrozenBalance(BigDecimal.ZERO);
			exchangeAccountInfo.setFrozenMargin(BigDecimal.ZERO);
			exchangeAccountInfo.setCurrentCapital(exchangeValue);
			cashBalance = cashBalance.add(exchangeValue);

			// 今日现货盈亏 = 当前持仓市值 - 昨日快照市值
			// 若昨日无快照数据（首次运行）则降级为 getTodayProfit() 估算
			BigDecimal yesterdaySpotValue = spotWalletSnapshotService.getYesterdayValue(authMember.getId());
			if (yesterdaySpotValue.compareTo(BigDecimal.ZERO) > 0) {
				currentTodayRevenue = currentTodayRevenue.add(exchangeValue.subtract(yesterdaySpotValue));
			} else {
				currentTodayRevenue = currentTodayRevenue.add(exchangeAccount.getTodayProfit());
			}
    	}
    	
    	BigDecimal todayRevenue = tradingAccount.getTodayRevenue();
    	BigDecimal followRevenue = followAccount != null ? followAccount.getCurrentRevenue() : BigDecimal.ZERO;
    	// 今日空投奖励（质押账户产生的今日收益）
    	BigDecimal todayAirdropReward = memberTransactionService.sumTodayAmountByMemberIdAndType(authMember.getId(), TransactionType.AIRDROP_REWARD);
    	currentTodayRevenue = currentTodayRevenue.add(todayRevenue).add(tradingAccount.getCurrentRevenue()).add(followRevenue).add(todayAirdropReward);
    	
    	vo.setCashBalance(cashBalance);
    	vo.setCurrentPositionWithCutList(null);
    	vo.setCurrentTodayRevenue(currentTodayRevenue);
		
    	MessageResult<TradingAccount> result = MessageResult.success("success");
        result.setData(vo);
        return result;
	}
	
	@ApiOperation(value="获取交易账户信息(带仓位)", notes="合约界面使用")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "symbol",value = "交易对symbol 当前查看的交易对",required = true,dataType = "string")
    })
	@GetMapping("get-with-positions")
	public MessageResult<TradingAccount> getWithPositions(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, String symbol) {
        TradingAccount tradingAccount = tradingAccountMonitor.get(authMember.getId());
		if(tradingAccount==null) {
			log.info("get account null");
			MessageResult<TradingAccount> result = MessageResult.success("success");
	        result.setData(tradingAccount);
	        return result;
		}
		tradingAccount.genWalletList();
		
		SysConfig sysConfig = sysConfigService.get();
		
		Map<String, BigDecimal> currentPriceMap = tradingAccountMonitor.getPriceMapAtPresent();
		
		TradingAccount vo = new TradingAccount();
		BeanUtils.copyProperties(tradingAccount, vo);
		vo.setCurrentPriceMap(currentPriceMap);
		List<MemberContractWalletVo> walletListVo = new ArrayList<>();
		
		List<MemberContractWalletVo> walletList = tradingAccount.getWalletList();
		for(MemberContractWalletVo wallet: walletList) {
			if(wallet.getContractCoin().getSymbol().equals(symbol) && currentPriceMap.containsKey(symbol)) {
				ContractCoinMatch match = contractCoinMatchFactory.getContractCoinMatch(wallet.getContractCoin().getSymbol());
				if(match != null) {
					List<ContractOrderEntrust> orderList = match.findRunningOpenOrdersByMemberId(authMember.getId());
					BigDecimal frozenMargin = BigDecimal.ZERO;
					for(ContractOrderEntrust order : orderList) {
						frozenMargin = frozenMargin.add(order.getPrincipalAmount().add(order.getOpenFee()));
					}
					wallet.setUsdtFrozenBalance(frozenMargin.setScale(2, BigDecimal.ROUND_DOWN));
					wallet.setUsdtBalance(wallet.getUsdtBalance().setScale(2, BigDecimal.ROUND_DOWN));
					wallet.setUsdtBuyPrincipalAmount(wallet.getUsdtBuyPrincipalAmount().setScale(2, BigDecimal.ROUND_DOWN));
					
					/*Map<Integer, BigDecimal> currentFundingRateMap = fundingRateHistoryService.getCurrentFundingRateMap();
					if(currentFundingRateMap.containsKey(wallet.getContractCoin().getId().intValue()))
						wallet.setCnyRate(currentFundingRateMap.get(wallet.getContractCoin().getId().intValue()));
					else
						wallet.setCnyRate(BigDecimal.ZERO);*/
					
					int volumeScale = String.valueOf(currentPriceMap.get(symbol).divide(wallet.getContractCoin().getShareNumber().multiply(BigDecimal.valueOf(0.1))).intValue()).length();
					wallet.setDecimalScale(volumeScale);
					walletListVo.add(wallet);
				}
			}
		}
		vo.setWalletList(walletListVo);
		
    	List<MemberContractPositionWithCut> positions = tradingAccount.getCurrentPositionWithCutList();
    	List<MemberContractPositionWithCut> positionVoList = new ArrayList<>();
    	if(positions!=null) {

	    	BigDecimal totalPrincipal = BigDecimal.ZERO;
	    	for(MemberContractPositionWithCut pos : positions) {
	    		if(pos.getExperienceRecordId() == null && pos.getPrincipalAmount() != null)
	    			totalPrincipal = totalPrincipal.add(pos.getPrincipalAmount());
	    	}
	    	BigDecimal accountCapital = tradingAccount.getCurrentCapital();
	    	BigDecimal accountMarginRate = BigDecimal.ZERO;
	    	if(accountCapital.compareTo(BigDecimal.ZERO) > 0)
	    		accountMarginRate = totalPrincipal.multiply(BigDecimal.valueOf(100))
	    			.divide(accountCapital, 2, BigDecimal.ROUND_DOWN);

	    	// 按交易对分组计算强平价：同交易对仓位合并计算，不同交易对独立计算
	    	BigDecimal staticBalance = tradingAccount.getStaticBalance();
	    	BigDecimal r = BigDecimal.valueOf(sysConfig.getContractMarginRate() / 100.0);
	    	Map<String, BigDecimal> symbolBlastPriceMap = new java.util.HashMap<>();
	    	// 按交易对分组
	    	Map<String, List<MemberContractPositionWithCut>> positionsBySymbol = new java.util.LinkedHashMap<>();
	    	for (MemberContractPositionWithCut pos : positions) {
	    		if (pos.getExperienceRecordId() != null) { continue; }
	    		String sym = pos.getCoin().getSymbol();
	    		positionsBySymbol.computeIfAbsent(sym, k -> new java.util.ArrayList<>()).add(pos);
	    	}
	    	// 对每个交易对独立计算强平价
	    	for (Map.Entry<String, List<MemberContractPositionWithCut>> entry : positionsBySymbol.entrySet()) {
	    		String sym = entry.getKey();
	    		List<MemberContractPositionWithCut> symPositions = entry.getValue();
	    		BigDecimal buyTotalQty = BigDecimal.ZERO, buyTotalOpenValue = BigDecimal.ZERO, buySumInvL = BigDecimal.ZERO;
	    		BigDecimal sellTotalQty = BigDecimal.ZERO, sellTotalOpenValue = BigDecimal.ZERO, sellSumInvL = BigDecimal.ZERO;
	    		for (MemberContractPositionWithCut pos : symPositions) {
	    			BigDecimal qty = pos.getCurrentPosition();
	    			BigDecimal op  = pos.getOpenPrice();
	    			BigDecimal invL = qty.divide(pos.getLeverage(), 8, RoundingMode.HALF_UP);
	    			if (pos.getDirection() == ContractOrderDirection.BUY) {
	    				buyTotalQty = buyTotalQty.add(qty);
	    				buyTotalOpenValue = buyTotalOpenValue.add(op.multiply(qty));
	    				buySumInvL = buySumInvL.add(invL);
	    			} else {
	    				sellTotalQty = sellTotalQty.add(qty);
	    				sellTotalOpenValue = sellTotalOpenValue.add(op.multiply(qty));
	    				sellSumInvL = sellSumInvL.add(invL);
	    			}
	    		}
	    		BigDecimal netQty = buyTotalQty.subtract(sellTotalQty);
	    		BigDecimal netOpenValue = buyTotalOpenValue.subtract(sellTotalOpenValue);
	    		BigDecimal totalSumInvL = buySumInvL.add(sellSumInvL);
	    		BigDecimal symBlastPrice = BigDecimal.valueOf(-1);
	    		if (netQty.compareTo(BigDecimal.ZERO) > 0) {
	    			BigDecimal denom = netQty.subtract(r.multiply(totalSumInvL));
	    			if (denom.compareTo(BigDecimal.ZERO) > 0) {
	    				BigDecimal px = netOpenValue.subtract(staticBalance).divide(denom, 8, RoundingMode.HALF_UP);
	    				symBlastPrice = px.compareTo(BigDecimal.ZERO) > 0 ? px : BigDecimal.valueOf(-1);
	    			}
	    		} else if (netQty.compareTo(BigDecimal.ZERO) < 0) {
	    			BigDecimal absNetQty = netQty.abs();
	    			BigDecimal absNetOpenValue = netOpenValue.abs();
	    			BigDecimal denom = absNetQty.add(r.multiply(totalSumInvL));
	    			if (denom.compareTo(BigDecimal.ZERO) > 0) {
	    				BigDecimal px = absNetOpenValue.add(staticBalance).divide(denom, 8, RoundingMode.HALF_UP);
	    				symBlastPrice = px.compareTo(BigDecimal.ZERO) > 0 ? px : BigDecimal.valueOf(-1);
	    			}
	    		}
	    		symbolBlastPriceMap.put(sym, symBlastPrice);
	    	}

	    	for(MemberContractPositionWithCut positionWithCut: positions) {
	    		MemberContractPositionWithCut positionVo = new MemberContractPositionWithCut();
	    		BeanUtils.copyProperties(positionWithCut, positionVo);
	    		positionVoList.add(positionVo);
	    		
	    		List<ContractOrderEntrust> cutOrderList = contractCoinMatchFactory.getContractCoinMatch(positionWithCut.getCoin().getSymbol()).findRunningCutOrders(positionWithCut.getId());
	    		positionVo.setCutList(cutOrderList);
	    		/*if(cutOrderList.size() > 0) {
	    			if(cutOrderList.get(0).getVolume().compareTo(BigDecimal.ZERO) == 0)
	    				cutOrderList.get(0).setVolume(positionWithCut.getAvailablePosition());
	    		}*/
				//positionWithCut.setCutList(contractOrderEntrustService.queryEntrustCutOrdersByPositionId(position.getId()));
	    		positionVo.setCurrentPrice(positionWithCut.getCurrentPrice() != null ? positionWithCut.getCurrentPrice().stripTrailingZeros() : BigDecimal.ZERO);
	    		int scale = positionVo.getCoin().getCoinScale();
	    		if (positionVo.getExperienceRecordId() != null) {
	    			BigDecimal qty = positionVo.getCurrentPosition();
	    			BigDecimal op  = positionVo.getOpenPrice();
	    			BigDecimal amtPerUnit = positionVo.getPrincipalAmount().divide(qty, 8, RoundingMode.HALF_UP);
	    			BigDecimal feeAndMaintain = op.multiply(
	    				positionVo.getCoin().getOpenFee()
	    					.add(positionVo.getCoin().getCloseFee())
	    					.add(positionVo.getCoin().getMaintenanceMarginRate()));
	    			BigDecimal egBlastPx = positionVo.getDirection() == ContractOrderDirection.BUY
	    				? op.subtract(amtPerUnit).add(feeAndMaintain)
	    				: op.add(amtPerUnit).subtract(feeAndMaintain);
	    			positionVo.setEstimatedBlastPrice(
	    				egBlastPx.compareTo(BigDecimal.ZERO) > 0
	    					? egBlastPx.setScale(scale, RoundingMode.HALF_UP)
	    					: BigDecimal.valueOf(-1));
	    		} else {
	    			// 取该仓位所属交易对的强平价（同交易对合并计算，不同交易对独立）
	    			BigDecimal symBlastPx = symbolBlastPriceMap.getOrDefault(positionVo.getCoin().getSymbol(), BigDecimal.valueOf(-1));
	    			positionVo.setEstimatedBlastPrice(
	    				symBlastPx.compareTo(BigDecimal.ZERO) > 0
	    					? symBlastPx.setScale(scale, RoundingMode.HALF_UP)
	    					: BigDecimal.valueOf(-1));
	    		}
				positionVo.setMarginRate(positionVo.getExperienceRecordId() != null
					? BigDecimal.valueOf(100)
					: accountMarginRate);
				positionVo.setOpenPrice(positionWithCut.getOpenPrice().setScale(scale, RoundingMode.HALF_UP));
			}
    	}
    	vo.setCurrentPositionWithCutList(positionVoList);
		
    	MessageResult<TradingAccount> result = MessageResult.success("success");
        result.setData(vo);
        return result;
	}
	
	@ApiOperation(value="划转资金", notes="划转资金")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "from",value = "钱包转出类型 0资金账户 1交易账户",required = true,paramType = "form",dataType = "int"),
    		@ApiImplicitParam(name = "to",value = "钱包转入类型 0资金账户 1交易账户",required = true,paramType = "form",dataType = "int"),
    		@ApiImplicitParam(name = "amount",value = "划转金额",required = true,paramType = "form",dataType = "float")
    })
	@PostMapping("transfer")
	public MessageResult transfer(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
			@RequestParam(value = "type") int type,
            @RequestParam(value = "amount") BigDecimal amount) {
		Member member = memberService.findOne(authMember.getId());
		if(member == null) {
            return MessageResult.error(msService.getMessage("REQUEST_OF_ILLEGAL"));
        }
		if(amount.compareTo(BigDecimal.ZERO)<=0)
			return MessageResult.error(msService.getMessage("REQUEST_OF_ILLEGAL"));
        MessageResult result;
        if (type == 0) {// 从 币币钱包 划转到 交易账户
            if (!redisLock.lockTradingAccount(member.getId()))
                return MessageResult.error(msService.getMessage("TRANSFER_FAILED"));
            try {
                result = userPurseService.transferSpotToTrading(member.getId(), amount);
            } finally {
                redisLock.unlockTradingAccount(member.getId());
            }
        } else if (type == 1) {// 从 交易账户 划转到 币币钱包
            if (!redisLock.lockTradingAccount(member.getId()))
                return MessageResult.error(msService.getMessage("TRANSFER_FAILED"));
            try {
                result = userPurseService.transferTradingToSpot(member.getId(), amount);
            } finally {
                redisLock.unlockTradingAccount(member.getId());
            }
        } else {
            return MessageResult.error(msService.getMessage("NO_IN_OUT_WALLET"));
        }
        if (result.getCode() == 0) {
            kafkaTemplate.send("trading-account-change", member.getId() + "");
        }
        return result;
	}

	@ApiOperation(value="修改杠杆", notes="修改杠杆")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "contractId",value = "合约ID",required = true,paramType = "form",dataType = "int"),
    		@ApiImplicitParam(name = "leverage",value = "杠杆值",required = true,paramType = "form",dataType = "int")
    })
	@PostMapping("modifyLeverage")
	public MessageResult modifyLeverage(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long contractId, BigDecimal leverage) {
		if (contractId == null) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
		
		ContractCoin contractCoin = contractCoinService.findOne(contractId);
		if(contractCoin == null) {
			return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
		}
		if(contractCoin.getLeverageType()==1) {
			if((","+contractCoin.getLeverage()+",").indexOf(","+leverage+",") < 0)
				return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
		} else {
			String[] leverageRange = contractCoin.getLeverage().split(",");
			int min = Integer.parseInt(leverageRange[0]);
			int max = Integer.parseInt(leverageRange[leverageRange.length-1]);
			if(leverage.intValue()<min || leverage.intValue()>max) {
				return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
			}
		}
		
		boolean isTrader = memberService.isTrader(authMember.getId());
		if(isTrader) {
			TradingAccount tradingAccount = tradingAccountMonitor.get(authMember.getId());
			List<MemberContractPositionWithCut> positionList = tradingAccount.getCurrentPositionWithCutList();
			for(MemberContractPositionWithCut position: positionList) {
				if(position.getCoin().getId().longValue() == contractId.longValue())
					return MessageResult.error(500, msService.getMessage("LEVERAGE_WITH_POSITION"));
			}
		}
			
		ContractCoinMatch match = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol());
		if(match.findRunningOpenOrdersByMemberId(authMember.getId()).size()>0) {
			return MessageResult.error(500, msService.getMessage("LEVERAGE_WITH_ENTRUST"));
		}
		
		//if(!redisLock.waitTradingAccountLock(authMember.getId()))
    	//	return MessageResult.error(500, msService.getMessage("LEVERAGE_FAILED"));
		Map<String, BigDecimal> priceMap = tradingAccountMonitor.getPriceMapAtPresent();
		try {
			memberContractPositionService.modifyLeverage(authMember.getId(), contractId, leverage, priceMap);
			kafkaTemplate.send("trading-account-change", authMember.getId()+"");
			return MessageResult.success("success");
		} catch (Exception e) {
			return MessageResult.error(500, msService.getMessage("SWAP_BALANCE_NOT_ENOUGH"));
		} finally {
			//redisLock.unlockTradingAccount(authMember.getId());
		}
	}
	
	@ApiOperation(value="获取杠杆", notes="获取杠杆")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "contractId",value = "合约ID",required = true,paramType = "form",dataType = "int")
    })
	@PostMapping("getLeverage")
	@Transactional(rollbackFor = Exception.class)
	public MessageResult getLeverage(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long contractId) {
		if (contractId == null) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
		MemberContractWallet memberContractWallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoinService.findOne(contractId));
		if(memberContractWallet == null)
			return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
		MessageResult result = MessageResult.success("success");
		if(memberContractWallet.getUsdtBuyLeverage()==null)
			result.setData(10);
		else
			result.setData(memberContractWallet.getUsdtBuyLeverage());
        return result;
	}

	@ApiOperation(value="修改仓位类型", notes="合仓和分仓")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "positionType",value = "类型 0合仓 1分仓",required = true,paramType = "form",dataType = "int")
    })
	@PostMapping("modifyType")
	public MessageResult modifyPositionType(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, ContractPositionType positionType) {
		// 带单员不允许切换到分仓模式
		if (positionType == ContractPositionType.SEPARATE) {
			if (memberService.isTrader(authMember.getId())) {
				return MessageResult.error(500, msService.getMessage("ILLEGAL_MODIFY_TYPE_TRADER"));
			}
		}

		userPurseService.modifyPositionType(authMember.getId(), positionType);
		kafkaTemplate.send("trading-account-change", authMember.getId()+"");
		MessageResult result = MessageResult.success("success");
        return result;
	}
	
	/**
	 * 获取资金账户资产列表
	 * <p>
	 * 返回用户 member_wallet 各币种余额及 USDT 估值，币种范围取 config_coin_symbol 中 isOpen=1 的币种。
	 * hideZero=true（默认）时只返回有余额的币种；hideZero=false 时返回所有开启币种，无余额补 0。
	 * </p>
	 *
	 * @param hideZero true=隐藏零余额（默认），false=展示所有开启币种
	 * @param authMember 当前登录用户
	 * @return 资金账户资产列表及 USDT 总估值
	 * @author auto
	 * @date 2026-05-20
	 */
	@ApiOperation(value = "获取资金账户资产列表", notes = "返回各币种余额及 USDT 估值，币种范围取现货开启的 config_coin_symbol")
	@GetMapping("account/asset/list")
	public MessageResult<FundAssetListResp> getAccountAssetList(
			@RequestParam(value = "hideZero", defaultValue = "true") boolean hideZero,
			@SessionAttribute(SESSION_MEMBER) AuthMember authMember) {
		try {
			// 1. 查询资金账户资产列表
			FundAssetListResp resp = fundAssetService.getUserAssetList(authMember.getId(), hideZero);
			MessageResult<FundAssetListResp> result = MessageResult.success("success");
			result.setData(resp);
			return result;
		} catch (Exception e) {
			log.error("[wallet-new/account/asset/list] memberId={} 查询资金账户资产列表失败", authMember.getId(), e);
			return MessageResult.error(500, msService.getMessage("SYSTEM_ERROR"));
		}
	}

	/**
	 * 从 Redis ExchangeNew2:latestPrice 读取指定币种对 USDT 的现货价格
	 * Redis field 格式：全小写拼接，如 ibitusdt
	 *
	 * @param coinUnit 币种，如 IBIT
	 * @return 价格，无行情时返回 null
	 */
	private BigDecimal getRedisSpotPrice(String coinUnit) {
		try {
			String field = coinUnit.toLowerCase() + "usdt";
			Object val = redisTemplate.opsForHash().get("ExchangeNew2:latestPrice", field);
			if (val != null) {
				BigDecimal price = new BigDecimal(val.toString());
				if (price.compareTo(BigDecimal.ZERO) > 0) {
					return price;
				}
			}
		} catch (Exception e) {
			log.warn("[wallet-new/get] Redis 价格读取失败 coinUnit={}, error={}", coinUnit, e.getMessage());
		}
		return null;
	}

	/*@ApiOperation(value="修改交易币种类型", notes="USDT或者币本位")
	@ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "moneyType",value = "类型 0 USDT 1 币本位",required = true,paramType = "form",dataType = "int")
    })
	@PostMapping("modifyMoneyType")
	public MessageResult modifyMoneyType(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, ContractMoneyType moneyType) {
		userPurseService.modifyMoneyType(authMember.getId(), moneyType);
		MessageResult result = MessageResult.success("success");
        return result;
	}*/
}
