import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.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");

/// <BaseResp<T> 返回 status code msg data.
class BaseResp<T> {
  String status;
  int code;
  String msg;
  T data;
  T extra;
  T userType;

  BaseResp(
      this.status, this.code, this.msg, this.data, this.extra, this.userType);

  @override
  String toString() {
    StringBuffer sb = new StringBuffer('{');
    sb.write("\"status\":\"$status\"");
    sb.write(",\"code\":$code");
    sb.write(",\"msg\":\"$msg\"");
    sb.write(",\"data\":\"$data\"");
    sb.write(",\"extra\":\"$extra\"");
    sb.write(",\"userType\":\"$userType\"");
    sb.write('}');
    return sb.toString();
  }
}

/// <BaseRespR<T> 返回 status code msg data Response.
class BaseRespR<T> {
  String status;
  int code;
  String msg;
  T data;
  T extra;
  T userType;
  Response response;

  BaseRespR(this.status, this.code, this.msg, this.data, this.extra,
      this.userType, this.response);

  @override
  String toString() {
    StringBuffer sb = new StringBuffer('{');
    sb.write("\"status\":\"$status\"");
    sb.write(",\"code\":$code");
    sb.write(",\"msg\":\"$msg\"");
    sb.write(",\"data\":\"$data\"");
    sb.write(",\"extra\":\"$extra\"");
    sb.write(",\"userType\":\"$userType\"");
    sb.write('}');
    return sb.toString();
    ;
  }
}

/// 请求方法.
class Method {
  static final String get = "GET";
  static final String post = "POST";
  static final String put = "PUT";
  static final String head = "HEAD";
  static final String delete = "DELETE";
  static final String patch = "PATCH";
}

///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 headers = opt.headers;
    if (addHeadMap != null) {
      print("请求之前");
      addHeadMap.forEach((k, v) {
        headers.putIfAbsent(k, () => v);
        print("HEADDD  ${k}  ${v}");
      });
    }
  }, onResponse: (response) {
    print("相应之前");
  }, onError: (e) {
    print("网络错误  $e");
  });

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

  set addHead(Map<String, dynamic> map) {
    if (map != null) {
      addHeadMap = map;
    }
  }

  /// 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)R
            .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;
    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(e);
    }
    return response;
  }

  /*
   * post请求
   */
  Future<Response> post(url, {data, options, cancelToken}) async {
    Response response;
    try {
      response = await _dio.post(url,
          queryParameters: data, options: options, cancelToken: cancelToken);
      print('post success---------${response.statusCode}  ${response.data}');
    } on DioError catch (e) {
      print('post error---------$e');
      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(e);
    }
    return response.data;
  }

  Future<BaseRespR<T>> requestR<T>(String method, String path,
      {data, Options options, CancelToken cancelToken}) async {
    Response response = await _dio.request(path,
        data: data,
        options: _checkOptions(method, options),
        cancelToken: cancelToken);
    _printHttpLog(response);
    String _status;
    int _code;
    String _msg;
    T _data;
    T _extra;
    T _userType;

    _status = response.statusCode.toString();
    if (response.statusCode == HttpStatus.ok ||
        response.statusCode == HttpStatus.created) {
      try {
        if (response.data is Map) {
          _code = (response.data[_codeKey] is String)
              ? int.tryParse(response.data[_codeKey])
              : response.data[_codeKey];
          _msg = response.data[_msgKey];
          _data = response.data[_dataKey];
        } else {
          Map<String, dynamic> _dataMap = _decodeData(response);
          _code = (_dataMap[_codeKey] is String)
              ? int.tryParse(_dataMap[_codeKey])
              : _dataMap[_codeKey];
          _msg = _dataMap[_msgKey];
          _data = _dataMap[_dataKey];
          _extra = response.data[_extraKey];
          _userType = response.data[_userType];
        }
        return new BaseRespR(
            _status, _code, _msg, _data, _extra, _userType, response);
      } catch (e) {
        return new Future.error(new DioError(
          response: response,
          message: "data parsing exception...",
          type: DioErrorType.RESPONSE,
        ));
      }
    } else {
      _code = 1;
      _msg = '请求失败';
    }
    return new Future.error(new DioError(
      response: response,
      message: "statusCode: $response.statusCode, service error",
      type: DioErrorType.RESPONSE,
    ));
  }

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

  /// decode response data.
  Map<String, dynamic> _decodeData(Response response) {
    if (response == null ||
        response.data == null ||
        response.data.toString().isEmpty) {
      return new Map();
    }
    return json.decode(response.data.toString());
  }

  /// 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
    Map<String, dynamic> headers = _options.headers;
    headers['Cookie'] = config.nativeCookie['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;
  }

  static String getBaseUrl() {
//    if (inProduction) {
//      //正式环境的
//      return APP_HOST_RELEASE;
//    } else {
//      return APP_HOST_DEBUG;
//    }
    return APP_HOST_DEBUG;
  }

  /// 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 = 'https://earth.iyanzhi.com/';
//    options.baseUrl = 'http://earth.gmapp.env/';
    options.baseUrl = getBaseUrl() + "/";
    print("BASEURL!! ${getBaseUrl()}");
    Map<String, dynamic> headers = Map<String, dynamic>();
    headers['Accept'] = 'application/json';
    headers['version'] = '1.0.0';
    options.headers = headers;
    return options;
  }
}
