import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:dio/dio.dart' show DioException, DioExceptionType;

import '../core/network/api_response.dart';
import '../core/network/dio_client.dart';
import '../data/services/futures_service.dart';
import 'app_provider.dart';
import 'auth_provider.dart';
import 'copy_trading_provider.dart';
import 'market_detail_provider.dart';
import 'ws_provider.dart';

enum OrderSide { long, short }

enum OrderType { market, limit, conditionalMarket, conditionalLimit }

enum PositionMode { open, close }

enum MarginMode { cross, isolated, split }

enum AmountUnit { lots, usdt, btc }

enum FuturesTab { positions, orders, assets }

bool _isFuturesTabActive(int tabIndex) => tabIndex == 2 || tabIndex == 3;

class FuturesPosition {
  final String id;
  final String symbol;
  final OrderSide side;
  final double size;          // 持仓量（基础币）
  final double availableSize; // 可平仓位
  final double entryPrice;
  final double markPrice;
  final double leverage;
  final double unrealizedPnl;
  final double liquidationPrice;
  final double margin;
  final String marginMode;
  final double? profitPrice;
  final double? lossPrice;
  final int? cutEntrustId;    // 止盈止损委托单 ID，有值时修改走 modify-cut
  final double contractSize;  // 每张合约对应基础币数量
  final double? apiMarginRate;
  final double? apiCurrentMargin;
  final double realizedPnl;
  final double commissionFee; // 累计手续费（JSON: commissionFee）

  const FuturesPosition({
    required this.id,
    required this.symbol,
    required this.side,
    required this.size,
    required this.availableSize,
    required this.entryPrice,
    required this.markPrice,
    required this.leverage,
    required this.unrealizedPnl,
    required this.liquidationPrice,
    required this.margin,
    this.marginMode = '全仓',
    this.profitPrice,
    this.lossPrice,
    this.cutEntrustId,
    this.contractSize = 1.0,
    this.apiMarginRate,
    this.apiCurrentMargin,
    this.realizedPnl = 0.0,
    this.commissionFee = 0.0,
  });

  double get lots => contractSize > 0 ? size / contractSize : size;

  double get currentMargin => (size > 0 && markPrice > 0 && leverage > 0)
      ? (size * markPrice) / leverage
      : margin;

  double get roe {
    final denom = (apiCurrentMargin != null && apiCurrentMargin! > 0)
        ? apiCurrentMargin!
        : currentMargin;
    return denom > 0 ? (unrealizedPnl / denom) * 100 : 0;
  }

  // 保证金比率 = 保证金 / 合约账户权益 × 100，不超过100%
  double get marginRatio {
    if (apiMarginRate != null && apiMarginRate! > 0) return apiMarginRate!;
    if (margin > 0 && (margin + unrealizedPnl) > 0) {
      return (margin / (margin + unrealizedPnl) * 100).clamp(0.0, 100.0);
    }
    return 0;
  }

  FuturesPosition withMarkPrice(double price) {
    return FuturesPosition(
      id: id,
      symbol: symbol,
      side: side,
      size: size,
      availableSize: availableSize,
      entryPrice: entryPrice,
      markPrice: price,
      leverage: leverage,
      unrealizedPnl: unrealizedPnl,
      liquidationPrice: liquidationPrice,
      margin: margin,
      marginMode: marginMode,
      profitPrice: profitPrice,
      lossPrice: lossPrice,
      cutEntrustId: cutEntrustId,
      contractSize: contractSize,
      apiMarginRate: apiMarginRate,
      apiCurrentMargin: apiCurrentMargin,
      realizedPnl: realizedPnl,
      commissionFee: commissionFee,
    );
  }

  FuturesPosition withContractSize(double cs) => FuturesPosition(
        id: id,
        symbol: symbol,
        side: side,
        size: size,
        availableSize: availableSize,
        entryPrice: entryPrice,
        markPrice: markPrice,
        leverage: leverage,
        unrealizedPnl: unrealizedPnl,
        liquidationPrice: liquidationPrice,
        margin: margin,
        marginMode: marginMode,
        profitPrice: profitPrice,
        lossPrice: lossPrice,
        cutEntrustId: cutEntrustId,
        contractSize: cs,
        apiMarginRate: apiMarginRate,
        apiCurrentMargin: apiCurrentMargin,
        realizedPnl: realizedPnl,
        commissionFee: commissionFee,
      );

  /// 从 /swap/wallet-new/get-with-positions 的 currentPositionWithCutList 解析
  factory FuturesPosition.fromJson(Map<String, dynamic> json) {
    final coin = json['coin'] as Map<String, dynamic>? ?? {};
    final symbol = coin['symbol'] as String? ?? '';
    final direction = json['direction'] as String? ?? 'BUY';
    final side = direction == 'BUY' ? OrderSide.long : OrderSide.short;

    final size = _toDouble(json['currentPosition']);
    final frozen = _toDouble(json['frozenPosition']);
    final explicitAvail = _toDouble(json['availablePosition']);
    final available = explicitAvail > 0
        ? explicitAvail
        : (size - frozen).clamp(0.0, double.infinity);

    final entryPrice = _toDouble(json['openPrice']);
    final leverage = _toDouble(json['leverage']);
    final currentPrice = _toDouble(json['currentPrice'] ?? entryPrice);
    final pnl = json['currentProfit'] != null
        ? _toDouble(json['currentProfit'])
        : (side == OrderSide.long
            ? (currentPrice - entryPrice) * size
            : (entryPrice - currentPrice) * size);
    // type 1 → 分仓；否则 → 全仓
    final posType = json['type'];
    final String marginMode = (posType == 1 || posType == '1') ? '分仓' : '全仓';

    // cutList 为止盈止损委托单列表，取第一条作为回显数据源
    final cutList = json['cutList'];
    Map<String, dynamic>? cutOrder;
    if (cutList is List && cutList.isNotEmpty) {
      cutOrder = Map<String, dynamic>.from(cutList.first as Map);
    }
    final rawProfit = cutOrder != null
        ? _toDouble(cutOrder['profitPrice'])
        : _toDouble(json['profitPrice']);
    final rawLoss = cutOrder != null
        ? _toDouble(cutOrder['lossPrice'])
        : _toDouble(json['lossPrice']);
    final cutEntrustId = cutOrder != null
        ? (cutOrder['id'] is int
            ? cutOrder['id'] as int
            : int.tryParse('${cutOrder['id'] ?? ''}'))
        : null;

    final rawMarginRate = json['marginRate'];
    final apiMarginRate = rawMarginRate != null ? _toDouble(rawMarginRate) : null;
    final rawCurrentMargin = _toDouble(json['currentMargin']);
    final realizedPnl = _toDouble(
        json['usdtProfit'] ?? json['realizedPnl'] ?? json['realizedProfit'] ?? 0);
    return FuturesPosition(
      id: json['id']?.toString() ?? '',
      symbol: symbol,
      side: side,
      size: size,
      availableSize: available,
      entryPrice: entryPrice,
      markPrice: currentPrice,
      leverage: leverage,
      unrealizedPnl: pnl,
      liquidationPrice: _calcLiquidationPrice(
        apiValue: _toDouble(json['estimatedBlastPrice']),
      ),
      margin: _toDouble(json['principalAmount']),
      marginMode: marginMode,
      profitPrice: rawProfit > 0 ? rawProfit : null,
      lossPrice: rawLoss > 0 ? rawLoss : null,
      cutEntrustId: cutEntrustId,
      apiMarginRate: apiMarginRate != null && apiMarginRate > 0 ? apiMarginRate : null,
      apiCurrentMargin: rawCurrentMargin > 0 ? rawCurrentMargin : null,
      realizedPnl: realizedPnl,
      commissionFee: _toDouble(json['commissionFee']),
    );
  }

  /// 从 /swap/order/history 历史仓位记录解析
  factory FuturesPosition.fromHistoryJson(Map<String, dynamic> json) {
    final rawSym = (json['symbol'] ?? json['coinSymbol'] ?? '').toString();
    final direction = (json['direction'] ?? 'BUY').toString().toUpperCase();
    final side = direction == 'BUY' || direction == '0'
        ? OrderSide.long
        : OrderSide.short;

    final size = _toDouble(json['tradedVolume'] ?? json['volume'] ?? 0);
    final entryPrice = _toDouble(json['usdtOpenPrice'] ?? json['openPrice'] ?? 0);
    final closePrice = _toDouble(json['tradedPrice'] ?? json['closePrice'] ?? entryPrice);
    final leverage = _toDouble(json['leverage'] ?? 20);
    final realizedPnl = _toDouble(
        json['profitAndLoss'] ?? json['profit'] ??
        json['realizedPnl'] ?? json['realizedProfit'] ??
        json['closeProfitLoss'] ?? 0);
    final marginMode = _mapHistoryPositionType(json['positionType'], json['patterns']);

    return FuturesPosition(
      id: json['id']?.toString() ?? '',
      symbol: rawSym,
      side: side,
      size: size,
      availableSize: 0,
      entryPrice: entryPrice,
      markPrice: closePrice,
      leverage: leverage,
      unrealizedPnl: 0,
      liquidationPrice: 0,
      margin: _toDouble(json['principalAmount'] ?? 0),
      marginMode: marginMode,
      realizedPnl: realizedPnl,
    );
  }

