import 'dart:async';

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

import '../data/models/copy_trading/trader.dart';
import '../data/repositories/copy_trading_repository.dart';
import 'auth_provider.dart';

// ── 排序枚举 ─────────────────────────────────────────────

enum TraderSort {
  comprehensive,   // 综合排序 → API orderBy=10
  winRate30d,      // 近14天胜率 → orderBy=30
  roi30d,          // 近14天收益率 → orderBy=20
}

extension TraderSortExt on TraderSort {
  String get apiOrderBy {
    switch (this) {
      case TraderSort.comprehensive: return '10';
      case TraderSort.winRate30d: return '30';
      case TraderSort.roi30d: return '20';
    }
  }
}

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

class CopyTradingState {
  final List<Trader> traders;
  final bool isLoading;
  final String? error;
  final String searchKeyword;
  /// 0=普通带单 1=无损带单 2=未登录时为「全部」；已登录时为「我的收藏」（与 Web 一致）
  final int tabIndex;
  final TraderSort sort;

  /// 是否是带单员（traderLevel == "20"）
  final bool isTrader;

  /// 权益数据：
  /// - 跟单员：来自 swap/follow-wallet/get（balance 字段）
  /// - 带单员：来自 swap/wallet-new/get（currentCapital 字段）
  final Map<String, dynamic>? wallet;

  /// 带单员专属统计（follow/customer/trader-info/{id}）
  /// 字段：currentClearedProfit、totalFollowProfit、following、maxFollow
  final Map<String, dynamic>? traderInfo;

  // ── 分页 ──────────────────────────────────────────────
  final int page;
  final bool hasMore;
  final bool isLoadingMore;

  const CopyTradingState({
    this.traders = const [],
    this.isLoading = false,
    this.error,
    this.searchKeyword = '',
    this.tabIndex = 0,
    this.sort = TraderSort.comprehensive,
    this.isTrader = false,
    this.wallet,
    this.traderInfo,
    this.page = 1,
    this.hasMore = true,
    this.isLoadingMore = false,
  });

  CopyTradingState copyWith({
    List<Trader>? traders,
    bool? isLoading,
    String? error,
    String? searchKeyword,
    int? tabIndex,
    TraderSort? sort,
    bool? isTrader,
    Map<String, dynamic>? wallet,
    Map<String, dynamic>? traderInfo,
    int? page,
    bool? hasMore,
    bool? isLoadingMore,
  }) =>
      CopyTradingState(
        traders: traders ?? this.traders,
        isLoading: isLoading ?? this.isLoading,
        error: error,
        searchKeyword: searchKeyword ?? this.searchKeyword,
        tabIndex: tabIndex ?? this.tabIndex,
        sort: sort ?? this.sort,
        isTrader: isTrader ?? this.isTrader,
        wallet: wallet ?? this.wallet,
        traderInfo: traderInfo ?? this.traderInfo,
        page: page ?? this.page,
        hasMore: hasMore ?? this.hasMore,
        isLoadingMore: isLoadingMore ?? this.isLoadingMore,
      );

  String get _traderTypeForList {
    if (tabIndex == 2) {
      return ''; // 未登录第三 tab「全部」；已登录不会在公开列表链路使用
    }
    switch (tabIndex) {
      case 0:
        return '0';
      case 1:
        return '1';
      default:
        return '';
    }
  }

  List<Trader> get displayTraders {
    if (searchKeyword.isEmpty) return traders;
    final kw = searchKeyword.toLowerCase();
    return traders.where((t) => t.name.toLowerCase().contains(kw)).toList();
  }
}

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

class CopyTradingNotifier extends Notifier<CopyTradingState> {
  static const _pageSize = 10;
  Timer? _searchDebounce;

  @override
  CopyTradingState build() {
    // 用 listen 代替 watch，避免登录/登出时 notifier 整体重建与同帧
    // context.go('/') 导航并发，触发 element._lifecycleState 断言崩溃。
    // （同 app_router.dart 中登录时不调用 notifyListeners 的原因一致）
    ref.listen<bool>(isLoggedInProvider, (prev, next) {
      if (prev == next) return;
      // 推迟到下一帧：确保 GoRouter 导航操作完成后再重置 state，
      // 防止两者同帧操作 element tree 产生冲突。
      WidgetsBinding.instance.addPostFrameCallback((_) {
        state = CopyTradingState(isLoading: true);
        if (next) {
          _load();
        } else {
          _loadPublic();
        }
      });
    });

    final isLoggedIn = ref.read(isLoggedInProvider);
    if (isLoggedIn) {
      Future.microtask(_load);
    } else {
      Future.microtask(_loadPublic);
    }
    return CopyTradingState(isLoading: true);
  }

