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

import '../../../core/l10n/app_localizations.dart';
import '../../../core/network/dio_client.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/utils/number_format.dart';
import '../../../data/services/spot_service.dart';
import '../../widgets/common/app_tab_bar.dart';
import '../../../providers/spot_provider.dart';
import '../../../providers/spot_symbol_cache_provider.dart';

/// 现货历史记录：历史委托 / 历史成交
class SpotHistoryScreen extends ConsumerStatefulWidget {
  const SpotHistoryScreen({super.key, required this.symbol});
  final String symbol;

  @override
  ConsumerState<SpotHistoryScreen> createState() => _SpotHistoryScreenState();
}

class _SpotHistoryScreenState extends ConsumerState<SpotHistoryScreen>
    with SingleTickerProviderStateMixin {
  late TabController _tabCtrl;
  late PageController _pageCtrl;

  static const int _pageSize = 20;
  static const int _maxSkipEmptyPages = 12;

  _KindFilter _kind = _KindFilter.all;
  _StatusFilter _status = _StatusFilter.all;
  _TimeFilter _time = _TimeFilter.all;
  /// true：仅当前路由交易对；false：全部交易对
  bool _useCurrentSymbolOnly = false;
  /// 是否已在交易对筛选里点选过；未点选时 pill 展示原型「交易对」
  bool _pairFilterTouched = false;

  final _ordersScroll = ScrollController();
  final List<SpotOrder> _orders = [];
  int _nextOrdersPage = 1;
  bool _ordersHasMore = true;
  bool _ordersLoading = false;
  int _ordersRawLoaded = 0;

  final _tradesScroll = ScrollController();
  final List<SpotOrder> _trades = [];
  int _nextTradesPage = 1;
  bool _tradesHasMore = true;
  bool _tradesLoading = false;
  int _tradesRawLoaded = 0;

  @override
  void initState() {
    super.initState();
    _tabCtrl = TabController(length: 2, vsync: this);
    _pageCtrl = PageController();
    // Tab 点击 → 驱动 PageView 动画（与合约历史页一致）
    _tabCtrl.addListener(() {
      if (!mounted) return;
      if (!_tabCtrl.indexIsChanging) return;
      _pageCtrl.animateToPage(
        _tabCtrl.index,
        duration: const Duration(milliseconds: 280),
        curve: Curves.easeOut,
      );
    });
    // 横向滑动 PageView → 指示器 offset 平滑插值
    _pageCtrl.addListener(() {
      if (!mounted) return;
      if (!_pageCtrl.hasClients) return;
      if (_tabCtrl.indexIsChanging) return;
      final page = _pageCtrl.page!;
      final offset = page - _tabCtrl.index;
      if (offset.abs() <= 1.0) {
        _tabCtrl.offset = offset.clamp(-1.0, 1.0);
      }
    });
    _ordersScroll.addListener(_onOrdersScroll);
    _tradesScroll.addListener(_onTradesScroll);
    _refreshOrders();
  }

  void _resetFiltersToDefault() {
    _kind = _KindFilter.all;
    _status = _StatusFilter.all;
    _time = _TimeFilter.all;
    _useCurrentSymbolOnly = false;
    _pairFilterTouched = false;
  }

  /// PageView 切换完成：重置筛选并刷新当前页数据
  void _onPageCommitted(int index) {
    if (!mounted) return;
    _resetFiltersToDefault();
    if (index == 0) {
      if (_ordersScroll.hasClients) {
        _ordersScroll.jumpTo(0);
      }
      _refreshOrders();
    } else {
      if (_tradesScroll.hasClients) {
        _tradesScroll.jumpTo(0);
      }
      _refreshTrades();
    }
  }

  void _onOrdersScroll() {
    if (_ordersScroll.position.pixels >=
        _ordersScroll.position.maxScrollExtent - 200) {
      _loadMoreOrders();
    }
  }

  void _onTradesScroll() {
    if (_tradesScroll.position.pixels >=
        _tradesScroll.position.maxScrollExtent - 200) {
      _loadMoreTrades();
    }
  }

  @override
  void dispose() {
    _tabCtrl.dispose();
    _pageCtrl.dispose();
    _ordersScroll.dispose();
    _tradesScroll.dispose();
    super.dispose();
  }

  String get _apiSymbol => widget.symbol
      .replaceAll('/', '')
      .replaceAll('-', '')
      .toUpperCase();

  /// 交易对 pill：未选过展示「交易对」；选过后展示「全部交易对」或具体币对
  String _pairChipDisplay(AppLocalizations l10n) {
    if (!_pairFilterTouched) return l10n.spotHistoryFilterSymbol;
    return _useCurrentSymbolOnly ? _apiSymbol : l10n.spotFilterSymbolAllPairs;
  }

  /// 委托类型：全部时展示「全部委托」，否则展示市价/限价（与原型一致）
  String _kindChipDisplay(AppLocalizations l10n) {
    switch (_kind) {
      case _KindFilter.all:
        return l10n.spotFilterEntrustAll;
      case _KindFilter.market:
        return l10n.spotFilterKindMarket;
      case _KindFilter.limit:
        return l10n.spotFilterKindLimit;
    }
  }

  String _statusChipDisplay(AppLocalizations l10n) {
    switch (_status) {
      case _StatusFilter.all:
        return l10n.spotHistoryFilterStatus;
      case _StatusFilter.completed:
        return l10n.spotOrderStatusCompleted;
      case _StatusFilter.partial:
        return l10n.spotOrderStatusPartialFilled;
      case _StatusFilter.cancelled:
        return l10n.spotOrderStatusCancelled;
    }
  }

  String _timeChipDisplay(AppLocalizations l10n) {
    switch (_time) {
      case _TimeFilter.all:
        return l10n.spotHistoryFilterTime;
      case _TimeFilter.d7:
        return l10n.spotFilterTime7d;
      case _TimeFilter.d30:
        return l10n.spotFilterTime30d;
    }
  }

  int? get _ctimeBeginMs {
    final now = DateTime.now();
    switch (_time) {
      case _TimeFilter.all:
        return null;
      case _TimeFilter.d7:
        return now.subtract(const Duration(days: 7)).millisecondsSinceEpoch;
      case _TimeFilter.d30:
        return now.subtract(const Duration(days: 30)).millisecondsSinceEpoch;
    }
  }

  /// 历史委托 Tab：传 type 给服务端（并做客户端二次过滤）
  int? _apiTypeOrders() {
    switch (_kind) {
      case _KindFilter.all:
        return null;
      case _KindFilter.limit:
        return 1;
      case _KindFilter.market:
        return 2;
    }
  }

  /// 历史成交 Tab：不按委托类型筛（与原型一致），仅客户端可按状态/时间筛
  int? _apiTypeTrades() => null;

  int? _apiStatus() {
    switch (_status) {
      case _StatusFilter.all:
      case _StatusFilter.partial:
        return null;
      case _StatusFilter.completed:
        return 2;
      case _StatusFilter.cancelled:
        return 4;
    }
  }

  bool _matchesFilters(SpotOrder o, {required bool tradesTab}) {
    if (_useCurrentSymbolOnly) {
      final os = o.symbol.replaceAll('/', '').toUpperCase();
      if (os != _apiSymbol) return false;
    }

    if (!tradesTab) {
      switch (_kind) {
        case _KindFilter.all:
          break;
        case _KindFilter.limit:
          if (o.typeCode != 1) return false;
        case _KindFilter.market:
          if (o.typeCode != 2) return false;
      }
    }

    switch (_status) {
      case _StatusFilter.all:
        break;
      case _StatusFilter.completed:
        if (o.statusCode != 2) return false;
      case _StatusFilter.partial:
        final isPartial = o.statusCode == 3 ||
            ((o.statusCode == 4 || o.statusCode == 5 || o.statusCode == 6) &&
                o.tradedAmount > 0 &&
                o.tradedAmount < o.amount);
        if (!isPartial) return false;
      case _StatusFilter.cancelled:
        final partial = o.tradedAmount > 0 && o.tradedAmount < o.amount;
        final isCancelState =
            o.statusCode == 4 || o.statusCode == 5 || o.statusCode == 6;
        if (!isCancelState || partial) return false;
    }

    final begin = _ctimeBeginMs;
    if (begin != null) {
      final t = o.createTime;
      if (t != null && t.millisecondsSinceEpoch < begin) return false;
    }
    return true;
  }

  Future<void> _refreshOrders() async {
    setState(() {
      _ordersLoading = true;
      _nextOrdersPage = 1;
      _ordersHasMore = true;
      _orders.clear();
      _ordersRawLoaded = 0;
    });
    await _fetchOrdersPage();
  }

  Future<void> _loadMoreOrders() async {
    if (_ordersLoading || !_ordersHasMore) return;
    setState(() => _ordersLoading = true);
    await _fetchOrdersPage();
  }

  Future<void> _fetchOrdersPage() async {
    try {
      final svc = SpotService(ref.read(dioClientProvider));
      int skipped = 0;
      while (skipped <= _maxSkipEmptyPages && _ordersHasMore) {
        final data = await svc.getHistoryOrders(
          page: _nextOrdersPage,
          size: _pageSize,
          symbol: _useCurrentSymbolOnly ? _apiSymbol : null,
          type: _apiTypeOrders(),
          status: _apiStatus(),
          ctimeBegin: _ctimeBeginMs,
        );
        final list = _extractRecordMaps(data);
        final total = _parsePageTotal(data);
        if (list.isEmpty) {
          _ordersHasMore = false;
          break;
        }
        _ordersRawLoaded += list.length;
        final filtered = list
            .map(SpotOrder.fromJson)
            .where((o) => _matchesFilters(o, tradesTab: false))
            .toList();
        _orders.addAll(filtered);
        _nextOrdersPage++;
        _ordersHasMore = total > _ordersRawLoaded ||
            (total == 0 && list.length >= _pageSize);
        if (filtered.isNotEmpty || !_ordersHasMore) break;
        skipped++;
      }
    } catch (_) {
      _ordersHasMore = false;
    } finally {
      if (mounted) setState(() => _ordersLoading = false);
    }
  }

  Future<void> _refreshTrades() async {
    setState(() {
      _tradesLoading = true;
      _nextTradesPage = 1;
      _tradesHasMore = true;
      _trades.clear();
      _tradesRawLoaded = 0;
    });
    await _fetchTradesPage();
  }

  Future<void> _loadMoreTrades() async {
    if (_tradesLoading || !_tradesHasMore) return;
    setState(() => _tradesLoading = true);
    await _fetchTradesPage();
  }

  Future<void> _fetchTradesPage() async {
    try {
      final svc = SpotService(ref.read(dioClientProvider));
      int skipped = 0;
      while (skipped <= _maxSkipEmptyPages && _tradesHasMore) {
        final data = await svc.getTrades(
          page: _nextTradesPage,
          size: _pageSize,
          symbol: _useCurrentSymbolOnly ? _apiSymbol : null,
          type: _apiTypeTrades(),
          status: _apiStatus(),
          ctimeBegin: _ctimeBeginMs,
        );
        final list = _extractRecordMaps(data);
        final total = _parsePageTotal(data);
        if (list.isEmpty) {
          _tradesHasMore = false;
          break;
        }
        _tradesRawLoaded += list.length;
        final filtered = list
            .map(SpotOrder.fromJson)
            .where((o) => _matchesFilters(o, tradesTab: true))
            .toList();
        _trades.addAll(filtered);
        _nextTradesPage++;
        _tradesHasMore = total > _tradesRawLoaded ||
            (total == 0 && list.length >= _pageSize);
        if (filtered.isNotEmpty || !_tradesHasMore) break;
        skipped++;
      }
    } catch (_) {
      _tradesHasMore = false;
    } finally {
      if (mounted) setState(() => _tradesLoading = false);
    }
  }

  Future<void> _applyFilters() async {
    await Future.wait([_refreshOrders(), _refreshTrades()]);
  }

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;
    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.spotHistoryRecordsTitle),
        bottom: AppTabBar(
          controller: _tabCtrl,
          tabs: [
            Tab(text: l10n.spotHistoryTitle),
            Tab(text: l10n.spotHistoryTradesTab),
          ],
        ),
      ),
      body: PageView(
        controller: _pageCtrl,
        physics: const BouncingScrollPhysics(
          parent: AlwaysScrollableScrollPhysics(),
        ),
        onPageChanged: (index) {
          if (!mounted) return;
          if (!_tabCtrl.indexIsChanging) {
            _tabCtrl.index = index;
          }
          _onPageCommitted(index);
        },
        children: [
          _OrdersTabBody(
            scrollController: _ordersScroll,
            orders: _orders,
            loading: _ordersLoading,
            hasMore: _ordersHasMore,
            onRefresh: _refreshOrders,
            tradesMode: false,
            pairText: _pairChipDisplay(l10n),
            kindText: _kindChipDisplay(l10n),
            statusText: _statusChipDisplay(l10n),
            timeText: _timeChipDisplay(l10n),
            onPickSymbol: () => _pickSymbol(l10n),
            onPickKind: () => _pickKind(l10n),
            onPickStatus: () => _pickStatus(l10n),
            onPickTime: () => _pickTime(l10n),
          ),
          _OrdersTabBody(
            scrollController: _tradesScroll,
            orders: _trades,
            loading: _tradesLoading,
            hasMore: _tradesHasMore,
            onRefresh: _refreshTrades,
            tradesMode: true,
            pairText: _pairChipDisplay(l10n),
            kindText: null,
            statusText: _statusChipDisplay(l10n),
            timeText: _timeChipDisplay(l10n),
            onPickSymbol: () => _pickSymbol(l10n),
            onPickKind: null,
            onPickStatus: () => _pickStatus(l10n),
            onPickTime: () => _pickTime(l10n),
          ),
        ],
      ),
    );
  }

  Future<void> _pickSymbol(AppLocalizations l10n) async {
    final cs = Theme.of(context).colorScheme;
    final v = await showModalBottomSheet<bool>(
      context: context,
      useRootNavigator: true,
      backgroundColor: cs.surface,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (ctx) => _SimpleSheet<bool>(
        selected: _useCurrentSymbolOnly,
        options: [
          (false, l10n.spotFilterSymbolAllPairs),
          (true, _apiSymbol),
        ],
      ),
    );
    if (v != null && mounted) {
      setState(() {
        _pairFilterTouched = true;
        _useCurrentSymbolOnly = v;
      });
      await _applyFilters();
    }
  }

  Future<void> _pickKind(AppLocalizations l10n) async {
    final cs = Theme.of(context).colorScheme;
    final v = await showModalBottomSheet<_KindFilter>(
      context: context,
      useRootNavigator: true,
      backgroundColor: cs.surface,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (ctx) => _SimpleSheet<_KindFilter>(
        selected: _kind,
        options: [
          (_KindFilter.all, l10n.spotFilterKindAll),
          (_KindFilter.market, l10n.spotFilterKindMarket),
          (_KindFilter.limit, l10n.spotFilterKindLimit),
        ],
      ),
    );
    if (v != null && mounted) {
      setState(() => _kind = v);
      await _applyFilters();
    }
  }

  Future<void> _pickStatus(AppLocalizations l10n) async {
    final cs = Theme.of(context).colorScheme;
    final v = await showModalBottomSheet<_StatusFilter>(
      context: context,
      useRootNavigator: true,
      backgroundColor: cs.surface,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (ctx) => _SimpleSheet<_StatusFilter>(
        selected: _status,
        options: [
          (_StatusFilter.all, l10n.spotFilterStatusAll),
          (_StatusFilter.completed, l10n.spotOrderStatusCompleted),
          (_StatusFilter.partial, l10n.spotOrderStatusPartialFilled),
          (_StatusFilter.cancelled, l10n.spotOrderStatusCancelled),
        ],
      ),
    );
    if (v != null && mounted) {
      setState(() => _status = v);
      await _applyFilters();
    }
  }

  Future<void> _pickTime(AppLocalizations l10n) async {
    final cs = Theme.of(context).colorScheme;
    final v = await showModalBottomSheet<_TimeFilter>(
      context: context,
      useRootNavigator: true,
      backgroundColor: cs.surface,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (ctx) => _SimpleSheet<_TimeFilter>(
        selected: _time,
        options: [
          (_TimeFilter.all, l10n.spotFilterTimeAll),
          (_TimeFilter.d7, l10n.spotFilterTime7d),
          (_TimeFilter.d30, l10n.spotFilterTime30d),
        ],
      ),
    );
    if (v != null && mounted) {
      setState(() => _time = v);
      await _applyFilters();
    }
  }
}

