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

/// 加密拦截器（预留给 cipher_guard_sdk）
///
/// 在拦截器链中位于最末位：
/// onRequest 顺序：`Auth → Custom → Retry → Logging → Encryption`
/// onResponse 逆序：`Encryption(解密) → Logging → Retry(业务码) → Custom → Auth`
///
/// 放最后是因为 Dio onResponse 按逆序执行——加密拦截器最先解密，
/// 后续的 RetryInterceptor 才能正确判断业务错误码、Token 过期等。
///
/// 回调为 null 时自动跳过，不影响正常请求流程。
/// 后续 cipher_guard_sdk 接入后，App 层在 ApiConfig 中注入
/// `onEncryptRequest` / `onDecryptResponse` 即可启用加密。
///
/// ## 加密能力
///
/// 与简单的 body 加解密不同，本拦截器支持完整的请求改写：
/// - 路径加密（如 `/api/login` → `/api/hex(encrypt(login))`）
/// - 请求体加密（Map → base64 字符串）
/// - Header 注入（X-Token、X-Signature、secret-key 等）
/// - Content-Type 覆盖（application/json → text/plain）
///
/// 加密回调接收原始 path、headers、body，返回 [EncryptedRequest]，
/// 拦截器根据非 null 字段覆盖请求。
///
/// ## Token 重试与重新加密
///
/// 瞬态错误重试（5xx / 超时）：复用已加密的请求，不重复加密。
/// Token 刷新重试：加密 headers 可能包含过期 token 衍生值
/// （如 X-Token、X-Signature），需要恢复加密前状态并用新 token 重新加密。
/// RetryInterceptor 通过 `_needsReEncryption` 标记通知本拦截器重新加密。
class EncryptionInterceptor extends Interceptor {
  /// extra 标记键：请求已加密，瞬态重试时跳过
  static const _encryptedKey = '__encrypted__';

  /// extra 标记键：加密前的请求快照（path / body / contentType）
  static const _preEncryptSnapshotKey = '__preEncryptSnapshot__';

  /// extra 标记键：加密回调注入的 header key 列表
  static const _encryptionAddedHeadersKey = '__encryptionAddedHeaders__';

  final ApiConfig _config;

  EncryptionInterceptor(this._config);

  @override
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) async {
    final encrypt = _config.onEncryptRequest;
    if (encrypt == null) {
      handler.next(options);
      return;
    }

    // Token 重试 + 已加密 → 恢复加密前状态，用新 token 上下文重新加密
    // 旧的加密 headers（如 X-Token、X-Signature）可能包含过期 token 信息
    if (options.extra[_encryptedKey] == true &&
        options.extra['_needsReEncryption'] == true) {
      _restorePreEncryptState(options);
      options.extra.remove('_needsReEncryption');
    }

    // 已加密（瞬态错误重试）→ 复用加密请求，不重复加密
    if (options.extra[_encryptedKey] == true) {
      handler.next(options);
      return;
    }

    try {
      // 保存加密前快照，Token 重试时恢复
      options.extra[_preEncryptSnapshotKey] = {
        'path': options.path,
        'data': options.data,
        'contentType': options.contentType,
      };

      // 收集当前 headers（转为 Map<String, String>）
      final currentHeaders = <String, String>{};
      options.headers.forEach((key, value) {
        if (value != null) currentHeaders[key] = value.toString();
      });

      final result = await encrypt(options.path, currentHeaders, options.data);

      // 根据非 null 字段覆盖请求
      if (result.path != null) {
        options.path = result.path!;
      }
      if (result.body != null) {
        options.data = result.body;
      }
      if (result.headers != null) {
        options.headers.addAll(result.headers!);
        // 记录加密注入的 header key，Token 重试时移除
        options.extra[_encryptionAddedHeadersKey] = result.headers!.keys
            .toList();
      }
      if (result.contentType != null) {
        options.contentType = result.contentType;
      }

      // 标记已加密，防止瞬态重试时重复加密
      options.extra[_encryptedKey] = true;

      _config.onLog?.call(
        'Request encrypted: ${options.path}',
        tag: 'Encryption',
      );

      handler.next(options);
    } catch (e) {
      _config.onLog?.call('Request encryption failed: $e', tag: 'Encryption');
      handler.reject(
        DioException(
          requestOptions: options,
          message: 'Request encryption failed: $e',
        ),
      );
    }
  }

  /// 恢复加密前的请求状态
  ///
  /// Token 重试场景：旧的加密数据（path / body / headers）可能包含过期 token，
  /// 需要恢复原始状态后用新 token 上下文重新加密。
  ///
  /// AuthInterceptor 已在本轮重试中注入了新 token headers，
  /// 这里只需移除上次加密注入的 headers（如 X-Token、X-Signature），
  /// 保留 Auth 设置的新 token。
  void _restorePreEncryptState(RequestOptions options) {
    final snapshot =
        options.extra[_preEncryptSnapshotKey] as Map<String, dynamic>?;
    if (snapshot != null) {
      options.path = snapshot['path'] as String;
      options.data = snapshot['data'];
      options.contentType = snapshot['contentType'] as String?;
    }

    // 移除上次加密注入的 headers
    final addedHeaders = options.extra[_encryptionAddedHeadersKey] as List?;
    if (addedHeaders != null) {
      for (final key in addedHeaders) {
        options.headers.remove(key);
      }
    }

    // 清除加密标记和快照
    options.extra.remove(_encryptedKey);
    options.extra.remove(_encryptionAddedHeadersKey);

    _config.onLog?.call(
      'Pre-encrypt state restored for token retry',
      tag: 'Encryption',
    );
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    final decrypt = _config.onDecryptResponse;
    if (decrypt == null) {
      handler.next(response);
      return;
    }

    // 跳过 null 响应
    if (response.data == null) {
      handler.next(response);
      return;
    }

    // 响应已是 Map → 未加密（health check、非加密端点等），跳过解密
    // 加密模式下响应通常是 String（base64）或 List<int>（bytes）
    // TODO: 接入加密后，若服务端所有端点都加密，可移除此判断
    if (response.data is Map<String, dynamic>) {
      handler.next(response);
      return;
    }

    try {
      final decrypted = await decrypt(response.data as Object);
      response.data = decrypted;

      _config.onLog?.call(
        'Response decrypted: ${response.requestOptions.path}',
        tag: 'Encryption',
      );

      handler.next(response);
    } catch (e) {
      _config.onLog?.call('Response decryption failed: $e', tag: 'Encryption');
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          message: 'Response decryption failed: $e',
        ),
      );
    }
  }
}
