import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:gmalpha_flutter/commonModel/cache/CacheManager.dart';
import 'package:gmalpha_flutter/commonModel/net/Api.dart';
import 'package:gmalpha_flutter/commonModel/toast/toast.dart';

const bool inProduction = const bool.fromEnvironment("dart.vm.product");

///Http配置.
class HttpConfig {
  /// constructor.
  HttpConfig({
    this.status,
    this.code,
    this.msg,
    this.data,
    this.options,
    this.pem,
    this.pKCSPath,
    this.pKCSPwd,
    this.nativeCookie,
  });

  /// BaseResp [String status]字段 key, 默认：status.
  String status;

  /// BaseResp [int code]字段 key, 默认：errorCode.
  String code;

  /// BaseResp [String msg]字段 key, 默认：errorMsg.
  String msg;

  /// BaseResp [T data]字段 key, 默认：data.
  String data;

  /// Options.
  BaseOptions options;

  /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
  /// PEM证书内容.
  String pem;

  /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
  /// PKCS12 证书路径.
  String pKCSPath;

  /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
  /// PKCS12 证书密码.
  String pKCSPwd;

  //缓存
  Map nativeCookie;
}

/// 单例 DioUtil.
/// debug模式下可以打印请求日志. DioUtil.openDebug().
/// dio详细使用请查看dio官网(https://github.com/flutterchina/dio).
class DioUtil {
  static final DioUtil _instance = DioUtil._init();
  static Dio _dio;

  /// BaseResp [String status]字段 key, 默认：status.
  String _statusKey = "status";

  /// BaseResp [int code]字段 key, 默认：error = 0 代表成功.
  String _codeKey = "error";

  /// BaseResp [String msg]字段 key, 默认：errorMsg.
  String _msgKey = "message";

  /// BaseResp [T data]字段 key, 默认：data.
  String _dataKey = "data";

  /// BaseResp [T data]字段 key, 默认：extra.
  String _extraKey = 'extra';

  // BaseResp [T data]字段 key, 默认：user_type.
  String _userType = 'user_type';

  /// Options.
  static BaseOptions _options = getDefOptions();

  /// PEM证书内容.
  String _pem;

  /// PKCS12 证书路径.
  String _pKCSPath;

  /// PKCS12 证书密码.
  String _pKCSPwd;

  String _proxy = '172.30.9.117:8888';

  static Map<String, dynamic> addHeadMap;

  /// 是否是debug模式.
  static bool _isDebug = !inProduction;

  static DioUtil getInstance() {
    return _instance;
  }

  factory DioUtil() {
    return _instance;
  }

  static var interceptor = InterceptorsWrapper(onRequest: (opt) {
    var queryParameters = opt.queryParameters;
    Map<String, dynamic> map;
    if (queryParameters != null) {
      map = queryParameters;
    } else {
      map = new Map<String, dynamic>();
    }
    map.putIfAbsent(
        "app_name",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("app_name") ?? "");
    map.putIfAbsent(
        "version",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("version") ?? "");
    map.putIfAbsent("platform", () => Platform.isAndroid ? "android" : "ios");
    map.putIfAbsent(
        "device_id",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("device_id") ??
            "");
    map.putIfAbsent(
        "os_version",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("os_version") ??
            "");
    map.putIfAbsent("model",
        () => CacheManager.getInstance().get(MEMORY_CACHE).get("model") ?? "");
    map.putIfAbsent("lat",
        () => CacheManager.getInstance().get(MEMORY_CACHE).get("lat") ?? "");
    map.putIfAbsent("lng",
        () => CacheManager.getInstance().get(MEMORY_CACHE).get("lng") ?? "");
    map.putIfAbsent(
        "channel",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("channel") ?? "");
    map.putIfAbsent(
        "current_city_id",
        () =>
            CacheManager.getInstance()
                .get(MEMORY_CACHE)
                .get("current_city_id") ??
            "");
    map.putIfAbsent(
        "manufacturer",
        () =>
            CacheManager.getInstance().get(MEMORY_CACHE).get("manufacturer") ??
            "");
    map.putIfAbsent("uuid",
        () => CacheManager.getInstance().get(MEMORY_CACHE).get("uuid") ?? "");
    opt.queryParameters = map;
  }, onResponse: (response) {
    print("响应之前  response${response}");
  }, onError: (e) {
    print("网络错误  $e message ${e.message}");
  });

