package com.bizzan.bitrade.consumer;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.constant.NotifyType;
import com.bizzan.bitrade.engine.ContractCoinMatch;
import com.bizzan.bitrade.engine.ContractCoinMatchFactory;
import com.bizzan.bitrade.engine.TradingAccountMonitor;
import com.bizzan.bitrade.entity.CloseType;
import com.bizzan.bitrade.entity.ContractCoin;
import com.bizzan.bitrade.entity.ContractOrderDirection;
import com.bizzan.bitrade.entity.ContractOrderEntrust;
import com.bizzan.bitrade.entity.ContractOrderEntrustStatus;
import com.bizzan.bitrade.entity.ContractOrderEntrustType;
import com.bizzan.bitrade.entity.ContractOrderType;
import com.bizzan.bitrade.entity.MemberContractPosition;
import com.bizzan.bitrade.entity.MemberContractWallet;
import com.bizzan.bitrade.handler.NettyHandler;
import com.bizzan.bitrade.handler.WebsocketMarketHandler;
import com.bizzan.bitrade.job.ExchangePushJob;
import com.bizzan.bitrade.job.KLineSyncJob;
import com.bizzan.bitrade.entity.ContractMoneyType;
import com.bizzan.bitrade.entity.ContractPositionStatus;
import com.bizzan.bitrade.entity.ExperienceGiftCloseReason;
import com.bizzan.bitrade.service.*;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.util.RedisManager;
import com.bizzan.bitrade.util.WebSocketConnectionManage;
import com.bizzan.bitrade.vo.MemberContractPositionWithCut;
import com.bizzan.bitrade.vo.TradingAccount;

import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.shiro.util.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.List;

import javax.persistence.EntityManager;

@Slf4j
@Component
public class MatchConsumer {
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    @Autowired
    private ContractCoinMatchFactory contractCoinMatchFactory; // 合约引擎工厂

    @Autowired
    private ContractCoinService contractCoinService;

    @Autowired
    private ContractMarketService marketService;

    @Autowired
    private ExchangePushJob exchangePushJob;

    @Autowired
    private ContractOrderEntrustService contractOrderEntrustService;

    @Autowired
    private MemberTransactionService memberTransactionService;
    @Autowired
    private MemberContractWalletService memberContractWalletService;


    @Autowired
    WebsocketMarketHandler wsHandler;

    @Autowired
    NettyHandler nettyHandler;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private RedisManager redisManager;
    @Autowired
    private TradingAccountMonitor tradingAccountMonitor;
    @Autowired
    private EntityManager entityManager;
    @Autowired
    private MemberContractPositionService memberContractPositionService;
    @Autowired
    private UserPurseService userPurseService;
    @Autowired
    private FollowWalletService followWalletService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private ExperienceGiftRecordService experienceGiftRecordService;