  static String _mapHistoryPositionType(dynamic posType, dynamic patterns) {
    final pt = posType?.toString() ?? '';
    if (pt == '0') return '全仓';
    if (pt == '1') return '逐仓';
    final p = (patterns?.toString() ?? '').toUpperCase();
    if (p == 'ISOLATED') return '逐仓';
    return '全仓';
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is FuturesPosition &&
          id == other.id &&
          symbol == other.symbol &&
          side == other.side &&
          size == other.size &&
          entryPrice == other.entryPrice &&
          markPrice == other.markPrice &&
          leverage == other.leverage &&
          unrealizedPnl == other.unrealizedPnl &&
          liquidationPrice == other.liquidationPrice &&
          margin == other.margin &&
          marginMode == other.marginMode;

  @override
  int get hashCode => Object.hash(id, symbol, side, size, entryPrice,
      markPrice, leverage, unrealizedPnl, liquidationPrice, margin, marginMode);
}

class FuturesOrder {
  final String id;
  final String symbol;
  final OrderSide side;
  final OrderType type;
  final double price;        // 委托价（entrustPrice）
  final double tradedPrice;  // 成交均价（tradedPrice）
  final double triggerPrice; // 计划委托触发价
  final double size;
  final double filledSize;
  final String status;
  final String action; // 开多/开空/平多/平空
  final String marginMode;
  final double leverage;
  final DateTime? createTime;
  final DateTime? dealTime;  // 成交时间
  final double? profitPrice; // 止盈价（>0 时有效）
  final double? lossPrice;   // 止损价（>0 时有效）
  final double fee;          // 手续费（openFee + closeFee）

  const FuturesOrder({
    required this.id,
    required this.symbol,
    required this.side,
    required this.type,
    required this.price,
    required this.size,
    required this.filledSize,
    required this.status,
    this.tradedPrice = 0,
    this.triggerPrice = 0,
    this.action = '开多',
    this.marginMode = '全仓',
    this.leverage = 20,
    this.createTime,
    this.dealTime,
    this.profitPrice,
    this.lossPrice,
    this.fee = 0,
  });

  /// 是否为开仓方向（开多/开空）
  bool get isOpenOrder => action.contains('开');

  /// 是否处于委托中（可撤销）状态
  bool get isPending => status == '委托中';

  /// 展示用类型标签
  String get typeLabel {
    switch (type) {
      case OrderType.market: return '市价';
      case OrderType.limit: return '限价';
      case OrderType.conditionalMarket:
      case OrderType.conditionalLimit: return '计划委托';
    }
  }

  String get priceDisplay {
    String fmt(double v) =>
        v == v.truncateToDouble() ? '${v.toInt()}' : v.toString();
    switch (type) {
      case OrderType.market:
      case OrderType.conditionalMarket:
        return '市价';
      case OrderType.limit:
        return price > 0 ? fmt(price) : '--';
      case OrderType.conditionalLimit:
        // 计划市价：entrustPrice=0；计划限价：entrustPrice=实际限价
        return price > 0 ? fmt(price) : '市价';
    }
  }

  /// 从 /swap/order/current 解析
  factory FuturesOrder.fromJson(Map<String, dynamic> json) {
    // direction 可能是字符串 "BUY"/"SELL" 或整数 0/1
    final dirRaw = json['direction'];
    final bool isLong;
    if (dirRaw is int) {
      isLong = dirRaw == 0; // 0=买多，1=卖空
    } else {
      isLong = (dirRaw as String? ?? 'BUY') == 'BUY';
    }
    final side = isLong ? OrderSide.long : OrderSide.short;

    // type 可能是字符串枚举或整数（0=市价，1=限价，2=计划委托）
    // 计划委托中 entrustPrice=0 为计划市价，entrustPrice>0 为计划限价
    // 注意：不能用 json['price'] 作为 fallback，否则市价单会被误判为限价单
    final typeRaw = json['type'];
    final entrustPriceRaw = _toDouble(json['entrustPrice']);
    final OrderType orderType;
    if (typeRaw is int) {
      orderType = switch (typeRaw) {
        0 => OrderType.market,
        2 => entrustPriceRaw > 0
            ? OrderType.conditionalLimit
            : OrderType.conditionalMarket,
        _ => OrderType.limit,
      };
    } else {
      final s = (typeRaw as String? ?? '').toUpperCase();
      if (s == 'SPOT_LIMIT' || s == 'PLAN') {
        orderType = entrustPriceRaw > 0
            ? OrderType.conditionalLimit
            : OrderType.conditionalMarket;
      } else {
        orderType = s == 'MARKET_PRICE' || s == 'MARKET'
            ? OrderType.market
            : OrderType.limit;
      }
    }

    // entrustType：0=OPEN(开仓)，1=CLOSE(平仓)
    // 注意：positionType 是全仓/逐仓模式，不是开平仓标志，不要用它判断
    final entrustTypeRaw = json['entrustType'];
    final bool isClose;
    if (entrustTypeRaw is int) {
      isClose = entrustTypeRaw == 1;
    } else if (entrustTypeRaw is String) {
      final et = entrustTypeRaw.toUpperCase();
      isClose = et == '1' || et == 'CLOSE';
    } else {
      // entrustType 不存在时，看 closeType 是否有值（有值说明是平仓单）
      final closeTypeRaw = json['closeType'];
      isClose = closeTypeRaw != null;
    }
    final action = isClose
        ? (isLong ? '平多' : '平空')
        : (isLong ? '开多' : '开空');

    // createTime 为 13 位毫秒时间戳
    final ts = json['createTime'];
    DateTime? createTime;
    if (ts is int) {
      createTime = DateTime.fromMillisecondsSinceEpoch(ts);
    } else if (ts is String) {
      createTime = DateTime.tryParse(ts);
    }

    // dealTime：成交时间（Long 毫秒时间戳）
    final dealTs = json['dealTime'];
    DateTime? dealTime;
    if (dealTs is int && dealTs > 0) {
      dealTime = DateTime.fromMillisecondsSinceEpoch(dealTs);
    } else if (dealTs is String) {
      dealTime = DateTime.tryParse(dealTs);
    }

    // status 可能是字符串或整数
    final statusRaw = json['status'];
    final status = _mapStatus(statusRaw is String ? statusRaw : statusRaw?.toString() ?? '');

    // symbol 可能是直接字段（EntrustBean），也可能嵌套在 coin 中（position list）
    final coin = json['coin'] as Map<String, dynamic>? ?? {};
    final symbol = (json['symbol'] as String?)
        ?? (coin['symbol'] as String?)
        ?? '';

    // 止盈止损（>0 时有效）
    final rawProfit = _toDouble(json['profitPrice'] ?? json['stopProfitPrice']);
    final rawLoss   = _toDouble(json['lossPrice']   ?? json['stopLossPrice']);

    return FuturesOrder(
      id: json['id']?.toString() ?? '',
      symbol: symbol,
      side: side,
      type: orderType,
      price: _toDouble(json['entrustPrice']),
      tradedPrice: _toDouble(json['tradedPrice']),
      triggerPrice: _toDouble(json['triggerPrice']),
      size: _toDouble(json['volume'] ?? json['size']),
      filledSize: _toDouble(json['tradedVolume'] ?? json['filledSize']),
      status: status,
      action: action,
      marginMode: _parsePatterns(json['patterns']),
      leverage: _toDouble(json['leverage'] ?? 20).toInt().toDouble(),
      createTime: createTime,
      dealTime: dealTime,
      profitPrice: rawProfit > 0 ? rawProfit : null,
      lossPrice: rawLoss > 0 ? rawLoss : null,
      fee: _toDouble(json['openFee']) + _toDouble(json['closeFee']),
    );
  }

