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

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


/// 重试拦截器
///
/// 两层重试机制：
///
/// 1. **Token 刷新重试**（onResponse）
///    检测 Token 过期响应 → 触发刷新回调 → 用新 Token 重试原请求
///
/// 2. **瞬态错误重试**（onError）
///    5xx / 超时 / 连接失败 → 指数退避 + jitter → 自动重试
///    由 [ApiConfig.maxRetries] 控制（默认 0 = 不启用）
///
/// 两层独立运作，可叠加。
class RetryInterceptor extends Interceptor {
  final ApiConfig config;
  final Dio dio;

  /// Token 刷新锁（防止多个请求同时刷新）
  bool _isRefreshing = false;
  Completer<bool>? _refreshCompleter;

  final _random = Random();

  RetryInterceptor({required this.config, required this.dio});

  // ── Token 刷新重试 ────────────────────────────────────────────────────────

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (response.data is! Map<String, dynamic>) {
      handler.next(response);
      return;
    }

    final data = response.data as Map<String, dynamic>;
    final code = _parseCode(data['code']);

    // 检查强制登出
    if (config.forceLogoutCodes.contains(code)) {
      config.onLog?.call(
        'Force logout detected (code: $code)',
        tag: 'Network',
      );
      config.onForceLogout?.call();
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          message: 'Force logout (code: $code)',
        ),
      );
      return;
    }

    // 检查 Token 过期
    if (config.tokenExpiredCodes.contains(code)) {
      config.onLog?.call(
        'Token expired (code: $code), refreshing...',
        tag: 'Network',
      );
      _handleTokenExpired(response, handler);
      return;
    }

    handler.next(response);
  }

  /// 处理 Token 过期：刷新 + 重试
  Future<void> _handleTokenExpired(
      Response response,
      ResponseInterceptorHandler handler,
      ) async {
    final refreshSuccess = await _refreshToken();

    if (!refreshSuccess) {
      config.onLog?.call('Token refresh failed', tag: 'Network');
      config.onForceLogout?.call();
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          message: 'Token refresh failed',
        ),
      );
      return;
    }

    // 刷新成功，用新 token 重试原请求
    config.onLog?.call('Token refreshed, retrying...', tag: 'Network');
    try {
      final options = response.requestOptions;
      // 更新 header 中的 token
      options.headers['token'] = config.token;

      final retryResponse = await dio.fetch(options);
      handler.resolve(retryResponse);
    } on DioException catch (e) {
      handler.reject(e);
    }
  }

  /// Token 刷新（串行锁）
  /// 多个请求同时过期时，只刷新一次，其余等待
  Future<bool> _refreshToken() async {
    if (_isRefreshing) {
      // 等待正在进行的刷新
      return _refreshCompleter?.future ?? Future.value(false);
    }

    _isRefreshing = true;
    _refreshCompleter = Completer<bool>();

    try {
      if (config.onTokenRefresh == null) {
        _refreshCompleter!.complete(false);
        return false;
      }

      final newToken = await config.onTokenRefresh!();
      final success = newToken != null;

      if (success) {
        config.updateToken(newToken);
      }

      _refreshCompleter!.complete(success);
      return success;
    } catch (e) {
      _refreshCompleter!.complete(false);
      return false;
    } finally {
      _isRefreshing = false;
      _refreshCompleter = null;
    }
  }

  // ── 瞬态错误重试（指数退避 + jitter）────────────────────────────────────

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    if (config.maxRetries <= 0 || !_isRetryable(err)) {
      handler.next(err);
      return;
    }

    final options = err.requestOptions;
    final attempt = (options.extra['_retryAttempt'] as int?) ?? 0;

    if (attempt >= config.maxRetries) {
      handler.next(err);
      return;
    }

    options.extra['_retryAttempt'] = attempt + 1;

    final delayMs = _backoffDelay(attempt);
    config.onLog?.call(
      'Transient error, retry ${attempt + 1}/${config.maxRetries} '
          'in ${delayMs}ms: ${options.path}',
      tag: 'Retry',
    );

    await Future<void>.delayed(Duration(milliseconds: delayMs));

    try {
      handler.resolve(await dio.fetch(options));
    } on DioException catch (e) {
      handler.reject(e);
    }
  }

  /// 判断是否可重试的瞬态错误
  bool _isRetryable(DioException err) {
    switch (err.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.receiveTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.connectionError:
        return true;
      case DioExceptionType.badResponse:
      // 5xx 服务端错误可重试
        final statusCode = err.response?.statusCode;
        return statusCode != null && statusCode >= 500;
      default:
        return false;
    }
  }

  /// 指数退避 + jitter
  ///
  /// delay = min(baseDelay * 2^attempt, 30s) + random(0, delay * 25%)
  int _backoffDelay(int attempt) {
    final baseMs = config.retryBaseDelay.inMilliseconds;
    final exponentialMs = min(baseMs * pow(2, attempt).toInt(), 30000);
    final jitterMs = _random.nextInt((exponentialMs * 0.25).toInt().clamp(1, 7500));
    return exponentialMs + jitterMs;
  }

  int _parseCode(dynamic code) {
    if (code is int) return code;
    if (code is String) return int.tryParse(code) ?? 0;
    return 0;
  }
}
