part of services;


class HttpService extends GetxService {
  static HttpService get to => Get.find();
  late final Dio _dio;

  @override
  void onInit() {
    super.onInit();
    final options = BaseOptions(
      baseUrl: kServerUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds:10),
      headers: {},
      contentType: Headers.jsonContentType,
      responseType: ResponseType.json
    );

    _dio = Dio(options);
    _dio.interceptors.add(_RequestInterceptor());
    _dio.interceptors.add(_CacheInterceptor());
  }

  /// 组织 headers 参数
  Map<String,dynamic>? _getHeaders({bool excludeToken = false,Map<String,dynamic>? params,String? url}) {
    final headers = <String, dynamic>{};
    headers['appId'] = AppConfig.appID;
    headers['appSecret'] = AppConfig.appSecret;
    headers['timestamp'] = (DateTime.now().millisecondsSinceEpoch~/1000).toString();
    headers['url'] = kServerUrl + url.toString();
    headers['token'] = UserStore.to.token;
    
    if (Get.isRegistered<UserStore>() &&
        UserStore.to.hasToken && !excludeToken) {
      // headers['Token'] = UserStore.to.token;
      headers['Authorization'] = UserStore.to.token;
    } else{
      headers['Authorization'] = '';
    }
    Map<String,dynamic>? tempParams = {};
    if (params != null) {
      tempParams.addAll(params);
      tempParams.addAll(headers);
      headers['Sign'] = SignTool.createSign(tempParams);
    }
    else {
      headers['Sign'] = SignTool.createSign(headers);
    }
    // Console.log(headers);
    return headers;
  }

  /// get
  Future<ResponseModel> get(
        String url,{
        Map<String, dynamic>? params,
        Options? options,
        CancelToken? cancelToken,
        bool excludeToken = false,
        bool showLoading = false,
        bool cacheEnabled = true,
  }) async {
    if (showLoading) CustomToast.loading();
    try {
      final requestOptions = options ?? Options();
      requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);

      // 如果启用缓存，将cacheEnabled参数添加到请求选项中
      if(cacheEnabled){
        requestOptions.extra ??= {};
        requestOptions.extra!['cacheEnabled'] = true;
      }
      final response = await _dio.get(
        url,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken,
      );
      if (showLoading) CustomToast.dismiss();
      return ResponseModel.fromJson(response.data);
    } catch (error){
      if (error is! DioException) CustomToast.dismiss();
      rethrow;
    }
  }

  /// post
  Future<ResponseModel> post(
        String url,{
        Map<String,dynamic>? params,
        Options? options,
        CancelToken? cancelToken,
        bool excludeToken = false,
        bool showLoading = false,
        bool cacheEnabled = true,
  }) async{
    if (showLoading) CustomToast.loading();
    try {
      final requestOptions = options ?? Options();
      requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
      Console.log('----headers------${requestOptions.headers}\n----params------$params-----');
      // 如果启用缓存，将cacheEnabled参数添加到请求选项中
      if(cacheEnabled){
        requestOptions.extra ??= {};
        requestOptions.extra!['cacheEnabled'] = true;
      }
      final response = await _dio.post(
        url,
        data: params,
        options: requestOptions,
        cancelToken: cancelToken,
      );
      if (showLoading) CustomToast.dismiss();
      Console.log(response.data);
      return ResponseModel.fromJson(response.data);
    } catch (error){
      if (error is! DioException) CustomToast.dismiss();
      rethrow;
    }
  }

  /// upload
  Future<ResponseModel> upload(
        String url, {
        required String path,
        required fileTypes,
        Options? options,
        CancelToken? cancelToken,
        bool excludeToken = false,
        ProgressCallback? onSendProgress,
  })async {
    final requestOptions = options ?? Options();
    requestOptions.headers = _getHeaders(excludeToken: excludeToken,url:url);
    final name = path.substring(path.lastIndexOf('/') + 1,path.length);
    final image = await MultipartFile.fromFile(path, filename: name);
    final formData = FormData.fromMap({
      'files':image,
      'file_types':fileTypes
    });
    final response = await _dio.post(
      url,
      data:formData,
      options:requestOptions,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
    );
    return ResponseModel.fromJson(response.data);
  }


  /// download
  Future<void> download(
        String url,{
        required String savePath,
        Options? options,
        CancelToken? cancelToken,
        bool excludeToken = false,
        ProgressCallback? onReceiveProgress,
  }) async {
    try{
      final requestOptions = options ?? Options();
      requestOptions.headers = _getHeaders(excludeToken: excludeToken,url: url);
      requestOptions.responseType = ResponseType.bytes;
      await _dio.download(
          url,
          savePath,
          options: requestOptions,
          cancelToken: cancelToken,
          onReceiveProgress: onReceiveProgress
      );

      Console.log('Download completed:$savePath');
    }catch(e){
       Console.log('Error during download: $e');
       rethrow;
    }
  }
}