  /// 从 /swap/order/history-open 历史委托记录解析
  factory FuturesOrder.fromHistoryJson(Map<String, dynamic> json) {
    final rawSym = (json['symbol'] ?? json['coinSymbol'] ?? '').toString();
    final dirRaw = json['direction'];
    final bool isLong;
    if (dirRaw is int) {
      isLong = dirRaw == 0;
    } else {
      final s = (dirRaw?.toString() ?? 'BUY').toUpperCase();
      isLong = s == 'BUY' || s == '0';
    }
    final side = isLong ? OrderSide.long : OrderSide.short;

    // 使用 type 字段判断委托类型；openType 是仓位开仓方式，平仓单中继承自仓位，不能用于判断平仓单类型
    final typeRaw = json['type'];
    final entrustPriceRaw = _toDouble(json['entrustPrice']);
    final OrderType orderType;
    if (typeRaw is int) {
      orderType = switch (typeRaw) {
        0 => OrderType.market,
        2 => entrustPriceRaw > 0
            ? OrderType.conditionalLimit
            : OrderType.conditionalMarket,
        _ => OrderType.limit,
      };
    } else {
      final s = (typeRaw?.toString() ?? '').toUpperCase();
      if (s == 'SPOT_LIMIT' || s == 'PLAN') {
        orderType = entrustPriceRaw > 0
            ? OrderType.conditionalLimit
            : OrderType.conditionalMarket;
      } else {
        orderType = (s == 'MARKET_PRICE' || s == 'MARKET')
            ? OrderType.market
            : OrderType.limit;
      }
    }

    // 历史记录同样用 entrustType 判断开平仓，fallback 看 closeType 是否有值
    final entrustTypeRaw = json['entrustType'];
    final bool isClose;
    if (entrustTypeRaw is int) {
      isClose = entrustTypeRaw == 1;
    } else if (entrustTypeRaw is String) {
      final et = entrustTypeRaw.toUpperCase();
      isClose = et == '1' || et == 'CLOSE';
    } else {
      isClose = json['closeType'] != null;
    }
    final action = isClose
        ? (isLong ? '平多' : '平空')
        : (isLong ? '开多' : '开空');

    final ts = json['createTime'] ?? json['usdtOpenTime'];
    DateTime? createTime;
    if (ts is int) {
      createTime = DateTime.fromMillisecondsSinceEpoch(ts);
    } else if (ts is String) {
      createTime = DateTime.tryParse(ts);
    }

    final dealTs = json['dealTime'];
    DateTime? dealTime;
    if (dealTs is int && dealTs > 0) {
      dealTime = DateTime.fromMillisecondsSinceEpoch(dealTs);
    } else if (dealTs is String) {
      dealTime = DateTime.tryParse(dealTs);
    }

    final statusRaw = json['status'];
    final status = _mapStatus(statusRaw is String ? statusRaw : statusRaw?.toString() ?? '');

    return FuturesOrder(
      id: json['id']?.toString() ?? '',
      symbol: rawSym,
      side: side,
      type: orderType,
      price: _toDouble(json['entrustPrice'] ?? 0),
      tradedPrice: _toDouble(json['tradedPrice'] ?? 0),
      triggerPrice: _toDouble(json['triggerPrice'] ?? 0),
      size: _toDouble(json['volume'] ?? json['size'] ?? 0),
      filledSize: _toDouble(json['tradedVolume'] ?? json['dealVolume'] ?? 0),
      status: status,
      action: action,
      marginMode: _parsePatterns(json['patterns']),
      leverage: _toDouble(json['leverage'] ?? 20).toInt().toDouble(),
      createTime: createTime,
      dealTime: dealTime,
      fee: _toDouble(json['openFee']) + _toDouble(json['closeFee']),
    );
  }

  static String _mapStatus(String raw) {
    return switch (raw) {
      'ENTRUST_ING' => '委托中',
      'ENTRUST_SUCCESS' => '已成交',
      'ENTRUST_CANCEL' => '已撤销',
      _ => raw,
    };
  }

  /// 解析仓位模式：CROSSED=全仓，FIXED=分仓；ordinal 0=全仓，1=分仓
  static String _parsePatterns(dynamic raw) {
    if (raw == null) return '全仓';
    final s = raw.toString().toUpperCase();
    if (s == 'FIXED' || s == '1') return '分仓';
    return '全仓';
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is FuturesOrder &&
          id == other.id &&
          symbol == other.symbol &&
          side == other.side &&
          type == other.type &&
          price == other.price &&
          triggerPrice == other.triggerPrice &&
          size == other.size &&
          filledSize == other.filledSize &&
          status == other.status &&
          action == other.action &&
          marginMode == other.marginMode &&
          leverage == other.leverage &&
          profitPrice == other.profitPrice &&
          lossPrice == other.lossPrice;

  @override
  int get hashCode => Object.hash(id, symbol, side, type, price, triggerPrice,
      size, filledSize, status, action, marginMode, leverage,
      profitPrice, lossPrice);
}

class FuturesAccountInfo {
  final double totalBalance;
  final double availableMargin;
  final double usedMargin;
  final double unrealizedPnl;

  const FuturesAccountInfo({
    required this.totalBalance,
    required this.availableMargin,
    required this.usedMargin,
    required this.unrealizedPnl,
  });

  factory FuturesAccountInfo.fromJson(Map<String, dynamic> json,
      {double unrealizedPnl = 0, double positionsMargin = 0}) {
    // currentCapital = 账户当前权益（合约账户总余额）
    // 已用保证金 = 持仓 principalAmount 之和（最准确），其次 frozenMargin
    // 可用保证金 = 总余额 - 已用保证金
    final total = _toDouble(json['currentCapital'] ?? json['balance']);
    final frozen = _toDouble(json['frozenMargin'] ?? json['frozenBalance']);
    final usedMargin = positionsMargin > 0
        ? positionsMargin
        : frozen > 0
            ? frozen
            : 0.0;
    final available = (total - usedMargin).clamp(0.0, double.infinity);
    return FuturesAccountInfo(
      totalBalance: total,
      usedMargin: usedMargin,
      availableMargin: available,
      unrealizedPnl: unrealizedPnl,
    );
  }

  const FuturesAccountInfo.empty()
      : totalBalance = 0,
        availableMargin = 0,
        usedMargin = 0,
        unrealizedPnl = 0;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is FuturesAccountInfo &&
          totalBalance == other.totalBalance &&
          availableMargin == other.availableMargin &&
          usedMargin == other.usedMargin &&
          unrealizedPnl == other.unrealizedPnl;

  @override
  int get hashCode =>
      Object.hash(totalBalance, availableMargin, usedMargin, unrealizedPnl);
}

class FuturesState {
  final String symbol;
  final int contractCoinId;
  final double contractSize;  // 每张对应基础币数量
  final int volScale;         // 张数精度
  final String coinSymbol;    // 基础资产名称
  final double lastPrice;
  final String? lastPriceStr; // WS 返回的原始价格字符串
  final double markPrice;
  final double change24h;
  final double leverage;
  final MarginMode marginMode;
  final PositionMode positionMode;
  final OrderSide orderSide;
  final OrderType orderType;
  final AmountUnit amountUnit;
  final double inputPrice;
  final double inputSize;
  final double sliderPercent;
  final bool isSliderInput;  // true=滑块输入，false=手动输入
  final bool tpslEnabled;
  final double? tpPrice;
  final double? slPrice;
  final double fundingRate;
  final String fundingCountdown;
  final FuturesTab activeTab;
  final List<FuturesPosition> positions;
  final List<FuturesOrder> openOrders;
  final bool ordersHasMore;
  final int ordersPage;
  final bool ordersLoadingMore;
  final FuturesAccountInfo accountInfo;
  final bool isLoading;         // 首屏骨架
  final bool isTabLoading;      // 底部列表骨架
  final bool isSwitchingMode;   // 模式切换中（切换完成前禁止下单）
  final String? errorMsg;
  final List<Map<String, dynamic>> orderBookAsks;
  final List<Map<String, dynamic>> orderBookBids;
  final bool hideOtherSymbols;

  final int pricePrecision;
  final int coinPrecision;
  final int usdtPrecision;
  final int leverageMin;
  final int leverageMax;
  final List<int> leverageOptions;
  final bool isDiscreteLeverage; // true=固定档位，false=区间任意值
  final double openFeeRate;