    /**
     * 开仓
     * @param records
     */
    @KafkaListener(topics = "swap-order-open",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderOpenSubmitted(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【开仓订单】接收订单>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("开仓订单转化失败");
                continue;
            }
            if(!redisLock.lockTradingAccount(order.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-open", JSON.toJSONString(order));
            	continue;
            }
            try {
	            // 加入处理引擎
	            contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).trade(order);
            } catch (Exception e) {
	        	log.info("onOrderOpenSubmitted error", e);
	        } finally {
	            redisLock.unlockTradingAccount(order.getMemberId());
	        }
            
        }
    }

    /**
     * 平仓
     * @param records
     */
    @KafkaListener(topics = "swap-order-close",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderCloseSubmitted(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【平仓订单】接收订单>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("平仓订单转化失败");
                continue;
            }
            if(!redisLock.lockTradingAccount(order.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-close", JSON.toJSONString(order));
            	continue;
            }
            try {
	            // 加入处理引擎
	            //contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).trade(order);
            	
    	        int successCount = memberContractPositionService.freezePosition(order.getPositionId(), order.getVolume());
    	        if(successCount == 1) {
    	        	// 保存委托单
    		        order.setStatus(ContractOrderEntrustStatus.ENTRUST_ING); // 委托状态：委托中
    		        order = contractOrderEntrustService.save(order);
    	        } else {
    	        	order.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE); // 委托状态：委托中
    		        contractOrderEntrustService.save(order);
    		        continue;
    	        }
    	
    	        contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).trade(order);
    	            
	        } catch (Exception e) {
	        	log.info("onOrderCloseSubmitted error", e);
	        } finally {
	            redisLock.unlockTradingAccount(order.getMemberId());
	        }
        }
    }
    
    /**
     * 一键平仓
     * @param records
     */
    @KafkaListener(topics = "swap-order-close-all",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderCloseAllSubmitted(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【一键平仓钱包】接收钱包>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            MemberContractPosition position = JSON.parseObject(record.value(), MemberContractPosition.class);
            
            if(position.getCloseType() == CloseType.FOLLOW_SELF_CLOSE
            		|| position.getCloseType() == CloseType.FOLLOW_POSITION_LOSS
            		|| position.getCloseType() == CloseType.FOLLOW_POSITION_PROFIT
            		|| position.getCloseType() == CloseType.FOLLOW_ACCOUNT_LOSS
            		|| position.getCloseType() == CloseType.FOLLOW_ACCOUNT_PROFIT) {
	            if(!redisLock.lockFollowAccount(position.getMemberId())) {
	            	try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	            	kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
	            	continue;
	            }
	            try {
	            // 加入处理引擎
	            	contractCoinMatchFactory.getContractCoinMatch(position.getCoin().getSymbol()).dealAllCloseOrder(position);
	            } catch (Exception e) {
	            	log.info("onOrderCloseAllSubmitted error", e);
	            } finally {
	                redisLock.unlockFollowAccount(position.getMemberId());
	            }
            } else {
            	if(!redisLock.lockTradingAccount(position.getMemberId())) {
	            	try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	            	kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
	            	continue;
	            }
	            try {
	            // 加入处理引擎
	            	contractCoinMatchFactory.getContractCoinMatch(position.getCoin().getSymbol()).dealAllCloseOrder(position);
	            } catch (Exception e) {
	            	log.info("onOrderCloseAllSubmitted error", e);
	            } finally {
	                redisLock.unlockTradingAccount(position.getMemberId());
	            }
            }
            
        }
    }
    
    /**
     * 一键全平
     * @param records
     */
    @KafkaListener(topics = "swap-order-close-all-coins",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderCloseAllCoinsSubmitted(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【一键平仓钱包】接收钱包>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            JSONObject jsonObject = JSON.parseObject(record.value());
            Long memberId = jsonObject.getLong("memberId");
            
            if(!redisLock.lockTradingAccount(memberId)) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-close-all-coins", JSON.toJSONString(jsonObject));
            	continue;
            }
            try {
	            List<MemberContractPosition> list = memberContractPositionService.findCurrentPositionsByMemberId(memberId);
	            log.info("list.size()="+list.size());
	            for(MemberContractPosition position : list) {
	    	        if(position.getIsLock()!=null && position.getIsLock() == 1) {
	    	        	continue;
	    	        }
	    	        
	            	ContractCoin contractCoin = position.getCoin();
	            	
	            	if(contractCoin == null || contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol())==null) {
	            		log.info("contractCoin={}", contractCoin);
	            		continue;
	            	}
	            	BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
	                if (currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
	                	continue;
	                }
	    	        
	                position.setCloseType(CloseType.ONE_KEY_CLOSE);
	                contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).dealAllCloseOrder(position);
	                //kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
	            }
            } catch (Exception e) {
            	log.info("onOrderCloseAllCoinsSubmitted error", e);
            } finally {
                redisLock.unlockTradingAccount(memberId);
            }
        }
    }

    /**
     * 撤销委托
     * @param records
     */
    @KafkaListener(topics = "swap-order-cancel",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderEntrustCancel(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【撤销订单】接收订单>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("取消订单转化失败");
                continue;
            }
            // 加入处理引擎
            contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).cancelContractOrderEntrust(order, false);
        }
    }
    
    @KafkaListener(topics = "swap-order-entrust-deal",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderEntrustDeal(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("订单转化失败");
                continue;
            }
            if(!redisLock.lockTradingAccount(order.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-entrust-deal", JSON.toJSONString(order));
            	continue;
            }
            try {
            	order.setTradedPrice(order.getEntrustPrice());
            	if(order.getEntrustType() == ContractOrderEntrustType.OPEN)
            		contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).dealOpenOrder(order);
            	else
            		contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).dealCloseOrder(order);
            } catch (Exception e) {
            	log.error("dealOrder error", e);
            } finally {
                redisLock.unlockTradingAccount(order.getMemberId());
            }
            log.info("触发成交委托订单完成");
        }
    }
    
    @KafkaListener(topics = "swap-order-spot-entrust-open",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderSpotEntrustOpen(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("计划开仓订单转化失败");
                continue;
            }
            if(!redisLock.lockTradingAccount(order.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-spot-entrust-open", JSON.toJSONString(order));
            	continue;
            }
        	
        	try {
                if(order.getEntrustPrice().compareTo(BigDecimal.ZERO) == 0) { // 市价委托
                    // 更改委托单类型
                    order.setType(ContractOrderType.MARKET_PRICE);
                }else{ // 限价委托
                    // 更改委托单类型
                    order.setType(ContractOrderType.LIMIT_PRICE);
                }
                int success = tradingAccountMonitor.genVolumeForOrder(order, true);
            	if(success == 1) {
            		order.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE);
            		order.setType(ContractOrderType.SPOT_LIMIT);
            		contractOrderEntrustService.save(order);
            		//orderIterator.remove();
            		continue;
            	}
                
                contractOrderEntrustService.save(order);
                //userPurseService.freezeMarginAndFee(order.getMemberId(), order.getPrincipalAmount().add(order.getOpenFee()));
                //memberContractWalletService.freezeUsdtBalance(wallet, principalAmount.add(openFee));
                contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).trade(order);
                //orderIterator.remove();
        	}catch (Exception e) {
            	log.error("[计划委托开仓]dealOpenOrder error", e);
            } finally {
                redisLock.unlockTradingAccount(order.getMemberId());
            }
        }
    }
    
    @KafkaListener(topics = "swap-order-spot-entrust-close",containerFactory = "kafkaListenerContainerFactory")
    public void onOrderSpotEntrustClose(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            ContractOrderEntrust order = JSON.parseObject(record.value(), ContractOrderEntrust.class);
            if (order == null) {
                log.info("计划平仓订单转化失败");
                continue;
            }
            if(!redisLock.lockTradingAccount(order.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-order-spot-entrust-close", JSON.toJSONString(order));
            	continue;
            }
        	
            try {
            	ContractCoinMatch contractCoinMatch = contractCoinMatchFactory.getContractCoinMatch(order.getSymbol());
                if(order.getVolume().compareTo(BigDecimal.ZERO) == 0) {
                	contractCoinMatch.cancelCloseOrdersByPositionId(order.getPositionId());
                    //BigDecimal volume = position.getAvailablePosition().add(releasedPosition);
                    MemberContractPosition position = memberContractPositionService.findOne(order.getPositionId());
                    log.info("availablePosition={}", position.getAvailablePosition());
                    BigDecimal volume = position.getAvailablePosition();
                    if(volume.compareTo(BigDecimal.ZERO) == 0) {
                    	//orderIterator.remove();
                    	order.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE);
                    	order.setDealTime(System.currentTimeMillis());
                    	contractOrderEntrustService.save(order);
                    	continue;
                    }
                    int successCount = memberContractPositionService.freezePosition(order.getPositionId(), volume);
                    if(successCount == 0) {
                    	log.info("冻结失败, position={}", order.getPositionId());
                    	order.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE);
                    	order.setDealTime(System.currentTimeMillis());
                    	contractOrderEntrustService.save(order);
                    	//redisLock.unlockPosition(order.getPositionId());
                    	continue;
                    }
                    order.setVolume(volume);
                } else {
                	int successCount = memberContractPositionService.freezePosition(order.getPositionId(), order.getVolume());
                    if(successCount == 0) {
                    	log.info("冻结失败, volume={}, position={}", order.getVolume(), order.getPositionId());
                    	//redisLock.unlockPosition(order.getPositionId());
                    	//orderIterator.remove();
                    	order.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE);
                    	order.setDealTime(System.currentTimeMillis());
                    	contractOrderEntrustService.save(order);
                    	continue;
                    }
                }
                boolean profitTriggered = order.getCutType() == 1;
                boolean lossTriggered = !profitTriggered;
                BigDecimal newPrice = order.getTriggerPrice();
                
                // 止盈止损触发后，统一以市价平仓
                order.setType(ContractOrderType.MARKET_PRICE);
                order.setTradedPrice(newPrice);
                
                //删除止盈止损价格，避免递归下止盈止损单
                //order.setLossPrice(BigDecimal.ZERO);
                //order.setProfitPrice(BigDecimal.ZERO);

                //计算平仓应该扣除多少保证金(平仓量/（可平仓量+冻结平仓量） *  保证金总量）
                /*if (order.getDirection() == ContractOrderDirection.BUY) { // 买入平空
                	BigDecimal totalPosition = wallet.getUsdtSellPosition().add(wallet.getUsdtFrozenSellPosition());
                	if(totalPosition.compareTo(BigDecimal.ZERO)<=0)
                		continue;
                    BigDecimal mPrinc = order.getVolume().divide(totalPosition, 8, RoundingMode.HALF_UP).multiply(wallet.getUsdtSellPrincipalAmount());
                    order.setPrincipalAmount(mPrinc);
                } else {
                	BigDecimal totalPosition = wallet.getUsdtBuyPosition().add(wallet.getUsdtFrozenBuyPosition());
                	if(totalPosition.compareTo(BigDecimal.ZERO)<=0)
                		continue;
                    BigDecimal mPrinc = order.getVolume().divide(totalPosition, 8, RoundingMode.HALF_UP).multiply(wallet.getUsdtBuyPrincipalAmount());
                    order.setPrincipalAmount(mPrinc);
                }*/
                contractOrderEntrustService.save(order);
                
                //发送通知邮件
                net.sf.json.JSONObject jsonObject = net.sf.json.JSONObject.fromObject(order);
                jsonObject.put("newPrice", newPrice);
                if(profitTriggered)
                	memberService.notifyByEmail(order.getMemberId(), NotifyType.CUT_PROFIT, jsonObject);
                else
                	memberService.notifyByEmail(order.getMemberId(), NotifyType.CUT_LOSS, jsonObject);
                
                contractCoinMatch.trade(order);
                //orderIterator.remove();
            }catch (Exception e) {
            	log.error("[止盈止损]dealOpenOrder error", e);
            } finally {
                redisLock.unlockTradingAccount(order.getMemberId());
            }
        }
    }
    
    @KafkaListener(topics = "swap-account-blast",containerFactory = "kafkaListenerContainerFactory")
    public void onAccountBlast(List<ConsumerRecord<String, String>> records){
    	int marginRate = sysConfigService.get().getContractMarginRate();
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            TradingAccount account = JSON.parseObject(record.value(), TradingAccount.class);
            if (account == null) {
                log.info("合约账户转化失败");
                continue;
            }
            
            if(!redisLock.lockTradingAccount(account.getId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-account-blast", JSON.toJSONString(account));
            	continue;
            }
			
			try {
				//刷新数据
				TradingAccount refreshedTradingAccount = userPurseService.findOneVO(account.getId());
				refreshedTradingAccount.setCurrentPriceMap(account.getcurrentPriceMap());
				
				//刷新数据后再次检查
				BigDecimal currentCapital = refreshedTradingAccount.getCurrentCapital();
				double currentMarginRate = currentCapital.divide(refreshedTradingAccount.getCurrentMargin(), 4, BigDecimal.ROUND_DOWN).doubleValue()*100;
				log.info("currentMarginRate={}", currentMarginRate);
				if(currentMarginRate>=marginRate) {
					kafkaTemplate.send("trading-account-change",account.getId()+"");
					continue;
				}
				
				log.info("blasted memberID={} currentCapital={} currentMarginRate={}",
						refreshedTradingAccount.getId(), currentCapital, currentMarginRate);
				tradingAccountMonitor.cancelOpenOrders(refreshedTradingAccount.getId());
				BigDecimal[] marketValueArray = refreshedTradingAccount.getMaketValue();
				BigDecimal totalMarketValue = marketValueArray[0].add(marketValueArray[1]);
				if(totalMarketValue.compareTo(BigDecimal.ZERO) == 0) {
					log.warn("blasted memberID={} totalMarketValue=0，重新加入爆仓检查 map", refreshedTradingAccount.getId());
					// 市值为0通常是价格尚未就绪，重新加入 map 等下次检查
					kafkaTemplate.send("trading-account-change", account.getId()+"");
					continue;
				}
				// 体验金仓位由 checkExperienceGiftPositions 独立处理，爆仓时只强制平普通仓位
				List<MemberContractPositionWithCut> nonTrialPositionList = refreshedTradingAccount.getCurrentPositionWithCutList()
						.stream()
						.filter(p -> p.getExperienceRecordId() == null)
						.collect(java.util.stream.Collectors.toList());
				log.info("blasted memberID={} 需平仓数量={}", refreshedTradingAccount.getId(), nonTrialPositionList.size());
				int positionIndex = 0;
				boolean anyBlastFailed = false;
				for(MemberContractPosition position: nonTrialPositionList) {
					position.setCloseType(CloseType.BLAST);
					//position.setBlastedAmount(tradingAccount.getStaticBalance());
					positionIndex += 1;
					if(positionIndex == nonTrialPositionList.size()) {
						BigDecimal lastBalance = userPurseService.getBalanceById(refreshedTradingAccount.getId());
						position.setBlastedAmount(BigDecimal.ZERO.subtract(lastBalance));
					} else {
						position.setBlastedAmount(position.getCurrentProfit().subtract(position.getCurrentMarketValue().multiply(currentCapital).divide(totalMarketValue,8,BigDecimal.ROUND_DOWN)));
					}
					log.info("blasted positionId={} amount={} symbol={}", position.getId(), position.getBlastedAmount(), position.getCoin()!=null?position.getCoin().getSymbol():"null");
					ContractCoinMatch match = contractCoinMatchFactory.getContractCoinMatch(position.getCoin().getSymbol());
					if (match == null) {
						log.error("blasted 撮合引擎未找到 symbol={} positionId={}", position.getCoin().getSymbol(), position.getId());
						anyBlastFailed = true;
						continue;
					}
					BigDecimal nowPrice = match.getNowPrice();
					log.info("blasted positionId={} symbol={} nowPrice={}", position.getId(), position.getCoin().getSymbol(), nowPrice);
					if (nowPrice == null || nowPrice.compareTo(BigDecimal.ZERO) <= 0) {
						log.warn("blasted 引擎价格未就绪 positionId={} symbol={}，本次跳过将重试", position.getId(), position.getCoin().getSymbol());
						anyBlastFailed = true;
						continue;
					}
					match.dealAllCloseOrder(position);
					//kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
				}
				if (anyBlastFailed) {
					log.warn("blasted memberID={} 部分仓位未成功平仓（价格未就绪），60秒后重新加入爆仓检查", refreshedTradingAccount.getId());
					// 延迟重试：重新把账户加入 tradingAccountMap，等下一个 checkBlast 周期
					kafkaTemplate.send("trading-account-change", account.getId()+"");
				}
				//BigDecimal blastedLoss = contractOrderEntrustService.sumBlastedLossByPositionIds(positionIds);
				//userPurseService.zeroBalance(refreshedTradingAccount.getId());
				tradingAccountMonitor.sendBlastEmail(refreshedTradingAccount);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			} finally {
				redisLock.unlockTradingAccount(account.getId());
			}
        	
        }
    }
    
    @KafkaListener(topics = "swap-position-blast",containerFactory = "kafkaListenerContainerFactory")
    public void onPositionBlast(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            MemberContractPosition position = JSON.parseObject(record.value(), MemberContractPosition.class);
            if (position == null) {
                log.info("计划平仓订单转化失败");
                continue;
            }
            
            if(!redisLock.lockTradingAccount(position.getMemberId())) {
            	try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            	kafkaTemplate.send("swap-position-blast", JSON.toJSONString(position));
            	continue;
            }
			
			try {
				position.setCloseType(CloseType.BLAST);
				position.setBlastedAmount(BigDecimal.ZERO.subtract(position.getPrincipalAmount()));
				log.info("blasted positionid={}, amount={}", position.getId(), position.getBlastedAmount());
				contractCoinMatchFactory.getContractCoinMatch(position.getCoin().getSymbol()).dealAllCloseOrder(position);
				tradingAccountMonitor.sendPositionBlastEmail(position);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			} finally {
				redisLock.unlockTradingAccount(position.getMemberId());
			}
        	
        }
    }
    
    /**
     * 体验金仓位平仓处理（盈利触顶 / 爆仓）
     * Topic: swap-experience-gift-close
     * 触发来源：ExperienceGiftCheckJob.checkExperienceGiftPositions
     */
    @KafkaListener(topics = "swap-experience-gift-close", containerFactory = "kafkaListenerContainerFactory")
    public void onExperienceGiftClose(List<ConsumerRecord<String, String>> records) {
        for (ConsumerRecord<String, String> rec : records) {
            ExperienceGiftCloseMsg msg = JSON.parseObject(rec.value(), ExperienceGiftCloseMsg.class);
            if (msg == null) {
                log.warn("体验金平仓消息解析失败: {}", rec.value());
                continue;
            }
            if (!redisLock.lockExperienceGiftRecord(msg.getRecordId())) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                kafkaTemplate.send("swap-experience-gift-close", rec.value());
                continue;
            }
            try {
                // 1. 按平仓原因确定 CloseType
                ExperienceGiftCloseReason closeReason = msg.getCloseReason();
                CloseType closeType;
                if (closeReason == ExperienceGiftCloseReason.BLASTED) {
                    closeType = CloseType.BLAST;
                } else if (closeReason == ExperienceGiftCloseReason.PROFIT_LIMIT
                        || closeReason == ExperienceGiftCloseReason.STOP_PROFIT
                        || closeReason == ExperienceGiftCloseReason.STOP_LOSS) {
                    closeType = CloseType.CUT;
                } else {
                    // 兜底：未知原因按用户手动平仓处理
                    closeType = CloseType.CLOSE;
                }

                // 2. pnl 由 Job 计算后传入，正常不为 null；保留兜底防御
                BigDecimal pnl = msg.getPnl();
                if (pnl == null) {
                    log.warn("体验金平仓 pnl 为 null，Job 未正常计算 recordId={}", msg.getRecordId());
                    continue;
                }

                // 3. 平仓前先查出待处理的止盈止损切单（状态为 ENTRUST_ING）
                // 必须在调用 closeExperienceGiftPosition 之前查询：
                // 服务内部会在同一事务中将切单状态更新（止盈止损→ENTRUST_SUCCESS，其他→ENTRUST_CANCEL），
                // 之后再查则返回空，导致无法从内存 Map 中移除
                List<ContractOrderEntrust> cutOrders = contractOrderEntrustService.queryEntrustCutOrdersByPositionId(msg.getPositionId());
                ContractCoinMatch coinMatch = contractCoinMatchFactory.getContractCoinMatch(msg.getCoinSymbol());

                // 4. 直接关闭仓位（同事务内完成：生成委托记录、更新仓位、更新切单状态、盈利入资金池、更新券状态）
                boolean positionClosed = memberContractPositionService.closeExperienceGiftPosition(
                        msg.getPositionId(), msg.getClosePrice(), closeType, pnl, msg.getRecordId(), closeReason);
                if (!positionClosed) {
                    log.warn("体验金平仓：仓位已关闭或不符合条件 positionId={}", msg.getPositionId());
                }

                // 5. 从引擎内存 Map 中移除切单（DB 更新已在服务事务内完成，此处仅处理内存状态）
                if (coinMatch != null) {
                    for (ContractOrderEntrust cutOrder : cutOrders) {
                        coinMatch.removeFromMap(cutOrder);
                    }
                }

                log.info("体验金平仓完成 recordId={}, positionId={}, reason={}, price={}, pnl={}",
                        msg.getRecordId(), msg.getPositionId(), msg.getCloseReason(), msg.getClosePrice(), msg.getPnl());
            } catch (Exception e) {
                log.error("体验金平仓异常 recordId={}: {}", msg.getRecordId(), e.getMessage(), e);
            } finally {
                redisLock.unlockExperienceGiftRecord(msg.getRecordId());
                // 事务已提交，此处发 Kafka 消费者读到的是已提交数据
                kafkaTemplate.send("trading-account-change", msg.getMemberId() + "");
                tradingAccountMonitor.updateAccount(msg.getMemberId());
            }
        }
    }

    @KafkaListener(topics = "after-swap-order-done",containerFactory = "kafkaListenerContainerFactory")
    public void onAfterOrderDone(List<ConsumerRecord<String, String>> records){
        for (int i = 0; i < records.size(); i++) {
            ConsumerRecord<String, String> record = records.get(i);
            log.info("【撤销订单】接收订单>>topic={},value={},size={}", record.topic(), record.value(), records.size());
            JSONObject json = JSON.parseObject(record.value());
            if (json == null) {
                log.info("取消订单转化失败");
                return;
            }
        }
    }

    /**
     * 更新合约交易对信息
     * @param content
     */
    @KafkaListener(topics = {"update-contract-coin"})
    public void onUpdateContractCoin(String content) {
        if (StringUtils.isEmpty(content)) {
            return;
        }
        JSONObject json = JSON.parseObject(content);
        if (json == null) {
            return;
        }
        Long contractCoinId = json.getLong("cid");

        ContractCoin coin = contractCoinService.findOne(contractCoinId);
        if(coin != null) {
            contractCoinMatchFactory.getContractCoinMatch(coin.getSymbol()).updateContractCoin(coin);
        }
    }

    /**
     * 新增合约交易对
     * @param content
     */
    @KafkaListener(topics = {"add-contract-coin"})
    public void onAddContractCoin(String content) {
        if (StringUtils.isEmpty(content)) {
            return;
        }
        JSONObject json = JSON.parseObject(content);
        if (json == null) {
            return;
        }
        String symbol = json.getString("symbol");

        if(symbol != null && !contractCoinMatchFactory.containsContractCoinMatch(symbol)) {
            // 添加引擎
            ContractCoinMatch match = new ContractCoinMatch(symbol);
            match.setContractCoinService(contractCoinService);
            match.setContractOrderEntrustService(contractOrderEntrustService);
            match.setMemberTransactionService(memberTransactionService);
            match.setMemberService(memberService);
            match.setMemberContractWalletService(memberContractWalletService);
            match.setMemberContractPositionService(memberContractPositionService);
            match.setUserPurseService(userPurseService);
            match.setFollowWalletService(followWalletService);
            match.setEntityManager(entityManager);
            match.addHandler(wsHandler);
            match.addHandler(nettyHandler);
            match.setExchangePushJob(exchangePushJob);
            match.setRedisLock(redisLock);
            match.setRedisManager(redisManager);
            match.setTradingAccountMonitor(tradingAccountMonitor);
            match.setKafkaTemplate(kafkaTemplate);
            match.setExperienceGiftRecordService(experienceGiftRecordService);
            match.run();
            contractCoinMatchFactory.addContractCoinMatch(symbol, match);

        }
    }
    
    @KafkaListener(topics = {"remove-contract-coin"})
    public void onRemoveContractCoin(String content) {
        if (StringUtils.isEmpty(content)) {
            return;
        }
        JSONObject json = JSON.parseObject(content);
        if (json == null) {
            return;
        }
        String symbol = json.getString("symbol");

        if(symbol != null && !contractCoinMatchFactory.containsContractCoinMatch(symbol)) {
            // 添加引擎
        	ContractCoinMatch match = contractCoinMatchFactory.getContractCoinMatch(symbol);
        	match.stop();
            contractCoinMatchFactory.removeContractCoinMatch(symbol);

        }
    }

    /**
     * 定向爆仓。。。大坏蛋
     * 提供一个交易对符号，提供一个价格
     * @param content
     */
    @KafkaListener(topics = {"swap-blast"})
    public void triggerPrice(String content) {
        if (StringUtils.isEmpty(content)) {
            return;
        }
        JSONObject json = JSON.parseObject(content);
        if (json == null) {
            return;
        }
        String symbol = json.getString("symbol");
        BigDecimal price = json.getBigDecimal("price");
        contractCoinMatchFactory.getContractCoinMatch(symbol).refreshBlastPrice(price);
    }

}
