import 'dart:async';
import 'dart:developer' as developer;

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

import '../core/network/dio_client.dart';
import '../data/models/home/market_ticker.dart';
import '../data/services/market_service.dart';
import '../data/services/spot_service.dart';
import 'coin_cache_provider.dart';
import 'spot_ws_provider.dart';
import 'ws_provider.dart';

/// 行情：合约 | 现货
enum MarketMode { futures, spot }

// ── Mock 数据（ENABLE_MOCK=true 时使用）──────────────────────

// volume24h 为成交额（USDT），与 WS tick.q 口径一致
const _mockTickers = [
  MarketTicker(
      symbol: 'BTCUSDT',
      baseAsset: 'BTC',
      lastPrice: 93077.12,
      change24h: -0.09,
      volume24h: 11900000000),
  MarketTicker(
      symbol: 'ETHUSDT',
      baseAsset: 'ETH',
      lastPrice: 3077.12,
      change24h: 4.81,
      volume24h: 5630000000),
  MarketTicker(
      symbol: 'SOLUSDT',
      baseAsset: 'SOL',
      lastPrice: 178.50,
      change24h: 2.10,
      volume24h: 2180000000),
  MarketTicker(
      symbol: 'BNBUSDT',
      baseAsset: 'BNB',
      lastPrice: 621.40,
      change24h: 0.87,
      volume24h: 1250000000),
  MarketTicker(
      symbol: 'DOGEUSDT',
      baseAsset: 'DOGE',
      lastPrice: 0.3842,
      change24h: 5.23,
      volume24h: 856000000),
  MarketTicker(
      symbol: 'XRPUSDT',
      baseAsset: 'XRP',
      lastPrice: 2.1430,
      change24h: -1.35,
      volume24h: 732000000),
  MarketTicker(
      symbol: 'ADAUSDT',
      baseAsset: 'ADA',
      lastPrice: 1.0820,
      change24h: 3.41,
      volume24h: 415000000),
  MarketTicker(
      symbol: 'AVAXUSDT',
      baseAsset: 'AVAX',
      lastPrice: 43.620,
      change24h: -0.76,
      volume24h: 389000000),
  MarketTicker(
      symbol: 'LTCUSDT',
      baseAsset: 'LTC',
      lastPrice: 112.30,
      change24h: -0.52,
      volume24h: 274000000),
  MarketTicker(
      symbol: 'LINKUSDT',
      baseAsset: 'LINK',
      lastPrice: 18.640,
      change24h: 4.10,
      volume24h: 198000000),
];

/// 排序字段
enum MarketSortField { volume, price, change }

// ── UI State ──────────────────────────────────────────────

class MarketState {
  final bool isLoading;
  final String? errorMessage;
  final List<MarketTicker> tickers;

  /// 稳定的展示顺序（只在初始加载/手动排序时更新，WS 价格推送不重排）
  final List<String> displayOrder;
  final String searchKeyword;
  final MarketSortField? sortField;
  final bool sortAsc;

  final MarketMode mode;
  final bool spotLoading;
  final List<MarketTicker> spotTickers;
  final List<String> spotDisplayOrder;
  final MarketSortField? spotSortField;
  final bool spotSortAsc;

  const MarketState({
    this.isLoading = false,
    this.errorMessage,
    this.tickers = const [],
    this.displayOrder = const [],
    this.searchKeyword = '',
    this.sortField,
    this.sortAsc = true,
    this.mode = MarketMode.futures,
    this.spotLoading = false,
    this.spotTickers = const [],
    this.spotDisplayOrder = const [],
    this.spotSortField,
    this.spotSortAsc = true,
  });

  MarketState copyWith({
    bool? isLoading,
    String? errorMessage,
    List<MarketTicker>? tickers,
    List<String>? displayOrder,
    String? searchKeyword,
    MarketSortField? sortField,
    bool? sortAsc,
    bool clearSort = false,
    MarketMode? mode,
    bool? spotLoading,
    List<MarketTicker>? spotTickers,
    List<String>? spotDisplayOrder,
    MarketSortField? spotSortField,
    bool? spotSortAsc,
    bool clearSpotSort = false,
  }) {
    return MarketState(
      isLoading: isLoading ?? this.isLoading,
      errorMessage: errorMessage,
      tickers: tickers ?? this.tickers,
      displayOrder: displayOrder ?? this.displayOrder,
      searchKeyword: searchKeyword ?? this.searchKeyword,
      sortField: clearSort ? null : (sortField ?? this.sortField),
      sortAsc: sortAsc ?? this.sortAsc,
      mode: mode ?? this.mode,
      spotLoading: spotLoading ?? this.spotLoading,
      spotTickers: spotTickers ?? this.spotTickers,
      spotDisplayOrder: spotDisplayOrder ?? this.spotDisplayOrder,
      spotSortField:
          clearSpotSort ? null : (spotSortField ?? this.spotSortField),
      spotSortAsc: spotSortAsc ?? this.spotSortAsc,
    );
  }