  const FuturesState({
    required this.symbol,
    this.contractCoinId = 0,
    this.contractSize = 1.0,
    this.volScale = 0,
    this.coinSymbol = '',
    required this.lastPrice,
    this.lastPriceStr,
    required this.markPrice,
    this.change24h = 0,
    this.leverage = 20,
    this.marginMode = MarginMode.cross,
    this.positionMode = PositionMode.open,
    this.orderSide = OrderSide.long,
    this.orderType = OrderType.market,
    this.amountUnit = AmountUnit.btc,
    this.inputPrice = 0,
    this.inputSize = 0,
    this.sliderPercent = 0,
    this.isSliderInput = false,
    this.tpslEnabled = false,
    this.tpPrice,
    this.slPrice,
    this.fundingRate = 0,
    this.fundingCountdown = '--:--:--',
    this.activeTab = FuturesTab.positions,
    required this.positions,
    required this.openOrders,
    this.ordersHasMore = false,
    this.ordersPage = 1,
    this.ordersLoadingMore = false,
    required this.accountInfo,
    this.isLoading = false,
    this.isTabLoading = false,
    this.isSwitchingMode = false,
    this.errorMsg,
    this.orderBookAsks = const [],
    this.orderBookBids = const [],
    this.hideOtherSymbols = false,
    this.pricePrecision = 2,
    this.coinPrecision = 4,
    this.usdtPrecision = 2,
    this.leverageMin = 1,
    this.leverageMax = 125,
    this.leverageOptions = const [],
    this.isDiscreteLeverage = false,
    this.openFeeRate = 0.0005,
  });

  /// 当前数量单位对应的精度
  int get currentAmountPrecision {
    switch (amountUnit) {
      case AmountUnit.lots:
        return 0;
      case AmountUnit.btc:
        return coinPrecision;
      case AmountUnit.usdt:
        return usdtPrecision;
    }
  }

  static String _norm(String s) => s.replaceAll('/', '').toUpperCase();

  List<FuturesPosition> get displayPositions {
    if (!hideOtherSymbols) return positions;
    final cur = _norm(symbol);
    return positions.where((p) => _norm(p.symbol) == cur).toList();
  }

  List<FuturesOrder> get displayOrders {
    if (!hideOtherSymbols) return openOrders;
    final cur = _norm(symbol);
    return openOrders.where((o) => _norm(o.symbol) == cur).toList();
  }

  String get orderTypeLabel {
    switch (orderType) {
      case OrderType.market:
        return '市价单';
      case OrderType.limit:
        return '限价单';
      case OrderType.conditionalMarket:
        return '市价条件委托单';
      case OrderType.conditionalLimit:
        return '限价条件委托单';
    }
  }

  String get marginModeLabel => switch (marginMode) {
    MarginMode.cross     => '全仓',
    MarginMode.isolated  => '逐仓',
    MarginMode.split     => '分仓',
  };

  String get amountUnitLabel {
    switch (amountUnit) {
      case AmountUnit.lots:
        return '张';
      case AmountUnit.usdt:
        return 'USDT';
      case AmountUnit.btc:
        if (coinSymbol.isNotEmpty) return coinSymbol;
            final base = symbol.toUpperCase().replaceFirst(RegExp(r'USDT$'), '');
        return base.isNotEmpty ? base : 'BTC';
    }
  }

  bool get isConditionalOrder =>
      orderType == OrderType.conditionalMarket ||
      orderType == OrderType.conditionalLimit;

  bool get showPriceInput =>
      orderType == OrderType.limit || orderType == OrderType.conditionalLimit;

  FuturesState copyWith({
    String? symbol,
    int? contractCoinId,
    double? contractSize,
    int? volScale,
    String? coinSymbol,
    double? lastPrice,
    String? lastPriceStr,
    double? markPrice,
    double? change24h,
    double? leverage,
    MarginMode? marginMode,
    PositionMode? positionMode,
    OrderSide? orderSide,
    OrderType? orderType,
    AmountUnit? amountUnit,
    double? inputPrice,
    double? inputSize,
    double? sliderPercent,
    bool? isSliderInput,
    bool? tpslEnabled,
    double? tpPrice,
    double? slPrice,
    double? fundingRate,
    String? fundingCountdown,
    FuturesTab? activeTab,
    List<FuturesPosition>? positions,
    List<FuturesOrder>? openOrders,
    bool? ordersHasMore,
    int? ordersPage,
    bool? ordersLoadingMore,
    FuturesAccountInfo? accountInfo,
    bool? isLoading,
    bool? isTabLoading,
    bool? isSwitchingMode,
    String? errorMsg,
    List<Map<String, dynamic>>? orderBookAsks,
    List<Map<String, dynamic>>? orderBookBids,
    bool? hideOtherSymbols,
    int? pricePrecision,
    int? coinPrecision,
    int? usdtPrecision,
    int? leverageMin,
    int? leverageMax,
    List<int>? leverageOptions,
    bool? isDiscreteLeverage,
    double? openFeeRate,
  }) =>
      FuturesState(
        symbol: symbol ?? this.symbol,
        contractCoinId: contractCoinId ?? this.contractCoinId,
        contractSize: contractSize ?? this.contractSize,
        volScale: volScale ?? this.volScale,
        coinSymbol: coinSymbol ?? this.coinSymbol,
        lastPrice: lastPrice ?? this.lastPrice,
        lastPriceStr: lastPriceStr ?? this.lastPriceStr,
        markPrice: markPrice ?? this.markPrice,
        change24h: change24h ?? this.change24h,
        leverage: leverage ?? this.leverage,
        marginMode: marginMode ?? this.marginMode,
        positionMode: positionMode ?? this.positionMode,
        orderSide: orderSide ?? this.orderSide,
        orderType: orderType ?? this.orderType,
        amountUnit: amountUnit ?? this.amountUnit,
        inputPrice: inputPrice ?? this.inputPrice,
        inputSize: inputSize ?? this.inputSize,
        sliderPercent: sliderPercent ?? this.sliderPercent,
        isSliderInput: isSliderInput ?? this.isSliderInput,
        tpslEnabled: tpslEnabled ?? this.tpslEnabled,
        tpPrice: tpPrice ?? this.tpPrice,
        slPrice: slPrice ?? this.slPrice,
        fundingRate: fundingRate ?? this.fundingRate,
        fundingCountdown: fundingCountdown ?? this.fundingCountdown,
        activeTab: activeTab ?? this.activeTab,
        positions: positions ?? this.positions,
        openOrders: openOrders ?? this.openOrders,
        ordersHasMore: ordersHasMore ?? this.ordersHasMore,
        ordersPage: ordersPage ?? this.ordersPage,
        ordersLoadingMore: ordersLoadingMore ?? this.ordersLoadingMore,
        accountInfo: accountInfo ?? this.accountInfo,
        isLoading: isLoading ?? this.isLoading,
        isTabLoading: isTabLoading ?? this.isTabLoading,
        isSwitchingMode: isSwitchingMode ?? this.isSwitchingMode,
        errorMsg: errorMsg,
        orderBookAsks: orderBookAsks ?? this.orderBookAsks,
        orderBookBids: orderBookBids ?? this.orderBookBids,
        hideOtherSymbols: hideOtherSymbols ?? this.hideOtherSymbols,
        pricePrecision: pricePrecision ?? this.pricePrecision,
        coinPrecision: coinPrecision ?? this.coinPrecision,
        usdtPrecision: usdtPrecision ?? this.usdtPrecision,
        leverageMin: leverageMin ?? this.leverageMin,
        leverageMax: leverageMax ?? this.leverageMax,
        leverageOptions: leverageOptions ?? this.leverageOptions,
        isDiscreteLeverage: isDiscreteLeverage ?? this.isDiscreteLeverage,
        openFeeRate: openFeeRate ?? this.openFeeRate,
      );
}

class FuturesNotifier extends AutoDisposeFamilyNotifier<FuturesState, String> {
  StreamSubscription<Map<String, dynamic>>? _tickerSub;
  StreamSubscription<Map<String, dynamic>>? _depthSub;
  StreamSubscription<Map<String, dynamic>>? _markSub;
  Timer? _fundingTimer;
  Timer? _pollTimer;
  bool _manuallyPaused = false; // 子页面推入时手动暂停，防止 tab 切换后误恢复
  int _nextSettleTimeMs = 0;
  final Map<String, int> _lastTickerUpdateMs = {}; // ticker 节流
  int _lastDepthUpdateMs = 0; // 盘口节流
  int _lastPositionTickUpdateMs = 0; // 持仓 mark price 节流（ticker 路径）
  Timer? _depthTimer;
  List<Map<String, dynamic>>? _pendingAsks;
  List<Map<String, dynamic>>? _pendingBids;
  // 切换到平仓模式时，若原 orderType 为计划委托类型则暂存，切回开仓时恢复
  OrderType? _savedConditionalType;