List<Map<String, dynamic>> _extractRecordMaps(Map<String, dynamic> data) {
  final raw = data['records'] ?? data['list'];
  if (raw is! List) return [];
  return raw.whereType<Map<String, dynamic>>().toList();
}

int _parsePageTotal(Map<String, dynamic> data) {
  final t = data['total'] ?? data['totalRow'] ?? data['totalCount'];
  if (t is int) return t;
  if (t is num) return t.toInt();
  return 0;
}

enum _KindFilter { all, market, limit }
enum _StatusFilter { all, completed, partial, cancelled }
enum _TimeFilter { all, d7, d30 }

/// 选项样式与合约页「仓位模式」底部弹窗一致：圆角描边、选中加粗 + 对勾
class _SimpleSheet<T> extends StatelessWidget {
  const _SimpleSheet({
    super.key,
    required this.selected,
    required this.options,
  });
  final T selected;
  final List<(T, String)> options;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return SafeArea(
      child: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              for (var i = 0; i < options.length; i++) ...[
              if (i > 0) const SizedBox(height: 10),
              GestureDetector(
                onTap: () => Navigator.pop(context, options[i].$1),
                child: Container(
                  width: double.infinity,
                  padding:
                      const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
                  decoration: BoxDecoration(
                    border: Border.all(
                      color: options[i].$1 == selected
                          ? cs.onSurface
                          : cs.outline,
                      width: options[i].$1 == selected ? 1.5 : 1,
                    ),
                    borderRadius: BorderRadius.circular(10),
                    color: Colors.transparent,
                  ),
                  child: Row(
                    children: [
                      Expanded(
                        child: Text(
                          options[i].$2,
                          style: TextStyle(
                            color: cs.onSurface,
                            fontWeight: FontWeight.w600,
                            fontSize: 14,
                          ),
                        ),
                      ),
                      if (options[i].$1 == selected)
                        Icon(Icons.check_circle,
                            size: 16, color: cs.onSurface),
                    ],
                  ),
                ),
              ),
            ],
          ],
        ),
      ),
    ),
  );
  }
}