class _RequestInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    // final responseData = response.data;
    // if (responseData is Map && responseData.containsKey('code')) {
    //   final code = responseData['code'];
    //   if (code != 200) {
    //     handler.reject(
    //       DioException(
    //         requestOptions: response.requestOptions,
    //         response: response,
    //         type: DioExceptionType.badResponse,
    //       ),
    //       true,
    //     );
    //     return;
    //   }
    // } else if (response.requestOptions.responseType == ResponseType.bytes) {
    //
    // }
    // super.onResponse(response, handler);
    if (response.data['code'] != 200) {
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          type: DioExceptionType.badResponse,
        ),
        true,
      );
    }else {
      super.onResponse(response, handler);
    }
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    // Console.log(err.type);
    switch (err.type) {
      case DioExceptionType.connectionTimeout:
        CustomToast.fail('网络连接超时');
        break;
      case DioExceptionType.sendTimeout:
        CustomToast.fail('发送超时');
        break;
      case DioExceptionType.receiveTimeout:
        CustomToast.fail('接收超时');
        break;
      case DioExceptionType.badCertificate:
        CustomToast.fail('证书错误');
        break;
      case DioExceptionType.badResponse:
        final response = err.response;
        final statusCode = response?.statusCode;
        final code = int.tryParse(response?.data['code']?.toString() ?? '');
        Console.log('*************     ${response?.data}');
        Console.log('******statusCode*******     $statusCode');
        // Console.log(response?.data);
        var msg = '服务器错误';
        switch (statusCode) {
          case 403:
            Console.log('----------403---------access_token-------------------------${UserStore.to.accessToken}--------------------');
            msg = '$statusCode - Unauthorized';
            final newToken = await refreshToken();
            Console.log('newToken----------$newToken');
            if (newToken != null && newToken.isNotEmpty) {
              final RequestOptions requestOptions = err.requestOptions;
              final headers = requestOptions.headers;
              headers['Authorization'] = newToken;

              // 更新请求体参数（如果有的话）
              if (requestOptions.data is Map<String, dynamic>) {
                final bodyParams = requestOptions.data as Map<String, dynamic>;
                bodyParams.addAll(headers);
                headers['Sign'] = SignTool.createSign(bodyParams);
              } else {
                final newParams = Map<String, dynamic>.from(headers);
                headers['Sign'] = SignTool.createSign(newParams);
              }

              try {
                final newResponse = await Dio().fetch(requestOptions);
                handler.resolve(newResponse);
              }catch (e) {
                handler.reject(err);
              }

            } else {
              UserStore.to.logout();
              CustomToast.fail('该账号已在其他设备登录，请重新登录');
              Console.log('-------------------access_token-------------------------${UserStore.to.accessToken}--------------------');

            }
            break;
          case 404:
            msg = '$statusCode - Server not found';
            // CustomToast.fail(msg);
            break;
          case 500:
            msg = '$statusCode - Server error';
            // CustomToast.fail(msg);
            break;
          case 502:
            msg = '$statusCode - Bad gateway';
            // CustomToast.fail(msg);
            break;
          default:
            msg = response?.data?['message']?.toString() ?? msg;
            if(code == 3001){
              // Toast.show(msg);
              CustomToast.info(msg);
            }
            else{
              CustomToast.fail(msg);
            }
            break;
        }
        break;
      case DioExceptionType.cancel:
        Console.log('请求取消');
        break;
      case DioExceptionType.connectionError:
        CustomToast.fail('网络连接失败');
        break;
      case DioExceptionType.unknown:
        CustomToast.fail('请求发生未知错误');
        break;
    }
    super.onError(err, handler);
  }

  Future<String?> refreshToken() async {

    Console.log('--------refreshToken----------------');
    final result = await HttpService.to.post(
      '/v1/members/login/getToken',
      params: {
        'access_token':StorageService.to.getString(kLocalAccessToken)
      }
    );
    if (result.data is Map) {
      final String token = result.data['token'];
      final String accessToken = result.data['access_token'];
      if(token.isEmpty && accessToken.isEmpty){
        UserStore.to.logout();
        return null;
      }
      await Future.wait([
        UserStore.to.setToken(result.data['token']),
        UserStore.to.setAccessToken(result.data['access_token']),
      ]);
      return result.data['token'];
    }
    // 在这里执行刷新token的逻辑
    // 如果刷新成功，返回新的token；如果刷新失败，返回null
    return null;
  }

}

// 缓存拦截器
class _CacheInterceptor extends Interceptor {

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {

    final cacheEnabled = options.extra['cacheEnabled'] ?? false;
    final  status = await Tools.checkCurrentNetStatus();
    if (cacheEnabled && !status) {
      // 在发起请求之前，检查缓存是否存在有效数据
      String cacheKey = '${options.uri.toString()}?${options.data}';
      final cachedFile = await DefaultCacheManager().getFileFromCache(cacheKey);
      // Console.log('获取数据----------cacheKey-----------------------$cacheKey');
      if (cachedFile != null && cachedFile.validTill.isAfter(DateTime.now())) {
        // 如果缓存有效，直接返回缓存数据
        final cachedData = await cachedFile.file.readAsBytes();
        final decodedData = utf8.decode(cachedData); // 将字节列表解码为字符串
        final jsonData = jsonDecode(decodedData);
        handler.resolve(Response(
          requestOptions: options,
          statusCode: 200,
          data: jsonData,
        ));
        return;
      }
    }
    // 如果缓存不存在或已过期，或未启用缓存，继续发起网络请求
    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    // 只缓存状态码为200的响应
    if (response.statusCode == 200) {
      try {
        // 获取请求的 URL
        final url = response.requestOptions.uri.toString();
        // 获取请求体参数（如果是 POST 请求）
        final requestBody = response.requestOptions.data.toString();
        // 将 GET 请求的参数和请求体参数拼接成缓存的键
        final cacheKey = requestBody.isEmpty ? url : '$url?$requestBody';
        // Console.log('----------cacheKey-----------------------$cacheKey');
        // 将响应数据转换为字符串，并将其编码为字节列表
        List<int> bytes = utf8.encode(jsonEncode(response.data));
        Uint8List uint8List = Uint8List.fromList(bytes);
        // 创建一个缓存文件并将数据写入其中
        DefaultCacheManager().putFile(
          cacheKey,
          uint8List,
          fileExtension: '.json', // 可以根据需求修改文件扩展名
          maxAge: const Duration(hours: 12)
        );
      } catch (e) {
        Console.log('Error caching response: $e');
      }
    }
    super.onResponse(response, handler);
  }
}