  // 路由 symbol（如 BTCUSDT）转 API 格式（BTC/USDT）
  static String _toApiSymbol(String symbol) {
    String s = symbol.replaceAll('-', '/');
    if (!s.contains('/')) {
      for (final base in ['USDT', 'BTC', 'ETH', 'BUSD']) {
        if (s.endsWith(base) && s.length > base.length) {
          return '${s.substring(0, s.length - base.length)}/$base';
        }
      }
    }
    return s;
  }

  @override
  FuturesState build(String symbol) {
    ref.onDispose(_dispose);

    // 读取该交易对上次用户选择的保证金模式（跨 symbol 切换时保留）
    final savedModeStr = ref.read(sharedPreferencesProvider)
        .getString('futures_margin_mode_$symbol');
    final savedMode = switch (savedModeStr) {
      'split'    => MarginMode.split,
      'isolated' => MarginMode.isolated,
      _          => MarginMode.cross,
    };

    final initial = FuturesState(
      symbol: symbol,
      lastPrice: 0,
      markPrice: 0,
      positions: const [],
      openOrders: const [],
      accountInfo: const FuturesAccountInfo.empty(),
      isLoading: true,
      amountUnit: ref.read(futuresAmountUnitPrefProvider),
      hideOtherSymbols: ref.read(futuresHideOtherPrefProvider),
      marginMode: savedMode,
      leverage: 20,
    );

    Future.microtask(() => _init(symbol));

    // 监听 K 线行情数据，保持与 K 线页面价格一致
    // 注意：不能用 fireImmediately: true，build() 返回前 state 尚未初始化
    ref.listen<MarketStats?>(
      marketDetailProvider(MarketDetailKey(symbol: symbol, isFutures: true))
          .select((s) => s.stats),
      (_, stats) {
        if (stats == null || stats.lastPrice <= 0) return;
        state = state.copyWith(
          lastPrice: stats.lastPrice,
          change24h: stats.change24h,
        );
      },
    );

    // 监听 WS 重连：重连成功后若合约信息未加载则重新初始化
    ref.listen<AsyncValue<WsConnectionState>>(
      wsConnectionStateProvider,
      (prev, next) {
        final prevState = prev?.valueOrNull;
        final nextState = next.valueOrNull;
        // 任何 → connected 的转换都触发（reconnecting→connecting→connected，prev 是 connecting）
        if (nextState == WsConnectionState.connected &&
            prevState != WsConnectionState.connected) {
          if (state.contractCoinId == 0) {
            _init(symbol);
          }
        }
      },
    );

    // 监听登录状态：登录后自动加载持仓；退出后清空数据并停止轮询
    ref.listen<bool>(isLoggedInProvider, (prev, loggedIn) {
      if (loggedIn) {
        // 登录后刷新带单员身份（用于判断是否显示分仓选项）
        ref.read(copyTradingProvider.notifier).silentRefresh();
        _loadWallet(state.symbol);
        _loadCurrentOrders();
        // 在合约页时直接启动轮询，不区分子 tab
        if (_isFuturesTabActive(ref.read(activeBottomTabProvider))) {
          _startPolling(state.symbol);
        }
      } else {
        _stopPolling();
        state = state.copyWith(
          positions: [],
          openOrders: [],
          accountInfo: const FuturesAccountInfo.empty(),
        );
      }
    });

    // 监听底部导航 tab 切换：离开合约页停止轮询，进入合约页直接启动轮询
    // 注意：若子页面手动暂停了轮询（_manuallyPaused），切回 tab 时不自动恢复
    ref.listen<int>(activeBottomTabProvider, (prev, tabIndex) {
      if (_isFuturesTabActive(tabIndex)) {
        if (ref.read(isLoggedInProvider) && !_manuallyPaused) {
          _loadWallet(state.symbol);
          _loadCurrentOrders();
          _startPolling(state.symbol);
        }
      } else {
        _stopPolling();
      }
    });

    // 监听带单员身份变化：若用户身份为带单员，强制切回全仓模式
    ref.listen<bool>(
      copyTradingProvider.select((s) => s.isTrader),
      (_, isTrader) {
        if (isTrader && state.marginMode == MarginMode.split) {
          state = state.copyWith(marginMode: MarginMode.cross);
        }
      },
    );

    return initial;
  }

  Future<void> _init(String symbol) async {
    await _loadContractInfo(symbol);
    _subscribeWebSocket(symbol);
    state = state.copyWith(isLoading: false, isTabLoading: true);
    if (ref.read(isLoggedInProvider)) {
      try {
        await Future.wait([_loadWallet(symbol), _loadCurrentOrders()]);
      } catch (_) {}
      if (_isFuturesTabActive(ref.read(activeBottomTabProvider))) {
        _startPolling(symbol);
      }
    }
    state = state.copyWith(isTabLoading: false);
  }

  void _startPolling(String symbol) {
    _pollTimer?.cancel();
    _pollTimer = Timer.periodic(const Duration(seconds: 1), (_) {
      if (!_isFuturesTabActive(ref.read(activeBottomTabProvider))) {
        _stopPolling();
        return;
      }
      _loadWallet(symbol);
      _loadCurrentOrders();
    });
  }

  void _stopPolling() {
    _pollTimer?.cancel();
    _pollTimer = null;
  }

  /// 供页面跳转时外部调用：暂停轮询（标记手动暂停，防止 tab 切换后误恢复）
  void stopPolling() {
    _manuallyPaused = true;
    _stopPolling();
  }

  /// 供页面返回时外部调用：恢复轮询（仅登录且在合约页时）
  void resumePolling(String symbol) {
    _manuallyPaused = false;
    if (ref.read(isLoggedInProvider) &&
        _isFuturesTabActive(ref.read(activeBottomTabProvider))) {
      _startPolling(symbol);
    }
  }


  Future<void> _loadContractInfo(String symbol) async {
    try {
      final svc = _service;
      final apiSymbol = _toApiSymbol(symbol);
      final info = await svc.getSymbolInfo(apiSymbol);
      final id = (info['id'] as num?)?.toInt() ?? 0;
      if (id == 0) return;

      final pricePrecision = ((info['coinScale'] ?? info['minScale']) as num?)?.toInt() ?? 2;
      const volScale = 0;
      final coinPrecision = (info['minScale'] as num?)?.toInt() ?? 4;
      final usdtPrecision = (info['baseCoinScale'] as num?)?.toInt() ?? 4;
      final contractSize = _toDouble(
          info['shareNumber'] ?? info['contractSize'] ?? info['shareSize'] ?? 1);
      final coinObj = info['coin'] as Map?;
      final rawSym = coinObj?['symbol'] as String? ?? apiSymbol;
      final coinSymbol = rawSym.contains('/') ? rawSym.split('/').first : rawSym;

      // 杠杆档位（逗号分隔字符串）
      final rawLeverage = info['leverage']?.toString() ?? '';
      final leverageOptions = rawLeverage.isNotEmpty
          ? rawLeverage
              .split(',')
              .map((s) => int.tryParse(s.trim()))
              .whereType<int>()
              .where((v) => v > 0)
              .toList()
          : <int>[];
      leverageOptions.sort();
      final leverageMin = leverageOptions.isNotEmpty ? leverageOptions.first : 1;
      final leverageMax = leverageOptions.isNotEmpty ? leverageOptions.last : 125;
      // leverageType: 1=固定档位，2=区间任意值
      final leverageType = (info['leverageType'] as num?)?.toInt() ?? 2;
      final isDiscrete = leverageType == 1;
      final openFeeRate = _toDouble(info['openFee'] ?? info['makerFee'] ?? 0.0005);

      state = state.copyWith(
        contractCoinId: id,
        pricePrecision: pricePrecision,
        volScale: volScale.toInt(),
        coinPrecision: coinPrecision,
        usdtPrecision: usdtPrecision,
        contractSize: contractSize > 0 ? contractSize : 1.0,
        coinSymbol: coinSymbol,
        leverageMin: leverageMin,
        leverageMax: leverageMax,
        leverageOptions: leverageOptions,
        isDiscreteLeverage: isDiscrete,
        openFeeRate: openFeeRate > 0 ? openFeeRate : 0.0005,
      );

      try {
        final leverage = await svc.getLeverage(id);
        state = state.copyWith(leverage: leverage.toDouble());
      } catch (_) {}
    } catch (_) {}
  }

  Future<void> refreshWallet() => _loadWallet(state.symbol);