class _OrdersTabBody extends StatelessWidget {
  const _OrdersTabBody({
    required this.scrollController,
    required this.orders,
    required this.loading,
    required this.hasMore,
    required this.onRefresh,
    required this.tradesMode,
    required this.pairText,
    this.kindText,
    required this.statusText,
    required this.timeText,
    required this.onPickSymbol,
    this.onPickKind,
    required this.onPickStatus,
    required this.onPickTime,
  });

  final ScrollController scrollController;
  final List<SpotOrder> orders;
  final bool loading;
  final bool hasMore;
  final Future<void> Function() onRefresh;
  final bool tradesMode;
  final String pairText;
  final String? kindText;
  final String statusText;
  final String timeText;
  final VoidCallback onPickSymbol;
  final VoidCallback? onPickKind;
  final VoidCallback onPickStatus;
  final VoidCallback onPickTime;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    final l10n = AppLocalizations.of(context)!;
    final showKind = !tradesMode && kindText != null && onPickKind != null;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
          child: Row(
            children: [
              Expanded(
                child: _DropdownFilterPill(
                  text: pairText,
                  onTap: onPickSymbol,
                ),
              ),
              const SizedBox(width: 8),
              if (showKind) ...[
                Expanded(
                  child: _DropdownFilterPill(
                    text: kindText!,
                    onTap: onPickKind!,
                  ),
                ),
                const SizedBox(width: 8),
              ],
              Expanded(
                child: _DropdownFilterPill(
                  text: statusText,
                  onTap: onPickStatus,
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: _DropdownFilterPill(
                  text: timeText,
                  onTap: onPickTime,
                ),
              ),
            ],
          ),
        ),
        Expanded(
          child: RefreshIndicator(
            onRefresh: onRefresh,
            child: orders.isEmpty && !loading
                ? ListView(
                    physics: const AlwaysScrollableScrollPhysics(),
                    children: [
                      const SizedBox(height: 120),
                      Center(
                        child: Text(
                          tradesMode ? l10n.noTradeData : l10n.noHistoryOrders,
                          style: TextStyle(
                            color: cs.onSurface.withAlpha(140),
                            fontSize: 14,
                          ),
                        ),
                      ),
                    ],
                  )
                : ListView.builder(
                    controller: scrollController,
                    physics: const AlwaysScrollableScrollPhysics(),
                    itemCount: orders.length + 1,
                    itemBuilder: (_, i) {
                      if (i == orders.length) {
                        if (loading) {
                          return const Padding(
                            padding: EdgeInsets.all(16),
                            child: Center(child: CircularProgressIndicator()),
                          );
                        }
                        if (!hasMore) {
                          return Padding(
                            padding: const EdgeInsets.symmetric(vertical: 16),
                            child: Center(
                              child: Text(
                                l10n.noMoreData,
                                style: TextStyle(
                                  color: cs.onSurface.withAlpha(120),
                                  fontSize: 12,
                                ),
                              ),
                            ),
                          );
                        }
                        return const SizedBox.shrink();
                      }
                      return _HistoryCard(
                        order: orders[i],
                        tradesMode: tradesMode,
                      );
                    },
                  ),
          ),
        ),
      ],
    );
  }
}

