import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:dio/adapter.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:marking_app/common/config/request_config.dart';
import 'package:marking_app/routes/RouterManager.dart';
import 'package:marking_app/utils/index.dart';
import 'package:marking_app/utils/the_global.dart';
import 'package:package_info/package_info.dart';

class RestDio {
  late Dio _dio;
  // 单例模式
  static final RestDio _instance = RestDio._internal();
  factory RestDio() => _instance;
  RestDio._internal() {
    init();
  }

  // 初始化请求配置
  init() {
    BaseOptions options = BaseOptions(
      connectTimeout: RequestConfig.connectTimeout,
      receiveTimeout: RequestConfig.receiveTimeout,
    );
    _dio = Dio(options);
    _dio.interceptors.add(AuthInterceptor()); // 添加 token
    _dio.interceptors.add(ResponseHandle()); // 添加 数据返回拦截
    _dio.interceptors.add(TheError()); // 添加 数据返回拦截
    const isProd = bool.fromEnvironment('dart.vm.product');
    if (!isProd && RequestConfig.requestDataPrinting) {
      _dio.interceptors.add(LogInterceptor(responseBody: true, requestBody: true)); //添加日志
    }
    // 添加https证书
    setHttpsPEM();
    // setHttpsPKCS12();

    // 使用代理
    // setFindProxy();
  }

  String PEM = "XXXXX"; // certificate content
  String PKCS12File = "XXXXX"; // certificate content

  //dio 基本方法
  Future<Dio> getDio() async {
    return _dio;
  }

  //添加证书
  setHttpsPEM() async {
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        // if (cert.pem == PEM) {
        //   // Verify the certificate
        //   return true;
        // }
        // return false;
        return true;
      };
    };
  }

  setHttpsPKCS12() async {
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      SecurityContext sc = new SecurityContext();
      //file is the path of certificate
      sc.setTrustedCertificates(PKCS12File);
      HttpClient httpClient = new HttpClient(context: sc);
      return httpClient;
    };
  }

  //设置代理
  setFindProxy() async {
    // 设置请求拦截器
    _dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
      // 判断 URI 的路径是否包含指定字符串
      if (options.uri.path.contains(RequestConfig.hwProxyKeywords)) {
        // 设置代理地址
        _dio.httpClientAdapter = DefaultHttpClientAdapter();
        (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
          client.findProxy = (uri) {
            print('进入开始代理...............');
            return RequestConfig.proBaseUrlOfHomework;
          };
        };
      }

      return handler.next(options); // 继续发送请求
    }));
  }
}

/*
*
*AuthInterceptor
*添加header认证
* */
class AuthInterceptor extends Interceptor {
  String PLATFORM = "android"; //可根据代码进行判断

  @override
  onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    //获取app版本
    PackageInfo packageInfo = await PackageInfo.fromPlatform();

    String version = packageInfo.version;
    if (Platform.isIOS) {
      PLATFORM = "ios";
    } else if (Platform.isAndroid) {
      PLATFORM = "android";
    } else if (Platform.isWindows) {
      PLATFORM = "Windows";
    } else if (Platform.isMacOS) {
      PLATFORM = "macos";
    } else if (Platform.isLinux) {
      PLATFORM = "Linux";
    }

    Map<String, String> headers = {};
    headers["Accept-Charset"] = "utf-8";
    headers["Connection"] = "keep-alive";
    headers["Accept"] = "*/*";
    headers["x-version"] = version; //自己更改配置
    headers["x-platform"] = PLATFORM;
    //获取存储数据 保存header token
    String? token = await FastData.getInstance().getToken();

    if (null != token && token.isNotEmpty) {
      headers["Authorization"] = 'Bearer $token'; //添加自己项目中的请求头 进行保存
    }

    options.headers = headers;
    Uri uri = options.uri;
    print(uri.toString());
    // 判断请求的 URL 是否包含指定字符串
    if (uri.toString().contains(RequestConfig.hwProxyKeywords)) {
      String newPath = options.path.replaceFirst(RegExp('^${RequestConfig.hwProxyKeywords}'), '');
      options.path = newPath;
      // 修改请求的 URL
      options.baseUrl = RequestConfig.proBaseUrlOfHomework;
    }

    // print('请求参数query：' + jsonEncode(options.queryParameters));
    // print('请求参数data：' + jsonEncode(options.data));

    return super.onRequest(options, handler);
  }
}

/*
 * ResponseHandle
 * 监听返回响应
 **/
class ResponseHandle extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    const isProd = bool.fromEnvironment('dart.vm.product');
    if (!isProd && RequestConfig.requestDataPrinting) {
      // printJson(response.data);
    }
    super.onResponse(response, handler);
  }
}

/*
 * ResponseHandle
 * 监听返回响应       
 **/
class TheError extends Interceptor {
  // 是否有网
  Future<bool> isConnected() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    return connectivityResult != ConnectivityResult.none;
  }

  @override
  Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
    // 自定义一个socket实例，因为dio原生的实例，message属于是只读的
    // 这里是我单独加的，因为默认的dio err实例，的几种类型，缺少无网络情况下的错误提示信息
    if (err.error is SocketException) {
      err.error = MyDioSocketException(
        err.message.contains('111') ? "服务器拒绝连接，请重试" : err.message,
        osError: err.error?.osError,
        address: err.error?.address,
        port: err.error?.port,
      );
    }
    // dio默认的错误实例，如果是没有网络，只能得到一个未知错误，无法精准的得知是否是无网络的情况
    if (err.type == DioErrorType.other) {
      bool isConnectNetWork = await isConnected();
      if (!isConnectNetWork && err.error is MyDioSocketException) {
        err.error.message = "当前网络不可用，请检查您的网络";
      }
    }

    // error统一处理
    AppException appException = AppException.create(err);

    // 错误提示
    // debugPrint('DioError===: ${appException.toString()}');
    err = appException;

    return super.onError(err, handler);
  }
}