  DioUtil._init() {
    _dio = new Dio(_options);
    _dio.interceptors.add(interceptor);
  }

  void setProxy(String proxy) {
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.findProxy = (url) {
        return 'PROXY $proxy';
      };
    };
  }

  /// set Config.
  void setConfig(HttpConfig config) {
    _statusKey = config.status ?? _statusKey;
    _codeKey = config.code ?? _codeKey;
    _msgKey = config.msg ?? _msgKey;
    _dataKey = config.data ?? _dataKey;
    _mergeOption(config.options);
    _mergeNativeCookie(config);
    _pem = config.pem ?? _pem;

    if (_dio != null) {
      _dio.options = _options;
//       (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
//              client.findProxy = (url) {
//            return _isDebug ? 'PROXY $_proxy' : 'DIRECT';
//          };
//       };
      if (_pem != null) {
        // httpClientAdapter
        (_dio.httpClientAdapter as DefaultHttpClientAdapter)
            .onHttpClientCreate = (client) {
          client.badCertificateCallback =
              (X509Certificate cert, String host, int port) {
            if (cert.pem == _pem) {
              // 证书一致，则放行
              return true;
            }
            return false;
          };
        };
      }
      if (_pKCSPath != null) {
        (_dio.httpClientAdapter as DefaultHttpClientAdapter)
            .onHttpClientCreate = (client) {
          SecurityContext sc = new SecurityContext();
          //file为证书路径
          sc.setTrustedCertificates(_pKCSPath, password: _pKCSPwd);
          HttpClient httpClient = new HttpClient(context: sc);
          return httpClient;
        };
      }
    }
  }

  /*
   * get请求
   */
  Future<Response> get(url, {data, options, cancelToken}) async {
    Response response;
    print("GET===> URL:$url   data:$data");
    try {
      response = await _dio.get(url,
          queryParameters: data, options: options, cancelToken: cancelToken);
//      print('get success---------${response.statusCode}');
//      print('get success---------${response.data}');
      _printHttpLog(response);
//      response.data; 响应体
//      response.headers; 响应头
//      response.request; 请求体
//      response.statusCode; 状态码
    } on DioError catch (e) {
      print('get error---------$e  formatError');
      formatError(e);
    }
    return response;
  }

  /*
   * post请求
   */
  Future<Response> post(url, {data, options, cancelToken}) async {
    Response response;
    print("POST===> URL:$url   data:$data");
    try {
      response = await _dio.post(url,
          data: FormData.from(data),
          options: options,
          cancelToken: cancelToken);
      print('post success---------${response.statusCode}  ${response.data}');
    } on DioError catch (e) {
      print('post error---------$e  message${e.message}');
      formatError(e);
    }
    return response;
  }

  /*
   * 下载文件
   */
  downloadFile(urlPath, savePath) async {
    Response response;
    try {
      response = await _dio.download(urlPath, savePath,
          onReceiveProgress: (int count, int total) {
        //进度
        print("$count $total");
      });
      print('downloadFile success---------${response.data}');
    } on DioError catch (e) {
      print('downloadFile error---------$e  formatError');
      formatError(e);
    }
    return response.data;
  }

  void formatError(DioError e) {
    String reason = "";
    if (e.type == DioErrorType.CONNECT_TIMEOUT) {
      // It occurs when url is opened timeout.
      print("连接超时");
      reason = "连接超时 ${e.message}";
    } else if (e.type == DioErrorType.SEND_TIMEOUT) {
      // It occurs when url is sent timeout.
      print("请求超时");
      reason = "请求超时 ${e.message}";
    } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
      //It occurs when receiving timeout
      print("响应超时");
      reason = "响应超时 ${e.message}";
    } else if (e.type == DioErrorType.RESPONSE) {
      // When the server response, but with a incorrect status, such as 404, 503...
      print("出现异常");
      reason = "出现异常 ${e.message}";
    } else if (e.type == DioErrorType.CANCEL) {
      // When the request is cancelled, dio will throw a error with this type.
      print("请求取消");
      reason = "请求取消 ${e.message}";
    } else {
      //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
      print("未知错误");
      reason = "未知错误 ${e.message}";
    }
    throw HttpException(reason);
  }

  Future<Response> download(
    String urlPath,
    savePath, {
    CancelToken cancelToken,
    data,
    Options options,
  }) {
    return _dio.download(urlPath, savePath,
        cancelToken: cancelToken, data: data, options: options);
  }

  /// check Options.
  Options _checkOptions(method, options) {
    if (options == null) {
      options = new Options();
    }
    options.method = method;
    return options;
  }

  /// merge Option.
  void _mergeOption(BaseOptions opt) {
    _options.method = opt.method ?? _options.method;
    _options.headers = (new Map.from(_options.headers))..addAll(opt.headers);
    _options.baseUrl = opt.baseUrl ?? _options.baseUrl;
    _options.connectTimeout = opt.connectTimeout ?? _options.connectTimeout;
    _options.receiveTimeout = opt.receiveTimeout ?? _options.receiveTimeout;
    _options.responseType = opt.responseType ?? _options.responseType;
    _options.extra = (new Map.from(_options.extra))..addAll(opt.extra);
    _options.contentType = opt.contentType ?? _options.contentType;
    _options.validateStatus = opt.validateStatus ?? _options.validateStatus;
    _options.followRedirects = opt.followRedirects ?? _options.followRedirects;
  }

  void _mergeNativeCookie(HttpConfig config) {
    //合并native cookie
    if (config.nativeCookie == null) {
      return;
    }
    if (_options.headers == null) {
      _options.headers = Map();
    }
    Map<String, dynamic> headers = _options.headers;
    headers['Cookie'] = config.nativeCookie['Cookie'];
    _options.headers = headers;

    print('cookie---------');
    print(_options.headers);
  }

  void setCookie(String cookie) {
    if (_options.headers == null) {
      _options.headers = Map();
    }
    Map<String, dynamic> headers = _options.headers;
    headers['Cookie'] = cookie;
    _options.headers = headers;

    print('cookie---------');
    print(_options.headers);
  }

  /// print Http Log.
  void _printHttpLog(Response response) {
    if (!_isDebug) {
      return;
    }
    try {
      print("----------------Http Log----------------" +
          "\n[statusCode]:   " +
          response.statusCode.toString() +
          "\n[request   ]:   " +
          _getOptionsStr(response.request));
      _printDataStr("reqdata ", response.request.data);
      _printDataStr("response", response.data);
    } catch (ex) {
      print("Http Log" + " error......");
    }
  }

  /// get Options Str.
  String _getOptionsStr(Options request) {
    return "method: " + request.method;
  }

  /// print Data Str.
  void _printDataStr(String tag, Object value) {
    String da = value.toString();
    while (da.isNotEmpty) {
      if (da.length > 512) {
        print("[$tag  ]:   " + da.substring(0, 512));
        da = da.substring(512, da.length);
      } else {
        print("[$tag  ]:   " + da);
        da = "";
      }
    }
  }

  /// get dio.
  Dio getDio() {
    return _dio;
  }

  /// create new dio.
  static Dio createNewDio([Options options]) {
    Dio dio = new Dio();
    return dio;
  }

  /// get Def Options.
  static BaseOptions getDefOptions() {
    BaseOptions options = BaseOptions();
    options.connectTimeout = 10 * 1000;
    options.receiveTimeout = 20 * 1000;
//    options.contentType = ContentType.parse('application/x-www-form-urlencoded');
//    options.contentType = ContentType.json;
    options.responseType = ResponseType.plain;
    options.baseUrl = APP_HOST_DEBUG + "/";
    Map<String, dynamic> headers = Map<String, dynamic>();
    headers['Accept'] = 'application/json';
    headers['version'] = '1.0.0';
    options.headers = headers;
    return options;
  }
}
