package com.spot.service.impl;

import com.spot.dto.mq.AssetChangeInfo;
import com.spot.dto.mq.AssetChangeMessage;
import com.spot.dto.mq.TradeRecord;
import com.spot.dto.mq.TradeResult;
import com.spot.entity.Account;
import com.spot.entity.AccountRecord;
import com.spot.entity.ConfigSymbol;
import com.spot.entity.ExOrder;
import com.spot.entity.ExTrade;
import com.spot.entity.MemberTransaction;
import com.spot.enums.AccountRecordIsChanges;
import com.spot.enums.AccountRecordOperateType;
import com.spot.enums.AccountRecordStatus;
import com.spot.enums.AccountRecordType;
import com.spot.enums.OrderSide;
import com.spot.enums.OrderStatus;
import com.spot.enums.OrderType;
import com.spot.enums.TransactionType;
import com.spot.service.AccountRecordService;
import com.spot.service.AccountService;
import com.spot.service.ConfigSymbolService;
import com.spot.service.ExTradeService;
import com.spot.service.MemberTransactionService;
import com.spot.service.OrderService;
import com.spot.service.TradeResultService;
import com.spot.utils.Constant;
import com.spot.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 撮合结果处理服务实现类
 * 处理撮合引擎推送的三类事件：成交（FILLED）、撤单（CANCELED）、挂单（NEW）
 *
 * @author jean
 * @date 2026-05-06
 */