  Future<void> _loadWallet(String symbol) async {
    if (!ref.read(isLoggedInProvider)) return;
    final data = await _service
        .getWithPositions(symbol)
        .catchError((_) => <String, dynamic>{});

    final wallet = data;
    final rawPositions = (data['currentPositionWithCutList'] as List<dynamic>? ?? [])
        .cast<Map<String, dynamic>>();

    List<FuturesPosition> positions = [];
    final cs = state.contractSize > 0 ? state.contractSize : 1.0;
    for (final item in rawPositions) {
      try {
        positions.add(FuturesPosition.fromJson(item).withContractSize(cs));
      } catch (e) {
        debugPrint('[FuturesPosition] parse error: $e\nitem: $item');
      }
    }
    // 有匹配当前币对的持仓时，同步杠杆到下单表单
    final currentSymbol = state.symbol.toUpperCase().replaceAll('-', '');
    final matchedPos = positions.where((p) {
      final ps = p.symbol.toUpperCase().replaceAll('-', '').replaceAll('/', '');
      return ps.contains(currentSymbol) || currentSymbol.contains(ps);
    }).toList();
    if (matchedPos.isNotEmpty && matchedPos.first.leverage > 0) {
      state = state.copyWith(
        positions: positions,
        leverage: matchedPos.first.leverage,
      );
    } else {
      state = state.copyWith(positions: positions);
    }
    _subscribePositionTickers(positions);

    try {
      if (wallet.isNotEmpty) {
        final totalPnl = positions.fold(0.0, (sum, p) => sum + p.unrealizedPnl);
        final positionsMargin =
            positions.fold(0.0, (sum, p) => sum + p.margin);
        final accountInfo = FuturesAccountInfo.fromJson(
          wallet,
          unrealizedPnl: totalPnl,
          positionsMargin: positionsMargin,
        );
        // marginMode 不从服务器同步（进入合约页默认全仓，用户主动切换才变更）
        final isTrader = ref.read(copyTradingProvider).isTrader;
        final syncedMode = (isTrader && state.marginMode == MarginMode.split)
            ? MarginMode.cross
            : null;
        state = state.copyWith(
          accountInfo: accountInfo,
          marginMode: syncedMode ?? state.marginMode,
        );
      }
    } catch (_) {}
  }

  Future<void> _loadCurrentOrders() async {
    if (!ref.read(isLoggedInProvider)) return;
    try {
      final result = await _service.getCurrentOrders(pageNo: 1, pageSize: 10);
      final orders = _parseOrders(result.items);
      state = state.copyWith(
        openOrders: orders,
        ordersPage: 1,
        ordersHasMore: result.hasMore,
      );
    } catch (_) {}
  }

  Future<void> loadMoreOrders() async {
    if (state.ordersLoadingMore || !state.ordersHasMore) return;
    state = state.copyWith(ordersLoadingMore: true);
    try {
      final nextPage = state.ordersPage + 1;
      final result =
          await _service.getCurrentOrders(pageNo: nextPage, pageSize: 10);
      final more = _parseOrders(result.items);
      state = state.copyWith(
        openOrders: [...state.openOrders, ...more],
        ordersPage: nextPage,
        ordersHasMore: result.hasMore,
        ordersLoadingMore: false,
      );
    } catch (e) {
      state = state.copyWith(ordersLoadingMore: false);
    }
  }

  static List<FuturesOrder> _parseOrders(List<Map<String, dynamic>> raw) {
    final orders = <FuturesOrder>[];
    for (final item in raw) {
      try {
        orders.add(FuturesOrder.fromJson(item));
      } catch (_) {}
    }
    return orders;
  }

  void _subscribeWebSocket(String symbol) {
    final wsSymbol = symbol.replaceAll('-', '').toLowerCase();
    final ws = ref.read(wsClientProvider);
    ws.subscribeTicker(wsSymbol);
    ws.subscribeDepth(wsSymbol);
    ws.subscribeMark(wsSymbol);

    _markSub?.cancel();
    _markSub = ws.markStream
        .where((d) => (d['symbol'] as String? ?? '').toLowerCase() == wsSymbol)
        .listen((data) {
      final markPrice = (data['markPrice'] as num?)?.toDouble() ?? 0;
      final fundingRate = (data['fundingRate'] as num?)?.toDouble() ?? 0;
      final nextFundingTime = (data['nextFundingTime'] as num?)?.toInt() ?? 0;

      if (markPrice > 0) {
        state = state.copyWith(markPrice: markPrice);
      }
      // 新结算时间 > 当前记录时才更新（允许同一周期多次推送相同值）
      if (nextFundingTime > 0 &&
          nextFundingTime > DateTime.now().millisecondsSinceEpoch &&
          nextFundingTime > _nextSettleTimeMs) {
        _nextSettleTimeMs = nextFundingTime;
        _startFundingCountdown();
      }
      state = state.copyWith(fundingRate: fundingRate);
    });

    _tickerSub = ws.tickerStream.listen((data) {
      final s = (data['symbol'] as String? ?? '').toLowerCase();
      final price = (data['price'] as num?)?.toDouble() ?? 0;
      if (price <= 0) return;
      final priceStr = data['priceStr'] as String? ?? '';

      final now = DateTime.now().millisecondsSinceEpoch;
      final isCurrentSymbol = s == wsSymbol;

      // 非当前交易对节流 500ms
      if (!isCurrentSymbol) {
        final last = _lastTickerUpdateMs[s] ?? 0;
        if (now - last < 500) return;
      }
      _lastTickerUpdateMs[s] = now;

      final tickerApiSymbol = _toApiSymbol(s.toUpperCase());
      final tSym = tickerApiSymbol.replaceAll('/', '').toUpperCase();

      final hasMatch = state.positions.any(
          (p) => p.symbol.replaceAll('/', '').toUpperCase() == tSym);

      // 持仓 mark price 更新节流 1000ms，避免每次 WS 推送都重建 positions 数组
      final canUpdatePositions =
          now - _lastPositionTickUpdateMs >= 1000;

      if (isCurrentSymbol) {
        final change = (data['change24h'] as num?)?.toDouble() ?? 0;
        if (hasMatch && canUpdatePositions) {
          _lastPositionTickUpdateMs = now;
          final updatedPositions = state.positions.map((p) {
            return p.symbol.replaceAll('/', '').toUpperCase() == tSym
                ? p.withMarkPrice(price)
                : p;
          }).toList();
          final totalPnl =
              updatedPositions.fold(0.0, (sum, p) => sum + p.unrealizedPnl);
          final ai = state.accountInfo;
          state = state.copyWith(
            lastPrice: price,
            lastPriceStr: priceStr.isNotEmpty ? priceStr : null,
            markPrice: price,
            change24h: change,
            positions: updatedPositions,
            accountInfo: FuturesAccountInfo(
              totalBalance: ai.totalBalance,
              usedMargin: ai.usedMargin,
              availableMargin: ai.availableMargin,
              unrealizedPnl: totalPnl,
            ),
          );
        } else {
          state = state.copyWith(
            lastPrice: price,
            lastPriceStr: priceStr.isNotEmpty ? priceStr : null,
            markPrice: price,
            change24h: change,
          );
        }
      } else if (hasMatch && canUpdatePositions) {
        _lastPositionTickUpdateMs = now;
        final updatedPositions = state.positions.map((p) {
          return p.symbol.replaceAll('/', '').toUpperCase() == tSym
              ? p.withMarkPrice(price)
              : p;
        }).toList();
        final totalPnl =
            updatedPositions.fold(0.0, (sum, p) => sum + p.unrealizedPnl);
        final ai = state.accountInfo;
        state = state.copyWith(
          positions: updatedPositions,
          accountInfo: FuturesAccountInfo(
            totalBalance: ai.totalBalance,
            usedMargin: ai.usedMargin,
            availableMargin: ai.availableMargin,
            unrealizedPnl: totalPnl,
          ),
        );
      }
    });

    _depthSub = ws.orderBookStream.listen((data) {
      final s = (data['symbol'] as String? ?? '').toLowerCase();
      if (s != wsSymbol) return;

      // 盘口节流 120ms
      final now = DateTime.now().millisecondsSinceEpoch;
      if (now - _lastDepthUpdateMs < 120) return;
      _lastDepthUpdateMs = now;

      final rawAsks = data['asks'] as List? ?? [];
      final rawBids = data['bids'] as List? ?? [];
      _pendingAsks = rawAsks.whereType<Map<String, dynamic>>().toList();
      _pendingBids = rawBids.whereType<Map<String, dynamic>>().toList();
      if (_depthTimer == null || !_depthTimer!.isActive) {
        _depthTimer = Timer(Duration.zero, () {
          final asks = _pendingAsks;
          final bids = _pendingBids;
          _pendingAsks = null;
          _pendingBids = null;
          if (asks != null && bids != null) {
            state = state.copyWith(orderBookAsks: asks, orderBookBids: bids);
          }
        });
      }
    });
  }