  CopyTradingRepository get _repo => ref.read(copyTradingRepositoryProvider);

  bool _favoriteTabLoggedIn() =>
      state.tabIndex == 2 && ref.read(isLoggedInProvider);

  Future<List<Trader>> _mergeFavoriteFlags(List<Trader> traders) async {
    if (traders.isEmpty) {
      return traders;
    }
    try {
      final rows = await _repo.getFavoriteList(currentPage: 1, pageSize: 200);
      final favIds = <String>{};
      for (final r in rows) {
        final sid =
            '${r['id'] ?? r['traderId'] ?? r['trader_id'] ?? ''}'.trim();
        if (sid.isNotEmpty) {
          favIds.add(sid);
        }
      }
      return traders
          .map((t) => t.copyWith(isFavorited: favIds.contains(t.id)))
          .toList();
    } catch (_) {
      return traders;
    }
  }

  Future<void> _load({bool silent = false}) async {
    if (!silent) {
      state = state.copyWith(isLoading: true, error: null);
    } else {
      state = state.copyWith(error: null);
    }
    try {
      if (_favoriteTabLoggedIn()) {
        final favoriteResults = await Future.wait([
          _repo.getFollowerInfo(),
          _repo.getFavoriteList(currentPage: 1, pageSize: 200),
          _repo.getFollowWallet(),
          _repo.getContractWallet(),
        ]);
        final followerInfo = favoriteResults[0] as Map<String, dynamic>?;
        final rawList = favoriteResults[1] as List<Map<String, dynamic>>;
        final followWallet = favoriteResults[2] as Map<String, dynamic>?;
        final contractWallet = favoriteResults[3] as Map<String, dynamic>?;

        final traderLevel = followerInfo?['trader']?.toString() ??
            followerInfo?['traderLevel']?.toString() ??
            '';
        final isTrader = traderLevel == '20';
        final traderId = followerInfo?['id']?.toString() ?? '';
        Map<String, dynamic>? traderInfoData;
        if (isTrader && traderId.isNotEmpty) {
          traderInfoData = await _repo.getTraderInfo(traderId);
        }
        final wallet = isTrader ? contractWallet : followWallet;

        final traders = rawList
            .map((e) => Trader.fromApi(e, isFavoritedOverride: true))
            .toList();

        state = state.copyWith(
          isLoading: false,
          error: null,
          traders: traders,
          isTrader: isTrader,
          wallet: wallet,
          traderInfo: traderInfoData,
          page: 1,
          hasMore: false,
          isLoadingMore: false,
        );
        return;
      }

      final results = await Future.wait([
        _repo.getFollowerInfo(),
        _repo.getTraderList(
          orderBy: state.sort.apiOrderBy,
          traderType: state._traderTypeForList,
          nickName: state.searchKeyword,
          page: 1,
          pageSize: _pageSize,
        ),
        _repo.getFollowWallet(),
        _repo.getContractWallet(),
      ]);

      final followerInfo = results[0] as Map<String, dynamic>?;
      final rawList = results[1] as List<Map<String, dynamic>>;
      final followWallet = results[2] as Map<String, dynamic>?;
      final contractWallet = results[3] as Map<String, dynamic>?;

      final traderLevel = followerInfo?['trader']?.toString() ??
          followerInfo?['traderLevel']?.toString() ??
          '';
      final isTrader = traderLevel == '20';
      final traderId = followerInfo?['id']?.toString() ?? '';

      Map<String, dynamic>? traderInfoData;
      if (isTrader && traderId.isNotEmpty) {
        traderInfoData = await _repo.getTraderInfo(traderId);
      }

      final wallet = isTrader ? contractWallet : followWallet;

      final isNoLossTab = state.tabIndex == 1;
      var traders = rawList
          .map((e) => Trader.fromApi(e, isNoLoss: isNoLossTab))
          .toList();
      traders = await _mergeFavoriteFlags(traders);

      state = state.copyWith(
        isLoading: false,
        traders: traders,
        isTrader: isTrader,
        wallet: wallet,
        traderInfo: traderInfoData,
        page: 1,
        hasMore: rawList.length >= _pageSize,
        isLoadingMore: false,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: e.toString(),
        traders: [],
        page: 1,
        hasMore: false,
      );
    }
  }

