import 'network_callbacks.dart';

/// API 配置
/// 非单例，由 App 层构造并注入到 ApiClient
class ApiConfig {
  /// 基础 URL（来自 config.json → AppConfig.apiBaseUrl）
  String baseURL;

  /// 当前 token（内存持有，App 层负责持久化）
  String? token;

  /// 平台相关 headers（App 层注入：version、platform、channel 等）
  Map<String, String> platformHeaders;

  // ── 认证回调 ──

  /// Token 过期时的刷新回调
  final OnTokenRefresh? onTokenRefresh;

  /// Token 更新后的通知回调
  ///
  /// 在 [updateToken] 被调用且新 token 非空时触发。
  /// App 层用于同步 token 到 WebSocket 等其他模块。
  final void Function(String newToken)? onTokenUpdated;

  // ── 基础回调 ──

  /// 日志输出回调（不设置则不输出日志）
  final OnLog? onLog;

  /// 网络可用性查询（App 层注入，请求前调用）
  ///
  /// 与 [SocketConfig.onCheckNetworkAvailable] 对称。
  /// 返回 true 表示网络可用，可以发起请求；
  /// 返回 false 则直接抛 [ApiError.noNetworkConnection]，不走网络。
  final OnCheckNetworkAvailable? onCheckNetworkAvailable;

  // ── 加密回调（预留给 cipher_guard_sdk）──

  /// 请求体加密回调，null 时不加密
  final OnEncryptRequest? onEncryptRequest;

  /// 响应体解密回调，null 时不解密
  final OnDecryptResponse? onDecryptResponse;

  // ── 业务错误回调 ──

  /// 业务错误拦截回调
  ///
  /// 在 token 过期 / 强制登出判断之后执行。
  /// 返回 true = App 层已处理，SDK 正常传递响应；
  /// 返回 false = 未处理，SDK 继续正常流程。
  final OnBusinessError? onBusinessError;

  /// 响应变换回调
  ///
  /// 在 `executeRequest` 解码前调用，App 层可以统一解包
  /// `{ code, data, message }` 结构。返回 null 表示不变换。
  final OnTransformResponse? onTransformResponse;

  // ── 重试配置 ──

  /// 瞬态错误最大重试次数（5xx / 超时 / 连接失败）
  ///
  /// 0 = 不重试（默认），设为 3 启用重试。
  /// 与 Token 刷新重试独立，两者可叠加。
  final int maxRetries;

  /// 重试基础延迟（指数退避起点）
  ///
  /// 实际延迟 = min(baseDelay * 2^attempt, 30s) + jitter
  final Duration retryBaseDelay;

  // ── Token 刷新配置 ──

  /// Token 刷新超时时间，防止 onTokenRefresh 卡住导致请求永远阻塞
  final Duration tokenRefreshTimeout;

  /// Token 刷新时间窗口：刷新成功后此时间内再次收到过期码直接返回成功，
  /// 避免服务端同步延迟导致的误判
  final Duration tokenReuseWindow;

  // ── 主动刷新配置 ──

  /// Token 过期时间解析回调
  ///
  /// App 层解析 JWT `exp` claim，用于主动刷新判断。
  /// 未注入时不启用主动刷新。
  final OnGetTokenExpiry? onGetTokenExpiry;

  /// 主动刷新阈值：距过期不足此时间时提前刷新
  ///
  /// 默认 1 小时。WebSocket 重连前、App 回前台时
  /// 自动检查并刷新即将过期的 token，避免带过期 token 发起请求。
  final Duration proactiveRefreshThreshold;

  ApiConfig({
    required this.baseURL,
    this.token,
    this.platformHeaders = const {},
    this.onTokenRefresh,
    this.onTokenUpdated,
    this.onLog,
    this.onCheckNetworkAvailable,
    this.onEncryptRequest,
    this.onDecryptResponse,
    this.onBusinessError,
    this.onTransformResponse,
    this.maxRetries = 0,
    this.retryBaseDelay = const Duration(seconds: 1),
    this.tokenRefreshTimeout = const Duration(seconds: 10),
    this.tokenReuseWindow = const Duration(seconds: 3),
    this.onGetTokenExpiry,
    this.proactiveRefreshThreshold = const Duration(hours: 1),
  });

  /// 构建默认 headers
  /// [includeToken] — 是否注入 token
  /// [customHeaders] — 单次请求自定义 header（优先级最高）
  Map<String, String> defaultHeaders({
    bool includeToken = true,
    Map<String, String>? customHeaders,
  }) {
    final headers = <String, String>{
      'content-type': 'application/json; charset=utf-8',
      'accept': 'application/json',
      'keep-alive': 'timeout=60',
      // Unix 时间戳（秒），整数值，非格式化日期字符串
      'timestamp': '${DateTime.now().millisecondsSinceEpoch ~/ 1000}',
      'app-request-id': _generateRequestId(),
    };

    // 合并平台 headers（App 层注入的 version、platform 等）
    headers.addAll(platformHeaders);

    // Token 注入
    if (includeToken && token != null && token!.isNotEmpty) {
      headers['token'] = token!;
    }

    // 单次请求自定义 header（覆盖默认值）
    if (customHeaders != null) {
      headers.addAll(customHeaders);
    }

    return headers;
  }

  /// 更新 token
  ///
  /// 同时触发 [onTokenUpdated] 通知其他模块（如 WebSocket）同步 token。
  void updateToken(String? newToken) {
    token = newToken;
    if (newToken != null && newToken.isNotEmpty) {
      onTokenUpdated?.call(newToken);
    }
  }

  /// 更新 base URL
  void updateBaseURL(String newBaseURL) {
    baseURL = newBaseURL;
  }

  /// 生成请求 ID（用于幂等性）
  String _generateRequestId() {
    final now = DateTime.now().microsecondsSinceEpoch;
    return '$now${Object().hashCode}';
  }
}