String getDioErrorTypeStr(DioError err) {
  DioErrorType errorType = err.type;
  String str;
  switch (errorType) {
    case DioErrorType.connectTimeout:
      str = '连接超时，请检查网络再重试';
      break;
    case DioErrorType.sendTimeout:
      str = '发送时间超时，请重试';
      break;
    case DioErrorType.receiveTimeout:
      str = '接收数据超时，请重试';
      break;
    case DioErrorType.response:
      str = '请求返回失败';
      break;
    case DioErrorType.cancel:
      str = '请求取消';
      break;
    case DioErrorType.other:
      str = '请求其他错误';
      break;
  }
  return str;
}

/// 自定义异常
class AppException extends DioError {
  final String _message;
  final int _code;
  AppException(this._code, this._message, StackTrace? theStackTrace,
      {required super.requestOptions, super.type, super.error}) {
    super.stackTrace = theStackTrace;
  }

  @override
  String toString() {
    return "$_code$_message" + super.toString();
  }

  String getMessage() {
    return _message;
  }

  factory AppException.create(DioError error) {
    switch (error.type) {
      case DioErrorType.cancel:
        {
          return AppException(-1, "请求取消", error.stackTrace,
              requestOptions: error.requestOptions, type: error.type, error: error.error);
        }
      case DioErrorType.connectTimeout:
        {
          return AppException(-1, "连接超时", error.stackTrace,
              requestOptions: error.requestOptions, type: error.type, error: error.error);
        }
      case DioErrorType.sendTimeout:
        {
          return AppException(-1, "请求超时", error.stackTrace,
              requestOptions: error.requestOptions, type: error.type, error: error.error);
        }
      case DioErrorType.receiveTimeout:
        {
          return AppException(-1, "响应超时", error.stackTrace,
              requestOptions: error.requestOptions, type: error.type, error: error.error);
        }
      case DioErrorType.response:
        {
          try {
            int? errCode = error.response!.statusCode;
            // String errMsg = error.response.statusMessage;
            // return ErrorEntity(code: errCode, message: errMsg);
            switch (errCode) {
              case 400:
                {
                  return AppException(errCode!, "请求语法错误", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 401:
                {
                  var currentContext = TheGlobal.navigatorKey.currentState?.overlay?.context;
                  if (currentContext != null) {
                    var routePath = ModalRoute.of(currentContext)?.settings;
                    if (routePath != null) {
                    } else {
                      if (TheGlobal.navigatorKey.currentContext != null) {
                        ExceptionHandle.toLogin(TheGlobal.navigatorKey.currentContext);
                      }
                    }
                  }
                  return AppException(errCode!, "登录信息过期，请重新登录", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 403:
                {
                  return AppException(errCode!, "服务器拒绝执行", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 404:
                {
                  return AppException(errCode!, "无法连接服务器", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 405:
                {
                  return AppException(errCode!, "请求方法被禁止", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 500:
                {
                  return AppException(errCode!, "服务器内部错误", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 502:
                {
                  return AppException(errCode!, "无效的请求", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 503:
                {
                  return AppException(errCode!, "服务器挂了", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              case 505:
                {
                  return AppException(errCode!, "不支持HTTP协议请求", error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
              default:
                {
                  // return ErrorEntity(code: errCode, message: "未知错误");
                  return AppException(errCode!, error.response!.statusMessage!, error.stackTrace,
                      requestOptions: error.requestOptions, type: error.type, error: error.error);
                }
            }
          } on Exception catch (_) {
            return AppException(-1, "未知错误", error.stackTrace,
                requestOptions: error.requestOptions, type: error.type, error: error.error);
          }
        }
      default:
        {
          return AppException(-1, error.error.message, error.stackTrace,
              requestOptions: error.requestOptions, type: error.type, error: error.error);
        }
    }
  }
}

class ExceptionHandle {
  // 异常处理信息
  static void exceptionPrompt(BuildContext context, AppException err, {bool logicHandle = true}) {
    ToastUtils.getFluttertoast(
      context: context,
      msg: err._message,
      backgroundColor: Colors.grey[350],
      toastLength: Toast.LENGTH_LONG,
    );
    if (!logicHandle) return;
    switch (err._code) {
      case 401:
        toLogin(context); // 重新前往登录
        break;
      default:
    }
  }

  static void toLogin(context, {int timeer = 800}) {
    setTimeOut(
      timeer,
      () => RouterManager.router.navigateTo(context, RouterManager.loginPath, clearStack: true),
    );
  }
}

// 这里是一个我单独写得soket错误实例，因为dio默认生成的是不允许修改message内容的，我只能自定义一个使用
class MyDioSocketException extends SocketException {
  String message;

  MyDioSocketException(
    this.message, {
    osError,
    address,
    port,
  }) : super(
          message,
          osError: osError,
          address: address,
          port: port,
        );
}