/// 原型：浅灰圆角条，单行文案 + 右侧下拉箭头；默认展示维度名，选中后展示选项文案
class _DropdownFilterPill extends StatelessWidget {
  const _DropdownFilterPill({
    required this.text,
    required this.onTap,
  });
  final String text;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    final isDark = Theme.of(context).brightness == Brightness.dark;
    final bg = isDark
        ? cs.surfaceContainerHighest.withAlpha(120)
        : const Color(0xFFF2F2F4);
    return Material(
      color: bg,
      borderRadius: BorderRadius.circular(8),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(8),
        child: Padding(
          padding: const EdgeInsets.fromLTRB(8, 10, 4, 10),
          child: Row(
            children: [
              Expanded(
                child: Text(
                  text,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    fontSize: 13,
                    color: cs.onSurface,
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down,
                size: 20,
                color: cs.onSurface.withAlpha(130),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _HistoryCard extends ConsumerWidget {
  const _HistoryCard({
    required this.order,
    required this.tradesMode,
  });
  final SpotOrder order;
  final bool tradesMode;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cs = Theme.of(context).colorScheme;
    final l10n = AppLocalizations.of(context)!;
    final base = _spotBaseAsset(order.symbol);
    final quote = 'USDT';
    final sym = order.symbol.replaceAll('/', '').toUpperCase();
    final precision = ref.watch(
        spotSymbolCacheProvider.select((map) => map[sym]));
    final pricePre = precision?.pricePre ?? 2;
    final volPre = precision?.volumePre ?? 4;
    final statusLabel = _historyStatusLabel(order, l10n);
    final statusColor = _historyStatusColor(cs, order);
    final sideColor =
        order.side == SpotSide.buy ? AppColors.rise : AppColors.fall;
    final typeSide = _orderTypeSideLine(order, l10n);

    return Container(
      padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
      decoration: BoxDecoration(
        border: Border(
          bottom: BorderSide(color: cs.outline.withAlpha(40)),
        ),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Expanded(
                child: Text(
                  order.symbol.replaceAll('/', ''),
                  style: TextStyle(
                    color: cs.onSurface,
                    fontSize: 15,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ),
              Text(
                statusLabel,
                style: TextStyle(
                  color: statusColor,
                  fontSize: 13,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ],
          ),
          const SizedBox(height: 6),
          Text(
            typeSide,
            style: TextStyle(
              color: sideColor,
              fontSize: 13,
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 10),
          if (tradesMode) ...[
            _kvRow(
              context,
              '${l10n.tradedDealAmount}($base)',
              '${formatAmount(order.tradedAmount, decimals: volPre)} / ${formatAmount(order.amount, decimals: volPre)}',
            ),
            const SizedBox(height: 6),
            _kvRow(
              context,
              '${l10n.spotAvgPrice}($quote)',
              order.avgPrice > 0
                  ? formatAmount(order.avgPrice, decimals: pricePre)
                  : '--',
            ),
            const SizedBox(height: 6),
            _kvRow(
              context,
              l10n.spotDealTime,
              order.createTime != null
                  ? _fmtTime(order.createTime!)
                  : '--',
            ),
          ] else ...[
            _kvRow(
              context,
              '${l10n.spotEntrustQuantity}($base)',
              formatAmount(order.amount, decimals: volPre),
            ),
            const SizedBox(height: 6),
            _kvRow(
              context,
              '${l10n.orderPriceLabel}($quote)',
              order.type == SpotOrderType.market && order.price <= 0
                  ? l10n.marketPrice
                  : formatAmount(order.price, decimals: pricePre),
            ),
            const SizedBox(height: 6),
            _kvRow(
              context,
              l10n.orderTime,
              order.createTime != null
                  ? _fmtTime(order.createTime!)
                  : '--',
            ),
          ],
        ],
      ),
    );
  }
}

Widget _kvRow(BuildContext context, String k, String v) {
  final cs = Theme.of(context).colorScheme;
  return Row(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Expanded(
        flex: 12,
        child: Text(
          k,
          style: TextStyle(
            color: cs.onSurface.withAlpha(140),
            fontSize: 12,
          ),
        ),
      ),
      Expanded(
        flex: 13,
        child: Text(
          v,
          textAlign: TextAlign.right,
          style: TextStyle(
            color: cs.onSurface,
            fontSize: 13,
            fontWeight: FontWeight.w500,
            fontFeatures: const [FontFeature.tabularFigures()],
          ),
        ),
      ),
    ],
  );
}

String _fmtTime(DateTime t) {
  final x = t.toLocal();
  String two(int n) => n.toString().padLeft(2, '0');
  return '${x.year}-${two(x.month)}-${two(x.day)} ${two(x.hour)}:${two(x.minute)}:${two(x.second)}';
}

String _spotBaseAsset(String symbolUpper) {
  final s = symbolUpper.replaceAll('/', '').toUpperCase();
  const quotes = ['USDT', 'USDC', 'BUSD', 'TUSD'];
  for (final q in quotes) {
    if (s.endsWith(q) && s.length > q.length) {
      return s.substring(0, s.length - q.length);
    }
  }
  return s;
}

Color _historyStatusColor(ColorScheme cs, SpotOrder o) {
  if (o.statusCode == 2) return AppColors.rise;
  return cs.onSurface;
}

String _historyStatusLabel(SpotOrder o, AppLocalizations l10n) {
  if (o.statusCode == 2) return l10n.spotOrderStatusCompleted;
  if (o.statusCode == 3) return l10n.spotOrderStatusPartialFilled;
  if (o.statusCode == 4 || o.statusCode == 5 || o.statusCode == 6) {
    if (o.tradedAmount > 0 && o.tradedAmount < o.amount) {
      return l10n.spotOrderStatusPartialFilled;
    }
    return l10n.spotOrderStatusCancelled;
  }
  return o.status;
}

String _orderTypeSideLine(SpotOrder o, AppLocalizations l10n) {
  final type = switch (o.type) {
    SpotOrderType.market => l10n.marketOrder,
    SpotOrderType.conditionalMarket => l10n.spotOrderTypeMarketConditional,
    _ => l10n.limitOrder,
  };
  final side = o.side == SpotSide.buy ? l10n.buyAction : l10n.sellAction;
  return '$type / $side';
}