  void _subscribePositionTickers(List<FuturesPosition> positions) {
    if (positions.isEmpty) return;
    try {
      final ws = ref.read(wsClientProvider);
      final symbols = positions
          .map((p) => p.symbol.replaceAll('/', '').toLowerCase())
          .toSet()
          .toList();
      ws.subscribeTickerBatch(symbols);
    } catch (_) {}
  }

  void _startFundingCountdown() {
    _fundingTimer?.cancel();
    _updateFundingCountdown();
    _fundingTimer = Timer.periodic(const Duration(seconds: 1), (_) {
      _updateFundingCountdown();
    });
  }

  void _updateFundingCountdown() {
    final remaining = _nextSettleTimeMs - DateTime.now().millisecondsSinceEpoch;
    if (remaining <= 0) {
      state = state.copyWith(fundingCountdown: '00:00:00');
      _fundingTimer?.cancel();
      // 下次结算时间由 WS mark 推送更新，无需 HTTP 拉取
      return;
    }
    final secs = remaining ~/ 1000;
    final h = secs ~/ 3600;
    final m = (secs % 3600) ~/ 60;
    final s = secs % 60;
    state = state.copyWith(
      fundingCountdown:
          '${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}',
    );
  }

  void _dispose() {
    _tickerSub?.cancel();
    _tickerSub = null;
    _depthSub?.cancel();
    _depthSub = null;
    _markSub?.cancel();
    _markSub = null;
    _fundingTimer?.cancel();
    _fundingTimer = null;
    _pollTimer?.cancel();
    _pollTimer = null;
    _depthTimer?.cancel();
    _depthTimer = null;
  }

  void setOrderSide(OrderSide side) =>
      state = state.copyWith(orderSide: side);

  void setOrderType(OrderType type) =>
      state = state.copyWith(orderType: type);

  void setPositionMode(PositionMode mode) {
    final isConditional = state.orderType == OrderType.conditionalMarket ||
        state.orderType == OrderType.conditionalLimit;
    if (mode == PositionMode.close && isConditional) {
      // 平仓不支持计划委托：暂存类型，临时切换为市价
      _savedConditionalType = state.orderType;
      state = state.copyWith(positionMode: mode, orderType: OrderType.market);
    } else if (mode == PositionMode.open && _savedConditionalType != null) {
      // 切回开仓：恢复之前的计划委托类型
      final restored = _savedConditionalType!;
      _savedConditionalType = null;
      state = state.copyWith(positionMode: mode, orderType: restored);
    } else {
      state = state.copyWith(positionMode: mode);
    }
  }

  /// 切换仓位模式。
  /// 全仓/分仓：必须等后端 modifyType 成功后才更新本地状态
  /// 返回 null 表示成功；返回错误消息字符串表示失败（调用方负责展示）
  Future<String?> setMarginMode(MarginMode mode) async {
    // 全仓/分仓：先调后端，成功后再更新本地状态；期间禁止下单
    final serverType = mode == MarginMode.split ? 1 : 0;
    state = state.copyWith(isSwitchingMode: true);
    try {
      await _service.modifyPositionType(serverType);
      state = state.copyWith(
        marginMode: mode,
        isSwitchingMode: false,
      );
      // 持久化该交易对的保证金模式，下次切回时自动恢复
      final modeStr = mode == MarginMode.split ? 'split'
          : mode == MarginMode.isolated ? 'isolated'
          : 'cross';
      ref.read(sharedPreferencesProvider)
          .setString('futures_margin_mode_${state.symbol}', modeStr);
      // 切换回普通模式后，重新从服务器获取当前杠杆倍数
      if (state.contractCoinId > 0) {
        try {
          final lev = await _service.getLeverage(state.contractCoinId);
          state = state.copyWith(leverage: lev.toDouble());
        } catch (_) {}
      }
      return null;
    } catch (e) {
      // 切换失败：重新拉取真实状态，保持原有 marginMode 不变
      state = state.copyWith(isSwitchingMode: false);
      await _loadWallet(state.symbol);
      final msg = e.toString();
      return msg;
    }
  }

  void setAmountUnit(AmountUnit unit) {
    state = state.copyWith(amountUnit: unit);
      ref.read(futuresAmountUnitPrefProvider.notifier).state = unit;
  }

  void toggleHideOtherSymbols() {
    final next = !state.hideOtherSymbols;
    state = state.copyWith(hideOtherSymbols: next);
    ref.read(futuresHideOtherPrefProvider.notifier).state = next;
  }

  void setTab(FuturesTab tab) {
    state = state.copyWith(activeTab: tab);
    _loadWallet(state.symbol);
    _loadCurrentOrders();
  }

  void setSliderPercent(double pct) =>
      state = state.copyWith(sliderPercent: pct, isSliderInput: true);

  void setSliderPercentFromInput(double pct) =>
      state = state.copyWith(sliderPercent: pct, isSliderInput: false);

  void toggleTpsl() =>
      state = state.copyWith(tpslEnabled: !state.tpslEnabled);

  String _errMsg(Object e) {
    if (e is ApiException) return e.message;

    if (e is DioException) {
      final inner = e.error;
      if (inner is ApiException) return inner.message;

      final data = e.response?.data;
      if (data is Map) {
        final msg = data['message'] as String? ?? data['msg'] as String?;
        if (msg != null && msg.isNotEmpty) return msg;
      }

      final errStr = inner?.toString() ?? '';
      if (errStr.isNotEmpty) {
        final m = RegExp(r'ApiException\(\d+\):\s*(.+?)[\r\n]?$', multiLine: true)
            .firstMatch(errStr);
        if (m != null) return m.group(1)!.trim();
      }

      final dioStr = e.toString();
      final dm = RegExp(r'Error:\s*ApiException\(\d+\):\s*(.+?)[\r\n]?$', multiLine: true)
          .firstMatch(dioStr);
      if (dm != null) return dm.group(1)!.trim();

      if (e.type == DioExceptionType.connectionTimeout ||
          e.type == DioExceptionType.receiveTimeout ||
          e.type == DioExceptionType.sendTimeout) {
        return 'errTimeout';
      }
      if (e.type == DioExceptionType.connectionError) {
        return 'errNetworkError';
      }
    }

    final s = e.toString();
    final m = RegExp(r'ApiException\(\d+\):\s*(.+?)[\r\n]?$', multiLine: true)
        .firstMatch(s);
    if (m != null) return m.group(1)!.trim();

    final lines = s.split('\n');
    for (final line in lines.reversed) {
      final t = line.trim();
      if (t.isEmpty || t.startsWith('Uri:') || t.startsWith('DioException')) continue;
      final idx = t.lastIndexOf(': ');
      if (idx != -1 && idx < t.length - 2) return t.substring(idx + 2).trim();
      return t;
    }
    return s;
  }

