import 'dart:io';

import 'package:dio/dio.dart';
import 'package:get/get_utils/get_utils.dart';

import '../models/index.dart';
import '../widgets/index.dart';
import 'loading.dart';
import 'log_util.dart';
import 'o2_api_manager.dart';
import 'o2_utils.dart';

class O2Interceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // print("\n================================= 请求数据 =================================");
    // print("method = ${options.method.toString()}");
    // print("url = ${options.uri.toString()}");
    // print("headers = ${options.headers}");
    // print("params = ${options.queryParameters}");
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    //  print(
    //       "\n================================= 响应数据开始 =================================");
    //   print("code = ${response.statusCode}");
    //   print("data = ${response.data}");
    //   print(
    //       "================================= 响应数据结束 =================================\n");
    super.onResponse(response, handler);
  }

  @override
  onError(DioError err, ErrorInterceptorHandler handler) {
    try {
      var code = err.response?.statusCode;
      if (code == 500) {
        List<String>? type = err.response?.headers['Content-Type'];
        if (type != null &&
            (type.contains('application/json') ||
                type.contains('application/json;charset=utf-8'))) {
          ApiResponse a = ApiResponse.fromJson(err.response?.data);
          if (a.message != null && a.message!.isNotEmpty) {
            Loading.toast(a.message!);
          }
          return super.onError(
              DioError(
                  requestOptions: err.requestOptions,
                  response: err.response,
                  error: a.message),
              handler);
        }
      } else if (err.response?.statusCode == 401) { // token过期 需要重新登录
        OLogger.e('401 异常 ${err.response.toString()}');
        O2OverlayEntryDialog.instance.openAlertDialog(
            'error'.tr,
            'common_session_timeout'.tr, callback: () => O2Utils.logout(hasToken: false));
      } else {
        OLogger.e(err);
      }
    } catch (e) {
      OLogger.e(e);
    }
    // Loading.toast('request_error'.tr);
    return super.onError(err, handler);
  }
}

class O2HttpClient {
  static final O2HttpClient instance = O2HttpClient._internal();

  factory O2HttpClient() {
    return instance;
  }

  O2HttpClient._internal() {
    //初始化
    _dio = Dio();
    _dio.interceptors.add(O2Interceptor());
  }

  late Dio _dio;
  get dio {
    return _dio;
  }