@Slf4j
@Service
public class TradeResultServiceImpl implements TradeResultService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountRecordService accountRecordService;

    @Autowired
    private ConfigSymbolService configSymbolService;

    @Autowired
    private ExTradeService exTradeService;

    @Autowired
    private MemberTransactionService memberTransactionService;

    // ==================== 成交处理 ====================

    /**
     * 处理成交结果
     * 事务内：更新 taker/maker 订单、更新账户余额、插入成交记录
     *
     * @param tradeResult 交易结果
     * @return 变动信息列表（资产+订单）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AssetChangeInfo> processTradeResult(TradeResult tradeResult) {
        if (tradeResult == null) {
            log.warn("交易结果为空，跳过处理");
            return null;
        }

        ExOrder takerOrder = tradeResult.getTakerOrder();

        try {
            TradeRecord tradeRecord = tradeResult.getTradeRecord();
            if (tradeRecord == null) {
                log.warn("成交记录为空，跳过处理 - MessageId: {}", tradeResult.getMessageId());
                return null;
            }

            // 1. 更新 taker/maker 订单状态
            boolean takerUpdated = false;
            if (takerOrder != null && takerOrder.getId() != null && takerOrder.getId() > 0) {
                takerUpdated = updateOrderFromTradeRecord(takerOrder, tradeRecord, true);
            }
            ExOrder makerOrder = tradeResult.getMakerOrder();
            boolean makerUpdated = false;
            if (makerOrder != null && makerOrder.getId() != null && makerOrder.getId() > 0) {
                makerUpdated = updateOrderFromTradeRecord(makerOrder, tradeRecord, false);
            }

            // 订单均未更新（已处理过或不存在），跳过余额和流水，防止重复处理
            if (!takerUpdated && !makerUpdated) {
                log.warn("订单均已处理，跳过余额更新和流水插入 - MessageId: {}", tradeResult.getMessageId());
                return null;
            }

            // 2. 更新账户余额
            updateAccountBalanceFromTrade(tradeResult, tradeRecord);

            // 3. 插入成交记录
            insertTradeRecord(tradeResult, tradeRecord);

            // 4. 构建变动信息（价格触发成交时 takerOrder 为空，降级使用 makerOrder）
            ExOrder notifyOrder = takerOrder != null ? takerOrder : tradeResult.getMakerOrder();
            return buildAssetChangeInfosForTrade(notifyOrder);

        } catch (Exception e) {
            log.error("处理成交结果失败 - MessageId: {}", tradeResult.getMessageId(), e);
            throw e;
        }
    }

    /**
     * 根据成交记录更新订单信息（通用：taker/maker 共用）
     *
     * @param order       订单（来自 TradeResult，含最新状态）
     * @param tradeRecord 成交记录
     * @param isTaker     true-吃单，false-挂单
     */
    private boolean updateOrderFromTradeRecord(ExOrder order, TradeRecord tradeRecord, boolean isTaker) {
        if (order.getId() == null || order.getId() <= 0) {
            return false;
        }
        try {
            ExOrder dbOrder = orderService.getById(order.getId());
            if (dbOrder == null) {
                log.warn("数据库中未找到订单，跳过更新 - OrderId: {}", order.getId());
                return false;
            }
            // 已终结状态不重复处理
            if (OrderStatus.FILLED.getCode().equals(dbOrder.getStatus()) ||
                    OrderStatus.CANCELED.getCode().equals(dbOrder.getStatus()) ||
                    OrderStatus.PENDING_CANCEL.getCode().equals(dbOrder.getStatus())) {
                return false;
            }

            ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(dbOrder.getSymbol());
            if (configSymbol == null) {
                log.error("未找到交易对配置 - Symbol: {}, OrderId: {}", dbOrder.getSymbol(), order.getId());
                throw new RuntimeException("未找到交易对配置");
            }

            BigDecimal tradeVolume = tradeRecord.getTradeVolume();
            BigDecimal tradeAmount = tradeRecord.getTradeAmount();
            BigDecimal tradePrice = tradeRecord.getTradePrice();

            if (tradeVolume == null || tradeAmount == null || tradePrice == null) {
                log.error("成交记录数据不完整 - OrderId: {}", order.getId());
                throw new RuntimeException("成交记录数据不完整");
            }

            BigDecimal tradeFee = isTaker ? tradeRecord.getTakerFee() : tradeRecord.getMakerFee();
            if (tradeFee == null) {
                tradeFee = BigDecimal.ZERO;
            }

            // 确定手续费币种
            String feeCoin = dbOrder.getFeeCoin();
            if (feeCoin == null || feeCoin.trim().isEmpty()) {
                feeCoin = OrderSide.BUY.getCode().equals(dbOrder.getSide())
                        ? configSymbol.getBase()
                        : configSymbol.getQuote();
            }

            dbOrder.setDealMoney(tradeAmount);
            dbOrder.setDealVolume(tradeVolume);
            dbOrder.setAvgPrice(tradePrice);
            dbOrder.setRemainingVolume(BigDecimal.ZERO);
            dbOrder.setFee(tradeFee);
            dbOrder.setFeeCoin(feeCoin);
            dbOrder.setStatus(order.getStatus());
            dbOrder.setRefundVolume(BigDecimal.ZERO);
            dbOrder.setMtime(TimeUtils.now());

            orderService.updateById(dbOrder);
        } catch (Exception e) {
            log.error("根据成交记录更新订单失败 - OrderId: {}, IsTaker: {}", order.getId(), isTaker, e);
            throw e;
        }
        return true;
    }

    /**
     * 根据成交结果更新 taker/maker 双方账户余额
     *
     * @param tradeResult 交易结果
     * @param tradeRecord 成交记录
     */
    private void updateAccountBalanceFromTrade(TradeResult tradeResult, TradeRecord tradeRecord) {
        if (tradeRecord == null) {
            return;
        }

        ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(tradeRecord.getSymbol());
        if (configSymbol == null) {
            log.error("未找到交易对配置 - Symbol: {}", tradeRecord.getSymbol());
            throw new RuntimeException("未找到交易对配置");
        }

        String baseCurrency = configSymbol.getBase();
        String quoteCurrency = configSymbol.getQuote();

        // 更新 taker 账户
        ExOrder takerOrder = tradeResult.getTakerOrder();
        if (takerOrder != null && takerOrder.getId() != null && takerOrder.getId() > 0
                && takerOrder.getUserId() != null) {
            updateOrderAccountBalance(takerOrder, tradeRecord, baseCurrency, quoteCurrency, true);
        }

        // 更新 maker 账户
        ExOrder makerOrder = tradeResult.getMakerOrder();
        if (makerOrder != null && makerOrder.getId() != null && makerOrder.getId() > 0
                && makerOrder.getUserId() != null) {
            updateOrderAccountBalance(makerOrder, tradeRecord, baseCurrency, quoteCurrency, false);
        }
    }

    /**
     * 更新单个订单的账户余额（买单/卖单分支）
     *
     * @param order         订单
     * @param tradeRecord   成交记录
     * @param baseCurrency  基础币种
     * @param quoteCurrency 计价币种
     * @param isTaker       true-吃单，false-挂单
     */
    private void updateOrderAccountBalance(ExOrder order, TradeRecord tradeRecord,
                                           String baseCurrency, String quoteCurrency, boolean isTaker) {
        if (order.getUserId() == null) {
            return;
        }

        BigDecimal tradeVolume = tradeRecord.getTradeVolume();
        BigDecimal tradeAmount = tradeRecord.getTradeAmount();
        BigDecimal fee = isTaker ? tradeRecord.getTakerFee() : tradeRecord.getMakerFee();
        if (fee == null) {
            fee = BigDecimal.ZERO;
        }

        String feeCoin = order.getFeeCoin();
        if (feeCoin == null || feeCoin.trim().isEmpty()) {
            feeCoin = OrderSide.BUY.getCode().equals(order.getSide()) ? baseCurrency : quoteCurrency;
        }

        BigDecimal refundVolume = order.getRefundVolume();

        try {
            Account baseAccount = accountService.getOrCreateAccount(Long.valueOf(order.getUserId()), baseCurrency);
            Account quoteAccount = accountService.getOrCreateAccount(Long.valueOf(order.getUserId()), quoteCurrency);

            if (OrderSide.BUY.getCode().equals(order.getSide())) {
                // 买单：扣除 quote 冻结，增加 base 可用，扣除手续费
                updateBuyOrderAccountBalance(order, baseAccount, quoteAccount,
                        tradeAmount, tradeVolume, fee, feeCoin, refundVolume, isTaker);
            } else {
                // 卖单：扣除 base 冻结，增加 quote 可用，扣除手续费
                updateSellOrderAccountBalance(order, baseAccount, quoteAccount,
                        tradeVolume, tradeAmount, fee, feeCoin, refundVolume, isTaker);
            }

        } catch (Exception e) {
            log.error("更新订单账户余额失败 - OrderId: {}, IsTaker: {}", order.getId(), isTaker, e);
            throw e;
        }
    }

    /**
     * 更新买单账户余额：扣除 quote 冻结 → 增加 base 可用 → 扣手续费 → 处理退款
     *
     * @param order        订单
     * @param baseAccount  基础币账户
     * @param quoteAccount 计价币账户
     * @param tradeAmount  成交金额（计价币）
     * @param tradeVolume  成交数量（基础币）
     * @param fee          手续费
     * @param feeCoin      手续费币种
     * @param refundVolume 退款金额
     */
    private void updateBuyOrderAccountBalance(ExOrder order, Account baseAccount, Account quoteAccount,
                                              BigDecimal tradeAmount, BigDecimal tradeVolume,
                                              BigDecimal fee, String feeCoin, BigDecimal refundVolume,
                                              boolean isTaker) {
        // 1. 扣除计价币冻结余额
        if (accountService.deductFrozenBalance(quoteAccount.getId(), tradeAmount) <= 0) {
            log.error("扣除计价币冻结余额失败 - AccountId: {}", quoteAccount.getId());
            throw new RuntimeException("扣除计价币冻结余额失败");
        }
        createAccountRecord(order.getUserId().longValue(), quoteAccount.getId(), quoteAccount.getSymbol(),
                tradeAmount, AccountRecordType.TRADE.getCode(), AccountRecordOperateType.FREEZE.getCode(),
                order.getId(), "成交扣除计价币冻结余额", AccountRecordIsChanges.NO.getCode());

        // 2. 增加基础币可用余额
        if (accountService.addAvailableBalance(baseAccount.getId(), tradeVolume) <= 0) {
            log.error("增加基础币可用余额失败 - AccountId: {}", baseAccount.getId());
            throw new RuntimeException("增加基础币可用余额失败");
        }
        createAccountRecord(order.getUserId().longValue(), baseAccount.getId(), baseAccount.getSymbol(),
                tradeVolume, AccountRecordType.TRADE.getCode(), AccountRecordOperateType.ADD.getCode(),
                order.getId(), "成交增加基础币可用余额", AccountRecordIsChanges.YES.getCode());

        // 3. 扣除基础币手续费
        if (fee.compareTo(BigDecimal.ZERO) > 0) {
            if (accountService.addAvailableBalance(baseAccount.getId(), fee.negate()) <= 0) {
                log.error("扣除基础币手续费失败 - AccountId: {}", baseAccount.getId());
                throw new RuntimeException("扣除基础币手续费失败");
            }
            createAccountRecord(order.getUserId().longValue(), baseAccount.getId(), baseAccount.getSymbol(),
                    fee, AccountRecordType.FEE.getCode(), AccountRecordOperateType.SUBTRACT.getCode(),
                    order.getId(), "成交扣除手续费", AccountRecordIsChanges.YES.getCode());
        }

        // 4. 退款：冻结→可用
        if (refundVolume != null && refundVolume.compareTo(BigDecimal.ZERO) > 0) {
            if (accountService.refundFromFrozenToAvailable(quoteAccount.getId(), refundVolume) <= 0) {
                log.error("买单退款失败 - AccountId: {}", quoteAccount.getId());
                throw new RuntimeException("买单退款失败");
            }
            createAccountRecord(order.getUserId().longValue(), quoteAccount.getId(), quoteAccount.getSymbol(),
                    refundVolume, AccountRecordType.UNFREEZE.getCode(), AccountRecordOperateType.UNFREEZE.getCode(),
                    order.getId(), "成交退款", AccountRecordIsChanges.NO.getCode());
        }

        // 5. 插入成交流水到 member_transaction（买方，direction 统一为 0）
        // 5.1 扣除计价币（USDT），负数表示支出
        createMemberTransaction(order.getUserId().longValue(), tradeAmount.negate(),
                quoteAccount.getSymbol(), TransactionType.SPOT_TRADE, BigDecimal.ZERO,
                Constant.TRANSACTION_DIRECTION_IN, order.getSymbol(), isTaker);
        // 5.2 得到基础币（BTC），正数表示收入
        createMemberTransaction(order.getUserId().longValue(), tradeVolume,
                baseAccount.getSymbol(), TransactionType.SPOT_TRADE, BigDecimal.ZERO,
                Constant.TRANSACTION_DIRECTION_IN, order.getSymbol(), isTaker);
        // 5.3 扣除基础币手续费，负数表示支出
        if (fee.compareTo(BigDecimal.ZERO) > 0) {
            createMemberTransaction(order.getUserId().longValue(), fee.negate(),
                    baseAccount.getSymbol(), TransactionType.SPOT_FEE, fee,
                    Constant.TRANSACTION_DIRECTION_IN, order.getSymbol(), isTaker);
        }
    }

    /**
     * 更新卖单账户余额：扣除 base 冻结 → 增加 quote 可用 → 扣手续费 → 处理退款
     *
     * @param order        订单
     * @param baseAccount  基础币账户
     * @param quoteAccount 计价币账户
     * @param tradeVolume  成交数量（基础币）
     * @param tradeAmount  成交金额（计价币）
     * @param fee          手续费
     * @param feeCoin      手续费币种
     * @param refundVolume 退款数量
     */
    private void updateSellOrderAccountBalance(ExOrder order, Account baseAccount, Account quoteAccount,
                                               BigDecimal tradeVolume, BigDecimal tradeAmount,
                                               BigDecimal fee, String feeCoin, BigDecimal refundVolume,
                                               boolean isTaker) {
        // 1. 扣除基础币冻结余额
        if (accountService.deductFrozenBalance(baseAccount.getId(), tradeVolume) <= 0) {
            log.error("扣除基础币冻结余额失败 - AccountId: {}", baseAccount.getId());
            throw new RuntimeException("扣除基础币冻结余额失败");
        }
        createAccountRecord(order.getUserId().longValue(), baseAccount.getId(), baseAccount.getSymbol(),
                tradeVolume, AccountRecordType.TRADE.getCode(), AccountRecordOperateType.FREEZE.getCode(),
                order.getId(), "成交扣除基础币冻结余额", AccountRecordIsChanges.NO.getCode());

        // 2. 增加计价币可用余额
        if (accountService.addAvailableBalance(quoteAccount.getId(), tradeAmount) <= 0) {
            log.error("增加计价币可用余额失败 - AccountId: {}", quoteAccount.getId());
            throw new RuntimeException("增加计价币可用余额失败");
        }
        createAccountRecord(order.getUserId().longValue(), quoteAccount.getId(), quoteAccount.getSymbol(),
                tradeAmount, AccountRecordType.TRADE.getCode(), AccountRecordOperateType.ADD.getCode(),
                order.getId(), "成交增加计价币可用余额", AccountRecordIsChanges.YES.getCode());

        // 3. 扣除计价币手续费
        if (fee.compareTo(BigDecimal.ZERO) > 0) {
            if (accountService.addAvailableBalance(quoteAccount.getId(), fee.negate()) <= 0) {
                log.error("扣除计价币手续费失败 - AccountId: {}", quoteAccount.getId());
                throw new RuntimeException("扣除计价币手续费失败");
            }
            createAccountRecord(order.getUserId().longValue(), quoteAccount.getId(), quoteAccount.getSymbol(),
                    fee, AccountRecordType.FEE.getCode(), AccountRecordOperateType.SUBTRACT.getCode(),
                    order.getId(), "成交扣除手续费", AccountRecordIsChanges.YES.getCode());
        }

        // 4. 退款：冻结→可用
        if (refundVolume != null && refundVolume.compareTo(BigDecimal.ZERO) > 0) {
            if (accountService.refundFromFrozenToAvailable(baseAccount.getId(), refundVolume) <= 0) {
                log.error("卖单退款失败 - AccountId: {}", baseAccount.getId());
                throw new RuntimeException("卖单退款失败");
            }
            createAccountRecord(order.getUserId().longValue(), baseAccount.getId(), baseAccount.getSymbol(),
                    refundVolume, AccountRecordType.UNFREEZE.getCode(), AccountRecordOperateType.UNFREEZE.getCode(),
                    order.getId(), "成交退款", AccountRecordIsChanges.NO.getCode());
        }

        // 5. 插入成交流水到 member_transaction（卖方，direction 统一为 1）
        // 5.1 扣除基础币（BTC），负数表示支出
        createMemberTransaction(order.getUserId().longValue(), tradeVolume.negate(),
                baseAccount.getSymbol(), TransactionType.SPOT_TRADE, BigDecimal.ZERO,
                Constant.TRANSACTION_DIRECTION_OUT, order.getSymbol(), isTaker);
        // 5.2 得到计价币（USDT），正数表示收入
        createMemberTransaction(order.getUserId().longValue(), tradeAmount,
                quoteAccount.getSymbol(), TransactionType.SPOT_TRADE, BigDecimal.ZERO,
                Constant.TRANSACTION_DIRECTION_OUT, order.getSymbol(), isTaker);
        // 5.3 扣除计价币手续费，负数表示支出
        if (fee.compareTo(BigDecimal.ZERO) > 0) {
            createMemberTransaction(order.getUserId().longValue(), fee.negate(),
                    quoteAccount.getSymbol(), TransactionType.SPOT_FEE, fee,
                    Constant.TRANSACTION_DIRECTION_OUT, order.getSymbol(), isTaker);
        }
    }

    /**
     * 插入成交明细记录（ExTrade）
     *
     * @param tradeResult 交易结果
     * @param tradeRecord 成交记录
     */
    private void insertTradeRecord(TradeResult tradeResult, TradeRecord tradeRecord) {
        if (tradeRecord == null) {
            return;
        }

        try {
            ExOrder takerOrder = tradeResult.getTakerOrder();
            ExOrder makerOrder = tradeResult.getMakerOrder();

            boolean isTakerSystem = (takerOrder == null || takerOrder.getId() == null || takerOrder.getId() <= 0);
            boolean isMakerSystem = (makerOrder == null || makerOrder.getId() == null || makerOrder.getId() <= 0);

            ExTrade exTrade = buildExTrade(tradeRecord, takerOrder, makerOrder, isTakerSystem, isMakerSystem);

            if (!exTradeService.save(exTrade)) {
                log.error("保存成交记录失败 - TakerOrderId: {}, MakerOrderId: {}",
                        isTakerSystem ? 0L : (takerOrder != null ? takerOrder.getId() : null),
                        isMakerSystem ? 0L : (makerOrder != null ? makerOrder.getId() : null));
                throw new RuntimeException("保存成交记录失败");
            }

        } catch (Exception e) {
            log.error("插入成交记录失败 - TakerOrderId: {}, MakerOrderId: {}",
                    tradeResult.getTakerOrder() != null ? tradeResult.getTakerOrder().getId() : "null",
                    tradeResult.getMakerOrder() != null ? tradeResult.getMakerOrder().getId() : "null", e);
            throw new RuntimeException("插入成交记录失败", e);
        }
    }

    /**
     * 构建 ExTrade 实体，根据吃单方向确定买卖双方
     *
     * @param tradeRecord   成交记录
     * @param takerOrder    吃单
     * @param makerOrder    挂单
     * @param isTakerSystem 吃单是否为系统单
     * @param isMakerSystem 挂单是否为系统单
     * @return ExTrade
     */
    private ExTrade buildExTrade(TradeRecord tradeRecord, ExOrder takerOrder, ExOrder makerOrder,
                                 boolean isTakerSystem, boolean isMakerSystem) {
        ExTrade exTrade = new ExTrade();
        exTrade.setSymbol(tradeRecord.getSymbol());
        exTrade.setPrice(tradeRecord.getTradePrice());
        exTrade.setVolume(tradeRecord.getTradeVolume());
        exTrade.setTrendSide(tradeRecord.getTakerSide());
        exTrade.setCtime(tradeRecord.getTradeTime());
        exTrade.setMtime(tradeRecord.getTradeTime());

        if (OrderSide.BUY.getCode().equals(tradeRecord.getTakerSide())) {
            // 吃单是买单：taker=买方，maker=卖方
            exTrade.setBidId(isTakerSystem ? 0L : takerOrder.getId());
            exTrade.setAskId(isMakerSystem ? 0L : makerOrder.getId());
            exTrade.setBidUserId(isTakerSystem ? 1 : safeUserId(tradeRecord.getTakerUserId()));
            exTrade.setAskUserId(isMakerSystem ? 1 : safeUserId(tradeRecord.getMakerUserId()));
            exTrade.setBuyFee(isTakerSystem ? BigDecimal.ZERO : safeDecimal(tradeRecord.getTakerFee()));
            exTrade.setSellFee(isMakerSystem ? BigDecimal.ZERO : safeDecimal(tradeRecord.getMakerFee()));
            if (!isTakerSystem && takerOrder.getFeeCoin() != null) {
                exTrade.setBuyFeeCoin(takerOrder.getFeeCoin());
            }
            if (!isMakerSystem && makerOrder.getFeeCoin() != null) {
                exTrade.setSellFeeCoin(makerOrder.getFeeCoin());
            }
        } else {
            // 吃单是卖单：taker=卖方，maker=买方
            exTrade.setBidId(isMakerSystem ? 0L : makerOrder.getId());
            exTrade.setAskId(isTakerSystem ? 0L : takerOrder.getId());
            exTrade.setBidUserId(isMakerSystem ? 1 : safeUserId(tradeRecord.getMakerUserId()));
            exTrade.setAskUserId(isTakerSystem ? 1 : safeUserId(tradeRecord.getTakerUserId()));
            exTrade.setBuyFee(isMakerSystem ? BigDecimal.ZERO : safeDecimal(tradeRecord.getMakerFee()));
            exTrade.setSellFee(isTakerSystem ? BigDecimal.ZERO : safeDecimal(tradeRecord.getTakerFee()));
            if (!isMakerSystem && makerOrder.getFeeCoin() != null) {
                exTrade.setBuyFeeCoin(makerOrder.getFeeCoin());
            }
            if (!isTakerSystem && takerOrder.getFeeCoin() != null) {
                exTrade.setSellFeeCoin(takerOrder.getFeeCoin());
            }
        }

        return exTrade;
    }

    /**
     * 构建成交变动信息（资产变动两个币种 + 订单变动）
     *
     * @param order taker 订单
     * @return 变动信息列表
     */
    private List<AssetChangeInfo> buildAssetChangeInfosForTrade(ExOrder order) {
        if (order == null || order.getUserId() == null || order.getSymbol() == null) {
            return null;
        }

        List<AssetChangeInfo> result = new ArrayList<>();

        ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(order.getSymbol());
        if (configSymbol != null) {
            List<String> assetSymbols = Arrays.asList(configSymbol.getBase(), configSymbol.getQuote());
            result.add(new AssetChangeInfo(AssetChangeMessage.TYPE_ASSET,
                    order.getUserId().longValue(), assetSymbols));
        }

        result.add(new AssetChangeInfo(AssetChangeMessage.TYPE_ORDER,
                order.getUserId().longValue(),
                Collections.singletonList(order.getSymbol())));

        return result;
    }

    /**
     * 创建账户流水记录
     *
     * @param uid         用户ID
     * @param accountId   账户ID
     * @param symbol      币种
     * @param amount      金额
     * @param type        流水类型
     * @param operateType 操作类型
     * @param linkId      关联ID（订单ID）
     * @param remark      备注
     * @param isChanges   是否有总金额变动
     */
    private void createAccountRecord(Long uid, Long accountId, String symbol, BigDecimal amount,
                                     Integer type, Integer operateType, Long linkId, String remark,
                                     Integer isChanges) {
        try {
            AccountRecord record = new AccountRecord();
            record.setUid(uid);
            record.setAccountId(accountId);
            record.setSymbol(symbol);
            record.setAmount(amount);
            record.setSource(Constant.ACCOUNT_RECORD_SOURCE_EX_ORDER);
            record.setLinkId(linkId);
            record.setType(type);
            record.setOperateType(operateType);
            record.setStatus(AccountRecordStatus.COMPLETED.getCode());
            record.setIsChanges(isChanges);
            record.setRemark(remark);
            record.setCtime(TimeUtils.now());
            record.setMtime(TimeUtils.now());

            if (!accountRecordService.save(record)) {
                log.error("保存账户流水失败 - AccountId: {}, Amount: {}, Type: {}", accountId, amount, type);
                throw new RuntimeException("保存账户流水失败");
            }
        } catch (Exception e) {
            log.error("创建账户流水失败 - AccountId: {}, Amount: {}, Type: {}", accountId, amount, type, e);
            throw new RuntimeException("创建账户流水失败", e);
        }
    }

    /**
     * 创建会员交易流水记录（member_transaction）
     *
     * @param memberId        用户ID
     * @param amount          交易金额
     * @param symbol          币种
     * @param transactionType 交易类型
     * @param fee             手续费
     * @param direction       方向：0-买，1-卖
     */
    private void createMemberTransaction(Long memberId, BigDecimal amount, String symbol,
                                         TransactionType transactionType, BigDecimal fee, int direction) {
        createMemberTransaction(memberId, amount, symbol, transactionType, fee, direction, null, false);
    }

    private void createMemberTransaction(Long memberId, BigDecimal amount, String symbol,
                                         TransactionType transactionType, BigDecimal fee, int direction,
                                         String tradePair, boolean isTaker) {
        try {
            MemberTransaction tx = new MemberTransaction();
            tx.setMemberId(memberId);
            tx.setAmount(amount);
            tx.setSymbol(symbol);
            tx.setType(transactionType.getOrdinal());
            tx.setFee(fee != null ? fee : BigDecimal.ZERO);
            tx.setRealFee("0");
            tx.setDiscountFee("0");
            tx.setDirection(direction);
            if (TransactionType.SPOT_FEE.equals(transactionType)) {
                tx.setFlag(isTaker ? 0 : 1);
            } else {
                tx.setFlag(0);
            }
            if (tradePair != null && !tradePair.trim().isEmpty()) {
                tx.setAddress(tradePair.trim().toUpperCase());
            }
            tx.setCreateTime(new Date());

            if (!memberTransactionService.save(tx)) {
                log.error("保存 member_transaction 失败 - MemberId: {}, Type: {}", memberId, transactionType);
                throw new RuntimeException("保存 member_transaction 失败");
            }
        } catch (Exception e) {
            log.error("创建 member_transaction 失败 - MemberId: {}, Type: {}", memberId, transactionType, e);
            throw new RuntimeException("创建 member_transaction 失败", e);
        }
    }

    // ==================== 撤单处理 ====================

    /**
     * 处理撤单结果
     * 校验订单状态为 PENDING_CANCEL → 退还冻结余额 → 更新状态为 CANCELED → 记录解冻流水
     *
     * @param tradeResult 交易结果
     * @return 变动信息列表（资产+订单）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AssetChangeInfo> processCancelResult(TradeResult tradeResult) {
        if (tradeResult == null) {
            log.warn("撤单结果为空，跳过处理");
            return null;
        }

        ExOrder takerOrder = tradeResult.getTakerOrder();
        if (takerOrder == null || takerOrder.getId() == null) {
            log.warn("撤单结果中订单信息为空 - MessageId: {}", tradeResult.getMessageId());
            return null;
        }

        String refundSymbol = null;

        try {
            // 1. 从数据库查询最新状态
            ExOrder dbOrder = orderService.getById(takerOrder.getId());
            if (dbOrder == null) {
                log.error("订单不存在 - OrderId: {}", takerOrder.getId());
                return null;
            }

            // 2. 只处理 PENDING_CANCEL 状态
            if (!OrderStatus.PENDING_CANCEL.getCode().equals(dbOrder.getStatus())) {
                log.warn("订单状态不是 PENDING_CANCEL，跳过 - OrderId: {}, Status: {}",
                        dbOrder.getId(), dbOrder.getStatus());
                return null;
            }

            // 3. 检查剩余数量
            BigDecimal remainingVolume = dbOrder.getRemainingVolume();
            if (remainingVolume == null || remainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
                log.warn("订单剩余数量为0，直接标记撤单 - OrderId: {}", dbOrder.getId());
                dbOrder.setStatus(OrderStatus.CANCELED.getCode());
                dbOrder.setMtime(TimeUtils.now());
                orderService.updateById(dbOrder);
                return null;
            }

            // 4. 获取交易对配置
            ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(dbOrder.getSymbol());
            if (configSymbol == null) {
                log.error("交易对配置不存在 - Symbol: {}", dbOrder.getSymbol());
                return null;
            }

            // 5. 计算退币数量和币种
            BigDecimal refundAmount = calculateRefundAmount(dbOrder, configSymbol, remainingVolume);
            if (refundAmount == null) {
                return null;
            }
            refundSymbol = calculateRefundSymbol(dbOrder, configSymbol);
            if (refundSymbol == null) {
                return null;
            }

            // 6. 查找账户
            Account account = accountService.getOrCreateAccount(dbOrder.getUserId().longValue(), refundSymbol);
            if (account == null) {
                log.error("账户不存在 - Uid: {}, Symbol: {}", dbOrder.getUserId(), refundSymbol);
                return null;
            }

            // 7. 退还冻结余额 → 可用余额
            if (accountService.refundFromFrozenToAvailable(account.getId(), refundAmount) <= 0) {
                log.error("撤单退币失败 - AccountId: {}, RefundAmount: {}", account.getId(), refundAmount);
                throw new RuntimeException("撤单退币失败，冻结余额不足");
            }

            // 8. 更新订单状态为已撤单
            dbOrder.setStatus(OrderStatus.CANCELED.getCode());
            dbOrder.setMtime(TimeUtils.now());
            if (!orderService.updateById(dbOrder)) {
                log.error("更新订单状态失败 - OrderId: {}", dbOrder.getId());
                throw new RuntimeException("更新订单状态失败");
            }

            // 9. 记录解冻流水
            createAccountRecord(dbOrder.getUserId().longValue(), account.getId(), refundSymbol,
                    refundAmount, AccountRecordType.UNFREEZE.getCode(),
                    AccountRecordOperateType.UNFREEZE.getCode(),
                    dbOrder.getId(), "撤单退还冻结金额",
                    AccountRecordIsChanges.NO.getCode());

        } catch (Exception e) {
            log.error("处理撤单结果失败 - MessageId: {}, OrderId: {}",
                    tradeResult.getMessageId(), takerOrder.getId(), e);
            throw e;
        }

        // 构建变动信息
        List<AssetChangeInfo> result = new ArrayList<>();
        if (takerOrder.getUserId() != null) {
            result.add(new AssetChangeInfo(AssetChangeMessage.TYPE_ASSET,
                    takerOrder.getUserId().longValue(),
                    Collections.singletonList(refundSymbol)));
            log.info("撤单通知资产变动{}>>>>>{}",takerOrder.getUserId(), refundSymbol);
        }
        if (takerOrder.getSymbol() != null && takerOrder.getUserId() != null) {
            result.add(new AssetChangeInfo(AssetChangeMessage.TYPE_ORDER,
                    takerOrder.getUserId().longValue(),
                    Collections.singletonList(takerOrder.getSymbol())));
        }

        return result.isEmpty() ? null : result;
    }

    /**
     * 计算退币金额
     */
    private BigDecimal calculateRefundAmount(ExOrder dbOrder, ConfigSymbol configSymbol,
                                             BigDecimal remainingVolume) {
        OrderSide orderSide = OrderSide.fromCode(dbOrder.getSide());
        OrderType orderType = OrderType.fromCode(dbOrder.getType());

        if (orderSide == null) {
            log.error("订单方向无效 - OrderId: {}, Side: {}", dbOrder.getId(), dbOrder.getSide());
            return null;
        }
        if (orderType == null) {
            log.error("订单类型无效 - OrderId: {}, Type: {}", dbOrder.getId(), dbOrder.getType());
            return null;
        }

        if (orderSide.isBuy()) {
            if (orderType.isMarket()) {
                // 市价买单直接退 remaining_volume
                return remainingVolume;
            } else {
                // 限价买单：退 price × remaining_volume
                if (dbOrder.getPrice() == null) {
                    log.error("限价买单价格为空 - OrderId: {}", dbOrder.getId());
                    return null;
                }
                return dbOrder.getPrice().multiply(remainingVolume);
            }
        } else {
            // 卖单直接退 remaining_volume
            return remainingVolume;
        }
    }

    /**
     * 计算退币币种
     */
    private String calculateRefundSymbol(ExOrder dbOrder, ConfigSymbol configSymbol) {
        OrderSide orderSide = OrderSide.fromCode(dbOrder.getSide());
        if (orderSide == null) {
            return null;
        }
        return orderSide.isBuy() ? configSymbol.getQuote() : configSymbol.getBase();
    }

    // ==================== 挂单处理 ====================

    /**
     * 处理挂单结果（撮合引擎返回 NEW 状态）
     * 仅更新订单状态为 NEW
     *
     * @param tradeResult 交易结果
     * @return null（挂单不产生变动通知）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AssetChangeInfo> processPendingOrder(TradeResult tradeResult) {
        if (tradeResult == null) {
            log.warn("挂单结果为空，跳过处理");
            return null;
        }

        try {
            ExOrder takerOrder = tradeResult.getTakerOrder();
            if (takerOrder == null || takerOrder.getId() == null) {
                log.warn("挂单结果中订单信息为空 - MessageId: {}", tradeResult.getMessageId());
                return null;
            }

            // 更新订单状态为 NEW
            takerOrder.setStatus(OrderStatus.NEW.getCode());
            takerOrder.setMtime(TimeUtils.now());
            if (!orderService.updateById(takerOrder)) {
                log.warn("订单状态更新失败（挂单）- OrderId: {}", takerOrder.getId());
            }

        } catch (Exception e) {
            log.error("处理挂单结果失败 - MessageId: {}", tradeResult.getMessageId(), e);
            throw e;
        }

        return null;
    }

    // ==================== 工具方法 ====================

    /**
     * userId 安全转换，null 时返回系统用户ID
     */
    private int safeUserId(Integer userId) {
        return userId != null ? userId : Constant.SYSTEM_USER_ID;
    }

    /**
     * BigDecimal 安全转换，null 时返回 ZERO
     */
    private BigDecimal safeDecimal(BigDecimal value) {
        return value != null ? value : BigDecimal.ZERO;
    }
}