  /// 只返回 symbol 列表（变化频率远低于 ticker 数据），
  /// 用于 ListView 构建行，各行内部自己 select 对应 ticker。
  /// 使用 displayOrder 保持稳定顺序，WS 价格更新不改变排列位置。
  List<String> get displaySymbols {
    final order = displayOrder.isEmpty
        ? tickers.map((t) => t.symbol).toList()
        : displayOrder;
    if (searchKeyword.isEmpty) return order;
    final kw = searchKeyword.toLowerCase();
    final tickerMap = {for (final t in tickers) t.symbol: t};
    return order.where((sym) {
      final t = tickerMap[sym];
      if (t == null) return false;
      return t.baseAsset.toLowerCase().contains(kw) ||
          sym.toLowerCase().contains(kw);
    }).toList();
  }

  List<String> get spotDisplaySymbols {
    final order = spotDisplayOrder.isEmpty
        ? spotTickers.map((t) => t.symbol).toList()
        : spotDisplayOrder;
    if (searchKeyword.isEmpty) return order;
    final kw = searchKeyword.toLowerCase();
    final tickerMap = {for (final t in spotTickers) t.symbol: t};
    return order.where((sym) {
      final t = tickerMap[sym];
      if (t == null) return false;
      return t.baseAsset.toLowerCase().contains(kw) ||
          sym.toLowerCase().contains(kw);
    }).toList();
  }
}

// ── Notifier ──────────────────────────────────────────────

class MarketNotifier extends Notifier<MarketState> {
  StreamSubscription? _tickerSub;
  Timer? _throttleTimer;
  // 缓冲区：积累 WS 推送的价格更新，由节流定时器批量刷新到 state
  final _tickerBuffer =
      <String, ({double price, double change24h, double volume24h, String priceStr})>{};

  StreamSubscription? _spotTickerSub;
  Timer? _spotThrottleTimer;
  final _spotTickerBuffer =
      <String, ({double price, double change24h, double volume24h, String priceStr})>{};
  bool _spotLoaded = false;
  int _spotTickerLogCount = 0;
  int _spotTickerSkipLogCount = 0;

  @override
  MarketState build() {
    // Notifier 销毁时清理订阅
    ref.onDispose(() {
      _tickerSub?.cancel();
      _throttleTimer?.cancel();
      _spotTickerSub?.cancel();
      _spotThrottleTimer?.cancel();
    });
    // 监听 WS 连接状态，重连成功后清空缓冲并重新加载数据
    ref.listen<AsyncValue<WsConnectionState>>(
      wsConnectionStateProvider,
      (prev, next) {
        final prevState = prev?.valueOrNull;
        final nextState = next.valueOrNull;
        if (prevState == WsConnectionState.reconnecting &&
            nextState == WsConnectionState.connected) {
          _tickerBuffer.clear();
          Future.microtask(_load);
        }
      },
    );

    // 现货 WS：connected 时重绑 ticker 监听并补订（新 client / 重连）
    ref.listen<AsyncValue<SpotWsState>>(
      spotWsConnectionStateProvider,
      (prev, next) {
        final nextState = next.valueOrNull;
        if (nextState == SpotWsState.connected) {
          _spotTickerSub?.cancel();
          _spotTickerSub = null;
          if (_spotLoaded && state.spotTickers.isNotEmpty) {
            _subscribeSpotWsTickers(state.spotTickers);
          }
        }
      },
    );
    Future.microtask(_load);
    return const MarketState(isLoading: true);
  }

