import 'dart:async';

import 'package:networks_sdk/src/presentation/wiring/network_callbacks.dart';

/// Token 刷新管理器
///
/// 两种刷新模式：
///
/// 1. **被动刷新**（[refreshIfNeeded]）— 拦截器检测到 token 过期后调用
/// 2. **主动刷新**（[proactivelyRefreshIfNeeded]）— 解析 JWT exp，
///    距过期不足阈值时提前刷新，避免带过期 token 发请求
///
/// 两种模式共享串行锁和时间窗口保护：
/// - **串行锁** — 同一时刻只执行一次刷新，其余请求等待同一 Completer
/// - **时间窗口** — 刷新成功后 [reuseWindow] 内再次调用直接返回缓存 token
/// - **超时保护** — 刷新回调超过 [timeout] 自动失败，防止死锁
class TokenRefreshManager {
  final OnTokenRefresh? onTokenRefresh;
  final OnLog? onLog;

  /// 刷新超时时间（防止 onTokenRefresh 卡住导致所有请求阻塞）
  final Duration timeout;

  /// 时间窗口：刷新成功后此时间内再次调用直接返回缓存 token
  final Duration reuseWindow;

  /// Token 过期时间解析（App 层注入 JWT exp 解析逻辑）
  final OnGetTokenExpiry? onGetTokenExpiry;

  /// 主动刷新阈值：距过期不足此时间时提前刷新（默认 1 小时）
  final Duration proactiveRefreshThreshold;

  /// 当前正在进行的刷新任务（null = 空闲）
  Completer<String?>? _completer;

  /// 上次刷新成功的时间戳
  DateTime? _lastRefreshTime;

  /// 上次刷新成功的 token（时间窗口内复用）
  String? _lastToken;

  TokenRefreshManager({
    this.onTokenRefresh,
    this.onLog,
    this.timeout = const Duration(seconds: 10),
    this.reuseWindow = const Duration(seconds: 3),
    this.onGetTokenExpiry,
    this.proactiveRefreshThreshold = const Duration(hours: 1),
  });

  /// 执行 token 刷新（如果需要）
  ///
  /// 返回新 token（刷新成功或在时间窗口内），
  /// 返回 null = 刷新失败或超时。
  Future<String?> refreshIfNeeded() async {
    // 1. 时间窗口：最近刷新过且未超时 → 直接返回缓存的 token
    if (_isWithinReuseWindow()) {
      _log('Token refreshed recently, reusing');
      return _lastToken;
    }

    // 2. 有正在进行的刷新 → 等待同一 Completer
    final existing = _completer;
    if (existing != null) {
      _log('Waiting for ongoing token refresh');
      return existing.future;
    }

    // 3. 发起新的刷新
    if (onTokenRefresh == null) {
      _log('No onTokenRefresh callback configured');
      return null;
    }

    final completer = Completer<String?>();
    _completer = completer;

    try {
      final newToken = await onTokenRefresh!().timeout(
        timeout,
        onTimeout: () {
          _log('Token refresh timed out after ${timeout.inSeconds}s');
          return null;
        },
      );

      final success = newToken != null && newToken.isNotEmpty;

      if (success) {
        _lastRefreshTime = DateTime.now();
        _lastToken = newToken;
        _log('Token refreshed successfully');
      } else {
        _log('Token refresh failed (null or empty token)');
      }

      // 先 complete 再清引用，确保等待者能拿到结果
      completer.complete(success ? newToken : null);
      return success ? newToken : null;
    } catch (e) {
      _log('Token refresh error: $e');
      completer.complete(null);
      return null;
    } finally {
      // 清理引用（Completer 已 complete，等待者不受影响）
      _completer = null;
    }
  }

  /// 检查 token 是否即将过期，是则主动刷新
  ///
  /// 解析 [currentToken] 的过期时间，距过期不足 [proactiveRefreshThreshold]
  /// 时调用 [refreshIfNeeded] 刷新。复用串行锁和超时保护。
  ///
  /// 返回新 token（已刷新）或 null（不需要刷新 / 刷新失败 / 无法解析过期时间）。
  Future<String?> proactivelyRefreshIfNeeded(String? currentToken) async {
    if (currentToken == null || onGetTokenExpiry == null) return null;

    final expiry = onGetTokenExpiry!(currentToken);
    if (expiry == null) return null;

    final remaining = expiry.difference(DateTime.now());
    if (remaining > proactiveRefreshThreshold) {
      _log(
        'Token valid (expires in ${remaining.inMinutes}min), skip proactive refresh',
      );
      return null;
    }

    _log(
      'Token expiring soon (${remaining.inMinutes}min left), proactively refreshing',
    );
    return refreshIfNeeded();
  }

  /// 重置状态（登出时调用）
  void reset() {
    _lastRefreshTime = null;
    _lastToken = null;
    // 不清理 _completer，让正在等待的请求正常结束
  }

  bool _isWithinReuseWindow() {
    final lastTime = _lastRefreshTime;
    if (lastTime == null) return false;
    return DateTime.now().difference(lastTime) < reuseWindow;
  }

  void _log(String message) {
    onLog?.call(message, tag: 'TokenRefresh');
  }
}