  Future<void> _loadPublic() async {
    state = state.copyWith(isLoading: true, error: null);
    try {
      final rawList = await _repo.getPublicTraderList(
        page: 1,
        pageSize: _pageSize,
        traderType: state._traderTypeForList,
        orderBy: state.sort.apiOrderBy,
      );
      final isNoLossTab = state.tabIndex == 1;
      final traders = rawList.map((e) => Trader.fromApi(e, isNoLoss: isNoLossTab)).toList();
      state = state.copyWith(
        isLoading: false,
        traders: traders,
        page: 1,
        hasMore: rawList.length >= _pageSize,
        isLoadingMore: false,
      );
    } catch (_) {
      state = state.copyWith(isLoading: false, traders: [], page: 1, hasMore: false);
    }
  }

  Future<void> loadMore() async {
    if (_favoriteTabLoggedIn()) {
      return;
    }
    if (!state.hasMore || state.isLoadingMore || state.isLoading) {
      return;
    }
    final nextPage = state.page + 1;
    state = state.copyWith(isLoadingMore: true);
    try {
      final isLoggedIn = ref.read(isLoggedInProvider);
      List<Map<String, dynamic>> rawList;
      if (isLoggedIn) {
        rawList = await _repo.getTraderList(
          orderBy: state.sort.apiOrderBy,
          traderType: state._traderTypeForList,
          nickName: state.searchKeyword,
          page: nextPage,
          pageSize: _pageSize,
        );
      } else {
        rawList = await _repo.getPublicTraderList(
          page: nextPage,
          pageSize: _pageSize,
          traderType: state._traderTypeForList,
          orderBy: state.sort.apiOrderBy,
        );
      }
      final isNoLossTab = state.tabIndex == 1;
      final more = rawList
          .map((e) => Trader.fromApi(e, isNoLoss: isNoLossTab))
          .toList();
      final mergedMore = await _mergeFavoriteFlags(more);
      state = state.copyWith(
        isLoadingMore: false,
        traders: [...state.traders, ...mergedMore],
        page: nextPage,
        hasMore: rawList.length >= _pageSize,
      );
    } catch (_) {
      state = state.copyWith(isLoadingMore: false);
    }
  }

  void setTab(int index) {
    state = state.copyWith(tabIndex: index, traders: [], page: 1, hasMore: true, isLoadingMore: false, isLoading: true);
    final isLoggedIn = ref.read(isLoggedInProvider);
    Future.microtask(isLoggedIn ? _load : _loadPublic);
  }

  void setSearch(String kw) {
    state = state.copyWith(searchKeyword: kw);
    if (_favoriteTabLoggedIn()) {
      return;
    }
    _searchDebounce?.cancel();
    _searchDebounce = Timer(const Duration(milliseconds: 400), () {
      final isLoggedIn = ref.read(isLoggedInProvider);
      Future.microtask(isLoggedIn ? _load : _loadPublic);
    });
  }

  void setSort(TraderSort sort) {
    if (_favoriteTabLoggedIn()) {
      state = state.copyWith(sort: sort);
      return;
    }
    state = state.copyWith(sort: sort, traders: [], page: 1, hasMore: true, isLoadingMore: false);
    final isLoggedIn = ref.read(isLoggedInProvider);
    Future.microtask(isLoggedIn ? _load : _loadPublic);
  }

  Future<void> refresh() {
    final isLoggedIn = ref.read(isLoggedInProvider);
    return isLoggedIn ? _load() : _loadPublic();
  }

  Future<void> silentRefresh() {
    final isLoggedIn = ref.read(isLoggedInProvider);
    return isLoggedIn ? _load(silent: true) : _loadPublic();
  }

  /// 收藏 / 取消收藏。成功返回新的收藏状态，失败还原列表并返回 null。
  Future<bool?> toggleFavorite(Trader trader) async {
    if (!ref.read(isLoggedInProvider)) {
      return null;
    }
    final was = trader.isFavorited;
    final id = trader.id;
    if (id.isEmpty) {
      return null;
    }

    List<Trader> optimistic(List<Trader> list) {
      return list
          .map((t) => t.id == id ? t.copyWith(isFavorited: !was) : t)
          .toList();
    }

    final prevList = state.traders;
    var nextList = optimistic(prevList);
    if (_favoriteTabLoggedIn() && was) {
      nextList = nextList.where((t) => t.id != id).toList();
    }
    state = state.copyWith(traders: nextList);

    try {
      if (was) {
        await _repo.unfavoriteTrader(id);
      } else {
        await _repo.favoriteTrader(id);
      }
      return !was;
    } catch (_) {
      state = state.copyWith(traders: prevList);
      return null;
    }
  }
}

final copyTradingProvider =
    NotifierProvider<CopyTradingNotifier, CopyTradingState>(
  CopyTradingNotifier.new,
);