  ///
  /// post方法 json
  ///
  Future<ApiResponse> post(String url, Map<String, dynamic> data,
      {bool needToken = true}) async {
    Response<dynamic> response =
        await _dio.post(url, data: data, options: _postJsonOptions(needToken));
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// get方法 json
  ///
  Future<ApiResponse> get(String url, {bool needToken = true, CancelToken? cancelToken}) async {
    Response<dynamic> response =
        await _dio.get(url, options: _getJsonOptions(needToken), cancelToken: cancelToken);
    return ApiResponse.fromJson(response.data);
  }

  /// 不转 ApiResponse  对象 外部使用的
  Future<Response<dynamic>> getOuter(String url) async {
    return await _dio.get(url, options: _getJsonOptions(false));
  }

  getWithCallback(String url, Function(ApiResponse?, dynamic) callback, {bool needToken = true}) async {
    _dio.get(url, options: _getJsonOptions(needToken)).then((response) {
      OLogger.d('执行了then');
      callback(ApiResponse.fromJson(response.data), null);
    }).catchError((e) {
      OLogger.d('执行了异常');
      callback(null, e);
    });
  }

  ///
  /// put 方法 json
  ///
  Future<ApiResponse> put(String url, Map<String, dynamic> data,
      {bool needToken = true, CancelToken? cancelToken}) async {
    var mock = _needChange2Mock(url, "put");
    if (mock != null) {
      var newUrl = "$url/${mock.append}";
      var to = mock.to;
      if (to != null && to.isNotEmpty) {
        if (to.toLowerCase() == 'post') {
          // put 方法转 post
          Response<dynamic> response = await _dio.post(newUrl,
              data: data, options: _putJsonOptions(needToken, newMethod: to));
          return ApiResponse.fromJson(response.data);
        }
      }
    }
    Response<dynamic> response =
        await _dio.put(url, data: data, options: _putJsonOptions(needToken), cancelToken: cancelToken);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// delete方法 json
  ///
  Future<ApiResponse> delete(String url, {bool needToken = true}) async {
    var mock = _needChange2Mock(url, "delete");
    if (mock != null) {
      var newUrl = "$url/${mock.append}";
      var to = mock.to;
      if (to != null && to.isNotEmpty) {
        if (to.toLowerCase() == 'get') {
          // delete 方法转 get
          Response<dynamic> response = await _dio.get(newUrl,
              options: _deleteJsonOptions(needToken, newMethod: to));
          return ApiResponse.fromJson(response.data);
        } else if (to.toLowerCase() == 'post') {
          // delete 方法转 post
          Response<dynamic> response = await _dio.post(newUrl,
              data: null,
              options: _deleteJsonOptions(needToken, newMethod: to));
          return ApiResponse.fromJson(response.data);
        }
      }
    }
    Response<dynamic> response =
        await _dio.delete(url, options: _deleteJsonOptions(needToken));
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// PUT方法上传文件
  ///
  Future<ApiResponse> putUploadFile(String url, File file,
      {Map<String, dynamic>? body, void Function(int, int)? onSendProgress}) async {
    var fileName = _filename(file);
    Map<String, dynamic> form = {};
    if (body != null) {
      form = body;
    }
    form["file"] = await MultipartFile.fromFile(file.path, filename: fileName);
    FormData formData = FormData.fromMap(form);
    // FormData formData = FormData.fromMap({
    //   "file": await MultipartFile.fromFile(file.path, filename: fileName),
    // });
    Response<dynamic> response =
        await _dio.put(url, data: formData, options: _putJsonOptions(true), onSendProgress: onSendProgress);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// POST方法上传文件
  ///
  Future<ApiResponse> postUploadFile(String url, File file,
      {Map<String, dynamic>? body, void Function(int, int)? onSendProgress}) async {
    var fileName = _filename(file);
    Map<String, dynamic> form = {};
    if (body != null) {
      form = body;
    }
    form["file"] = await MultipartFile.fromFile(file.path, filename: fileName);
    FormData formData = FormData.fromMap(form);
    Response<dynamic> response =
        await _dio.post(url, data: formData, options: _postJsonOptions(true), onSendProgress: onSendProgress);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// 下载文件
  ///
  Future<Response> downloadFile(String url, String savePath,
      {ProgressCallback? onReceiveProgress}) async {
    Map<String, String> header = {};
    header['x-client'] = _xClient();
    if (O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    header[HttpHeaders.acceptEncodingHeader] = "*";
    Options options = Options(headers: header);
    Response response = await _dio.download(url, savePath,
        onReceiveProgress: onReceiveProgress, options: options);
    return response;
  }

  ///
  /// 下载文件
  /// 不知道文件名称的情况
  ///
  Future<String> downloadFileNoFileName(String url, String saveFolderPath,
      {ProgressCallback? onReceiveProgress}) async {
    Map<String, String> header = {};
    header['x-client'] = _xClient();
    if (O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    header[HttpHeaders.acceptEncodingHeader] = "*";
    Options options = Options(headers: header);
    String fileLocalPath = '';
    await _dio.download(url, (Headers headers) {
      var fileName = headers.value("Content-Disposition");
      if (fileName != null && fileName.isNotEmpty) {
        var i = fileName.lastIndexOf("''");
        if (i > -1) {
          fileName = fileName.substring(i + "''".length, fileName.length);
        }
      }
      if (fileName == null || fileName.isEmpty == true) {
        fileName = 'unknow';
      }
      fileLocalPath = '$saveFolderPath/$fileName';
      return fileLocalPath;
    }, onReceiveProgress: onReceiveProgress, options: options);
    return fileLocalPath;
  }

  String _filename(File file) {
    if (file.existsSync()) {
      var index = file.path.lastIndexOf(Platform.pathSeparator);
      return file.path.substring(index + 1, file.path.length);
    }
    return '';
  }

  ///
  ///是否需要替换方法类型
  ///比如 put转post
  ///
  MockMode? _needChange2Mock(String url, String currentMethod) {
    var mock = O2ApiManager.instance.centerServerInfo?.mockConfig?.mock;
    MockMode? mm;
    if (mock != null) {
      for (var element in mock.keys) {
        if (url.contains(element)) {
          // 判断是需要转化的模块
          var mode = mock[element] as Map<String, MockMode>;
          for (var method in mode.keys) {
            if (currentMethod.toLowerCase() == method.toLowerCase()) {
              // 请求方法相同
              mm = mode[method];
              continue;
            }
          }
          continue;
        }
      }
    }
    return mm;
  }

  Options _deleteJsonOptions(bool needToken, {String? newMethod}) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: newMethod == null || newMethod.isEmpty ? 'DELETE' : newMethod,
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  Options _getJsonOptions(bool needToken) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: 'GET', headers: header, contentType: ContentType.json.mimeType);
  }

  Options _postJsonOptions(bool needToken) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: 'POST',
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  Options _putJsonOptions(bool needToken, {String? newMethod}) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: newMethod == null || newMethod.isEmpty ? 'PUT' : newMethod,
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  String _xClient() {
    String xClient = "flutter";
    if (Platform.isAndroid) {
      xClient = 'android';
    } else if (Platform.isIOS) {
      xClient = 'ios';
    } 
    return xClient;
  }
}