  Future<void> _load() async {
    List<MarketTicker> tickers;

    try {
      // 优先从全局缓存读取，缓存为空则请求接口
      final cache = ref.read(coinCacheProvider);
      if (cache.isNotEmpty) {
        tickers = cache.values.toList();
      } else {
        final dio = ref.read(dioClientProvider);
        final result = await MarketService(dio).getEnabledCoins();
        tickers = result.isNotEmpty ? result : _mockTickers;
      }
    } catch (e) {
      tickers = _mockTickers;
    }

    // 保留已有的实时价格，避免刷新静态列表时价格闪零
    final liveMap = {for (final t in state.tickers) t.symbol: t};
    final merged = tickers.map((t) {
      final live = liveMap[t.symbol];
      if (live == null || live.lastPrice == 0) return t;
      return t.copyWith(
        lastPrice: live.lastPrice,
        change24h: live.change24h,
        volume24h: live.volume24h,
      );
    }).toList();

    final order = _computeOrder(merged, state.sortField, state.sortAsc);
    state =
        state.copyWith(isLoading: false, tickers: merged, displayOrder: order);
    // 加载完初始数据后，订阅所有 symbol 的 WS ticker 流
    _subscribeWsTickers(tickers);
  }

  /// 根据当前排序参数计算稳定展示顺序
  List<String> _computeOrder(
      List<MarketTicker> tickers, MarketSortField? sortField, bool sortAsc) {
    final list = tickers.toList();
    if (sortField != null) {
      list.sort((a, b) {
        final cmp = switch (sortField) {
          MarketSortField.volume => a.volume24h.compareTo(b.volume24h),
          MarketSortField.price => a.lastPrice.compareTo(b.lastPrice),
          MarketSortField.change => a.change24h.compareTo(b.change24h),
        };
        return sortAsc ? cmp : -cmp;
      });
    }
    return list.map((t) => t.symbol).toList();
  }

  /// 订阅 WS ticker 流，批量订阅所有 symbol 的 market.{symbol}.ticket
  void _subscribeWsTickers(List<MarketTicker> tickers) {
    final ws = ref.read(wsClientProvider);
    final symbols = tickers.map((t) => t.symbol).toList();

    // 差量订阅：自动退订已下架币对，订阅新增币对
    ws.resubscribeTickerBatch(symbols);

    // 仅首次建立监听；刷新时 WS 流不中断，保持数据持续流入
    if (_tickerSub == null) {
      _tickerSub = ws.tickerStream.listen(_onTickerUpdate);
    }

    // 仅首次启动节流定时器
    if (_throttleTimer == null || !_throttleTimer!.isActive) {
      _throttleTimer = Timer.periodic(
        const Duration(milliseconds: 500),
        (_) => _flushTickerBuffer(),
      );
    }
  }

  /// 收到单条 ticker 推送，写入缓冲区（不立即更新 state）
  void _onTickerUpdate(Map<String, dynamic> data) {
    final symbol = data['symbol'] as String?;
    final price = (data['price'] as num?)?.toDouble();
    if (symbol == null || price == null) return;

    final change24h = (data['change24h'] as num?)?.toDouble() ?? 0;
    final volume24h = (data['volume24h'] as num?)?.toDouble() ?? 0;
    final priceStr = data['priceStr'] as String? ?? '';

    // 只缓存价格字段，合并时保留原有 icon 等静态信息
    _tickerBuffer[symbol] = (
      price: price,
      change24h: change24h,
      volume24h: volume24h,
      priceStr: priceStr,
    );
  }

  /// 节流刷新：将缓冲区中的价格更新合并到 state.tickers（保留 icon 等字段）
  /// 注意：只更新价格数值，不重新排序 displayOrder，避免列表位置跳动
  void _flushTickerBuffer() {
    if (_tickerBuffer.isEmpty) return;

    final updated = state.tickers.map((t) {
      final newer = _tickerBuffer[t.symbol];
      if (newer == null) return t;
      return t.copyWith(
        lastPrice: newer.price,
        change24h: newer.change24h,
        volume24h: newer.volume24h,
        lastPriceStr: newer.priceStr.isNotEmpty ? newer.priceStr : null,
      );
    }).toList();

    _tickerBuffer.clear();
    // 只更新 tickers 价格，不触发 displayOrder 变更
    state = state.copyWith(tickers: updated);
  }

  void setSearch(String keyword) {
    state = state.copyWith(searchKeyword: keyword);
  }

  /// 切换排序：第一次点 → 降序(▼)，再点 → 升序(▲)，第三次 → 取消
  void toggleSort(MarketSortField field) {
    MarketSortField? newField;
    bool newAsc;
    if (state.sortField == field) {
      if (!state.sortAsc) {
        // 降序 → 升序
        newField = field;
        newAsc = true;
      } else {
        // 升序 → 取消排序（恢复初始顺序）
        newField = null;
        newAsc = false;
      }
    } else {
      // 切换到新列，默认降序
      newField = field;
      newAsc = false;
    }
    final order = _computeOrder(state.tickers, newField, newAsc);
    state = state.copyWith(
      sortField: newField,
      sortAsc: newAsc,
      displayOrder: order,
      clearSort: newField == null,
    );
  }

