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());
  }

  /// 组织 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();
    
    if (Get.isRegistered<UserStore>() &&
        UserStore.to.hasToken && !excludeToken) {
      // headers['Token'] = UserStore.to.token;
      headers['Authorization'] = UserStore.to.token;
    } else{
      headers['Authorization'] = '';
    }
    if (params != null) {
      params.addAll(headers);
    }
    headers['Sign'] = SignTool.createSign(params!);
    // 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,
  }) async {
    if (showLoading) CustomToast.loading();
    try {
      final requestOptions = options ?? Options();
      requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
      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,
  }) async{
    if (showLoading) CustomToast.loading();
    try {
      final requestOptions = options ?? Options();
      requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
      Console.log('----headers------${requestOptions.headers}');
      Console.log('----params------$params');
      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,
        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({'file':image});
    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);
      await _dio.download(
          url,
          savePath,
          options: requestOptions,
          cancelToken: cancelToken,
          onReceiveProgress: onReceiveProgress
      );
      Console.log('Download completed:$savePath');
    }catch(e){
       Console.log(e);
       rethrow;
    }
  }
}




class _RequestInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler 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;
        print('*************     ${response?.data}');
        // Console.log(response?.data);
        final code = int.tryParse(response?.data['code']?.toString() ?? '');
        var msg = '服务器错误';
        switch (statusCode) {
          case 401:
            msg = '$statusCode - Unauthorized';
            final newToken = await refreshToken();
            if (newToken != null) {
              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('登录已失效，请重新登录');
            }
            break;
          case 404:
            msg = '$statusCode - Server not found';
            break;
          case 500:
            msg = '$statusCode - Server error';
            break;
          case 502:
            msg = '$statusCode - Bad gateway';
            break;
          default:
            // if (code == 901) UserStore.to.logout();
            // msg = response?.data?['msg']?.toString() ?? msg;
            msg = response?.data?['message']?.toString() ?? msg;
            break;
        }
        CustomToast.fail(msg);
        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 {
    final result = await HttpService.to.post(
      '/v1/members/login/getToken',
    );
    if (result.data is! Map) {
      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;
  }

}