  Future<String?> setLeverage(double lev) async {
    if (state.contractCoinId <= 0) {
      await _loadContractInfo(state.symbol);
      if (state.contractCoinId <= 0) return 'errServiceUnavailable';
    }
    try {
      await _service.modifyLeverage(state.contractCoinId, lev.toInt());
      state = state.copyWith(leverage: lev);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> placeOpenOrder({
    OrderSide? side,
    double? entrustPrice,
    double? triggerPrice,
    double? volume,
    double? tpPrice,
    double? slPrice,
  }) async {
    // 模式切换 API 尚未完成，禁止下单，防止 positionType 不一致导致仓位错误
    if (state.isSwitchingMode) return 'errSwitchingMode';

    if (state.contractCoinId == 0) {
      await _loadContractInfo(state.symbol);
      if (state.contractCoinId == 0) return 'errServiceUnavailable';
    }
    final vol = volume ?? state.inputSize;
    if (vol <= 0) return 'errEnterVolume';

    final int type;
    final double? ep;
    switch (state.orderType) {
      case OrderType.market:
        type = 0;
        ep = null;
      case OrderType.limit:
        type = 1;
        ep = entrustPrice ?? state.inputPrice;
        if (ep <= 0) return 'errEnterPrice';
      case OrderType.conditionalMarket:
        type = 2;
        ep = null; // 计划市价不传委托价，后端以 entrustPrice=0 标识市价
        if ((triggerPrice ?? 0) <= 0) return 'errEnterTriggerPrice';
      case OrderType.conditionalLimit:
        type = 2;
        ep = entrustPrice;
        if ((triggerPrice ?? 0) <= 0) return 'errEnterTriggerPrice';
    }

    final dir = (side ?? state.orderSide) == OrderSide.long ? 0 : 1;

    // 滑块模式：市价/限价传百分比给后端；计划委托不支持百分比，需换算为实际张数
    if (state.isSliderInput && state.sliderPercent > 0.0001 && type != 2) {
      final pct = (state.sliderPercent * 100).round().clamp(1, 100);
      try {
        await _service.openOrder(
          contractCoinId: state.contractCoinId,
          type: type,
          direction: dir,
          volume: pct.toDouble(),
          leverage: state.leverage.toInt(),
          entrustPrice: ep,
          triggerPrice: triggerPrice,
          profitPrice: tpPrice ?? state.tpPrice,
          lossPrice: slPrice ?? state.slPrice,
          isPercentage: 1,
          positionType: state.marginMode == MarginMode.split ? 1 : 0,
        );
        await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
        Future.delayed(const Duration(seconds: 2), () => _loadWallet(state.symbol));
        return null;
      } catch (e) {
        return _errMsg(e);
      }
    }

    if (state.contractSize <= 0) return 'errContractNotReady';

    // 计划市价用触发价作为换算基准，计划限价用委托价，普通单用委托价/最新价
    final double effectivePrice;
    if (state.orderType == OrderType.conditionalMarket) {
      effectivePrice = (triggerPrice != null && triggerPrice > 0)
          ? triggerPrice
          : state.lastPrice;
    } else {
      effectivePrice = ep ?? (state.lastPrice > 0 ? state.lastPrice : 0);
    }
    if (effectivePrice <= 0) return 'errPriceNotReady';
    final double volumeInBtc;
    switch (state.amountUnit) {
      case AmountUnit.lots:
        volumeInBtc = vol * state.contractSize / effectivePrice;
      case AmountUnit.usdt:
        volumeInBtc = vol / effectivePrice;
      case AmountUnit.btc:
        volumeInBtc = vol;
    }
    final factor = math.pow(10, state.coinPrecision).toDouble();
    final double finalVolume = (volumeInBtc * factor).floorToDouble() / factor;
    if (finalVolume <= 0) return 'errVolumeInsufficient';

    try {
      await _service.openOrder(
        contractCoinId: state.contractCoinId,
        type: type,
        direction: dir,
        volume: finalVolume,
        leverage: state.leverage.toInt(),
        entrustPrice: ep,
        triggerPrice: triggerPrice,
        profitPrice: tpPrice ?? state.tpPrice,
        lossPrice: slPrice ?? state.slPrice,
        positionType: state.marginMode == MarginMode.split ? 1 : 0,
      );
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      Future.delayed(const Duration(seconds: 2), () => _loadWallet(state.symbol));
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  /// 市价平仓；volume 不传或 <=0 时全仓平
  Future<String?> closeMarket(FuturesPosition position, {double? volume}) async {
    try {
      final vol = (volume != null && volume > 0)
          ? volume.clamp(0.0, position.availableSize)
          : position.availableSize;
      await _service.closeMarket(
        positionId: position.id,
        volume: vol,
      );
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  /// 限价平仓；volume 不传或 <=0 时全仓平
  Future<String?> closeLimit(FuturesPosition position, double price, {double? volume}) async {
    if (price <= 0) return 'errEnterClosePrice';
    try {
      final vol = (volume != null && volume > 0)
          ? volume.clamp(0.0, position.availableSize)
          : position.availableSize;
      await _service.closeLimit(
        positionId: position.id,
        price: price,
        volume: vol,
      );
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> cancelOrder(FuturesOrder order) async {
    final id = int.tryParse(order.id);
    if (id == null) return 'errInvalidOrderId';
    try {
      await _service.cancelOrder(id);
      await _loadCurrentOrders();
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> closeAllPositions() async {
    try {
      await _service.closeAllPositions();
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  // price 为 null/0 时市价，否则限价
  Future<String?> closeByDirection(OrderSide side, {double? price, double? volume}) async {
    final normSymbol = FuturesState._norm(_toApiSymbol(state.symbol));
    final pos = state.positions.where(
      (p) => p.side == side && FuturesState._norm(p.symbol) == normSymbol,
    ).firstOrNull;
    if (pos == null) return side == OrderSide.long ? 'errNoLongPosition' : 'errNoShortPosition';
    final closeVol = (volume != null && volume > 0)
        ? volume.clamp(0.0, pos.availableSize)
        : pos.availableSize;
    if (price != null && price > 0) {
      return _closeLimitVolume(pos, price, closeVol);
    } else {
      return _closeMarketVolume(pos, closeVol);
    }
  }

  Future<String?> closeConditionalByDirection(
    OrderSide side, {
    required double triggerPrice,
    double? volume,
    double entrustPrice = 0,
  }) async {
    final normSymbol = FuturesState._norm(_toApiSymbol(state.symbol));
    final pos = state.positions.where(
      (p) => p.side == side && FuturesState._norm(p.symbol) == normSymbol,
    ).firstOrNull;
    if (pos == null) return side == OrderSide.long ? 'errNoLongPosition' : 'errNoShortPosition';

    final closeVol = (volume != null && volume > 0)
        ? volume.clamp(0.0, pos.availableSize)
        : pos.availableSize;

    try {
      await _service.closeConditional(
        positionId: pos.id,
        triggerPrice: triggerPrice,
        volume: closeVol,
        entrustPrice: entrustPrice,
      );
      await _loadCurrentOrders();
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> _closeMarketVolume(FuturesPosition position, double volume) async {
    try {
      await _service.closeMarket(positionId: position.id, volume: volume);
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> _closeLimitVolume(FuturesPosition position, double price, double volume) async {
    if (price <= 0) return 'errEnterClosePrice';
    try {
      await _service.closeLimit(positionId: position.id, price: price, volume: volume);
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> cancelAllOrders() async {
    final ids = state.openOrders
        .map((o) => int.tryParse(o.id))
        .whereType<int>()
        .toList();
    if (ids.isEmpty) return 'errNoOrdersToCancel';
    try {
      await _service.cancelOrdersByIds(ids);
      await _loadCurrentOrders();
      return null;
    } catch (e) {
      await _loadCurrentOrders();
      return _errMsg(e);
    }
  }

  Future<String?> setPositionTpsl(
    FuturesPosition position, {
    double? profitPrice,
    double? lossPrice,
  }) async {
    try {
      if (position.cutEntrustId != null) {
        // 已有止盈止损委托单 → 修改
        await _service.modifyCutOrder(
          entrustId: position.cutEntrustId!,
          profitPrice: profitPrice,
          lossPrice: lossPrice,
        );
      } else {
        // 首次设置，成功后刷新持仓以获取 cutEntrustId
        await _service.setTpsl(
          positionId: position.id,
          profitPrice: profitPrice,
          lossPrice: lossPrice,
        );
        await _loadWallet(state.symbol);
      }
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<String?> reversePosition(FuturesPosition position) async {
    try {
      await _service.reverseOpenPosition(position.id);
      await Future.wait([_loadWallet(state.symbol), _loadCurrentOrders()]);
      return null;
    } catch (e) {
      return _errMsg(e);
    }
  }

  Future<void> refresh() async {
    // 下拉刷新只更新数据，不触发 isLoading（骨架屏仅用于首屏）
    await Future.wait([
      _loadWallet(state.symbol),
      _loadCurrentOrders(),
    ]);
  }

  FuturesService get _service =>
      FuturesService(ref.read(dioClientProvider));
}

final futuresProvider =
    AutoDisposeNotifierProviderFamily<FuturesNotifier, FuturesState, String>(
  FuturesNotifier.new,
);

final futuresAmountUnitPrefProvider =
    StateProvider<AmountUnit>((ref) => AmountUnit.btc);

final futuresHideOtherPrefProvider = StateProvider<bool>((ref) => false);

final futuresActiveSymbolProvider = StateProvider<String>((ref) => '');

/// 当前底部导航栏选中的 tab 索引（0=首页, 1=行情, 2=交易, 3=合约, 4=跟单, 5=资产）
final activeBottomTabProvider = StateProvider<int>((ref) => 0);

/// 上次访问的交易路径（现货 /spot/:sym 或合约 /futures/:sym），用于恢复交易 tab
final lastTradingRouteProvider = StateProvider<String>((ref) => '/spot/BTCUSDT');

double _toDouble(dynamic v) {
  if (v == null) return 0.0;
  if (v is num) return v.toDouble();
  return double.tryParse(v.toString()) ?? 0.0;
}

/// 预计强平价：直接使用后端返回值，<=0 或 -1 均视为无效（显示 '--'）
double _calcLiquidationPrice({required double apiValue}) {
  return apiValue > 0 ? apiValue : 0.0;
}