  Future<void> refresh() async {
    if (state.mode == MarketMode.futures) {
      await ref.read(coinCacheProvider.notifier).refresh();
      await _load();
    } else {
      await _loadSpot();
    }
  }

  void setMode(MarketMode mode) {
    if (state.mode == mode) return;
    state = state.copyWith(mode: mode);
    if (mode == MarketMode.spot && !_spotLoaded) {
      Future.microtask(_loadSpot);
    }
  }

  void loadSpotIfNeeded() {
    if (!_spotLoaded) {
      Future.microtask(_loadSpot);
    }
  }

  Future<void> _loadSpot() async {
    state = state.copyWith(spotLoading: true);
    if (!kReleaseMode) {
      debugPrint('[SpotMarket] _loadSpot start');
      developer.log('_loadSpot start', name: 'SpotMarket');
    }
    try {
      final dio = ref.read(dioClientProvider);
      final svc = SpotService(dio);
      final symbols = await svc.getSymbols();
      if (!kReleaseMode) {
        debugPrint('[SpotMarket] getSymbols count=${symbols.length}');
        developer.log(
          'getSymbols ok, count=${symbols.length} sample=${symbols.isNotEmpty ? symbols.first : <String, dynamic>{}}',
          name: 'SpotMarket',
        );
      }
      // 按后台 sort 字段排序（数值小在前）
      symbols.sort((a, b) {
        final sa = (a['sort'] as num?)?.toInt() ?? 9999;
        final sb = (b['sort'] as num?)?.toInt() ?? 9999;
        return sa.compareTo(sb);
      });
      final tickers = symbols.map((m) {
        final sym = (m['symbol'] as String? ?? '').toUpperCase();
        final base = (m['base'] as String? ?? '').toUpperCase();
        return MarketTicker(
          symbol: sym,
          baseAsset: base.isNotEmpty ? base : _deriveBase(sym),
          lastPrice: 0,
          change24h: 0,
          volume24h: 0,
          isFutures: false,
          icon: _spotIconUrl(m),
        );
      }).toList();

      // 保留已有实时价格
      final liveMap = {for (final t in state.spotTickers) t.symbol: t};
      final merged = tickers.map((t) {
        final live = liveMap[t.symbol];
        if (live == null || live.lastPrice == 0) return t;
        return t.copyWith(
          lastPrice: live.lastPrice,
          change24h: live.change24h,
          volume24h: live.volume24h,
          icon: t.icon.isEmpty && live.icon.isNotEmpty ? live.icon : null,
        );
      }).toList();

      state = state.copyWith(
        spotLoading: false,
        spotTickers: merged,
        spotDisplayOrder: merged.map((t) => t.symbol).toList(),
      );
      _spotLoaded = true;
      _subscribeSpotWsTickers(merged);
    } catch (e, st) {
      if (!kReleaseMode) {
        debugPrint('[SpotMarket] _loadSpot failed: $e');
        developer.log(
          '_loadSpot failed: $e',
          name: 'SpotMarket',
          error: e,
          stackTrace: st,
        );
      }
      state = state.copyWith(spotLoading: false);
    }
  }

  void _subscribeSpotWsTickers(List<MarketTicker> tickers) {
    final ws = ref.read(spotWsClientProvider);
    final symbols = tickers.map((t) => t.symbol).toList();
    if (!kReleaseMode) {
      debugPrint(
          '[SpotMarket] subscribe WS tickers: ${symbols.length} → ${symbols.take(3).toList()}');
      developer.log(
        'subscribe spot tickers: ${symbols.length} symbols → ${symbols.take(5).map((s) => 'market_${s.toLowerCase()}_ticker').join(", ")}…',
        name: 'SpotMarket',
      );
    }
    ws.resubscribeTickerBatch(symbols);

    if (_spotTickerSub == null) {
      _spotTickerSub = ws.tickerStream.listen(_onSpotTickerUpdate);
    }
    if (_spotThrottleTimer == null || !_spotThrottleTimer!.isActive) {
      _spotThrottleTimer = Timer.periodic(
        const Duration(milliseconds: 500),
        (_) => _flushSpotTickerBuffer(),
      );
    }
  }

  void _onSpotTickerUpdate(Map<String, dynamic> data) {
    final symbol = data['symbol'] as String?;
    var price = (data['price'] as num?)?.toDouble();
    price ??= (data['close'] as num?)?.toDouble();
    if (symbol == null || price == null) {
      if (!kReleaseMode && _spotTickerSkipLogCount < 12) {
        _spotTickerSkipLogCount++;
        debugPrint('[SpotMarket] ticker skip (no symbol/price) $data');
        developer.log(
          'ticker update skipped sym=$symbol price=$price raw=$data',
          name: 'SpotMarket',
        );
      }
      return;
    }
    if (!kReleaseMode && price <= 0 && _spotTickerLogCount < 3) {
      debugPrint('[SpotMarket] ticker price<=0 sym=$symbol raw=$data');
    }
    if (!kReleaseMode && _spotTickerLogCount < 8) {
      _spotTickerLogCount++;
      developer.log(
        'ticker apply sym=$symbol price=$price ch=${data['change24h']} vol=${data['turnover']}',
        name: 'SpotMarket',
      );
    }
    _spotTickerBuffer[symbol] = (
      price: price,
      change24h: (data['change24h'] as num?)?.toDouble() ?? 0,
      volume24h: (data['turnover'] as num?)?.toDouble() ?? 0,
      priceStr: data['priceStr'] as String? ?? '',
    );
  }

  void _flushSpotTickerBuffer() {
    if (_spotTickerBuffer.isEmpty) return;
    if (!kReleaseMode && _spotTickerBuffer.isNotEmpty) {
      debugPrint('[SpotMarket] flush buffer keys=${_spotTickerBuffer.keys}');
      developer.log(
        'flush spot buffer keys=${_spotTickerBuffer.keys.toList()}',
        name: 'SpotMarket',
      );
    }
    final updated = state.spotTickers.map((t) {
      final newer = _spotTickerBuffer[t.symbol];
      if (newer == null) return t;
      return t.copyWith(
        lastPrice: newer.price,
        change24h: newer.change24h,
        volume24h: newer.volume24h,
        lastPriceStr: newer.priceStr.isNotEmpty ? newer.priceStr : null,
      );
    }).toList();
    _spotTickerBuffer.clear();
    state = state.copyWith(spotTickers: updated);
  }

  void toggleSpotSort(MarketSortField field) {
    MarketSortField? newField;
    bool newAsc;
    if (state.spotSortField == field) {
      if (!state.spotSortAsc) {
        newField = field;
        newAsc = true;
      } else {
        newField = null;
        newAsc = false;
      }
    } else {
      newField = field;
      newAsc = false;
    }
    final sorted = state.spotTickers.toList();
    if (newField != null) {
      sorted.sort((a, b) {
        final cmp = switch (newField!) {
          MarketSortField.volume => a.volume24h.compareTo(b.volume24h),
          MarketSortField.price => a.lastPrice.compareTo(b.lastPrice),
          MarketSortField.change => a.change24h.compareTo(b.change24h),
        };
        return newAsc ? cmp : -cmp;
      });
    }
    state = state.copyWith(
      spotSortField: newField,
      spotSortAsc: newAsc,
      spotDisplayOrder: sorted.map((t) => t.symbol).toList(),
      clearSpotSort: newField == null,
    );
  }

  static String _spotIconUrl(Map<String, dynamic> m) {
    final raw = m['icon'];
    if (raw == null) return '';
    if (raw is String) return raw;
    return raw.toString();
  }

  static String _deriveBase(String sym) {
    for (final q in const ['USDT', 'BTC', 'ETH', 'BUSD']) {
      if (sym.endsWith(q) && sym.length > q.length) {
        return sym.substring(0, sym.length - q.length);
      }
    }
    return sym;
  }
}

final spotTickerProvider =
    Provider.family<MarketTicker?, String>((ref, symbol) {
  return ref.watch(marketProvider.select(
    (s) => s.spotTickers.cast<MarketTicker?>().firstWhere(
          (t) => t?.symbol == symbol,
          orElse: () => null,
        ),
  ));
});

final marketProvider = NotifierProvider<MarketNotifier, MarketState>(
  MarketNotifier.new,
);

/// 按 symbol 精确订阅单个 ticker，BTC 价格变化只重建 BTC 那一行。
/// 依赖 MarketTicker 已实现的 == 来判断是否真正变化。
final tickerProvider = Provider.family<MarketTicker?, String>((ref, symbol) {
  return ref.watch(marketProvider.select(
    (s) => s.tickers.cast<MarketTicker?>().firstWhere(
          (t) => t!.symbol == symbol,
          orElse: () => null,
        ),
  ));
});
