import 'dart:async';

import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../../app.dart';

class HttpUtil {
  static HttpUtil _instance = HttpUtil._internal();

  factory HttpUtil() => _instance;

  late Dio dio;
  CancelToken cancelToken = new CancelToken();

  HttpUtil._internal() {
    BaseOptions options = new BaseOptions(
      baseUrl: Constants.BASE_URL,
      connectTimeout: Duration(seconds: 4),
      receiveTimeout: Duration(seconds: 5),
      headers: {},
      contentType: 'application/json; charset=utf-8',
    );

    dio = new Dio(options);

    // CookieJar cookieJar = CookieJar();
    // dio.interceptors.add(CookieManager(cookieJar));

    dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        return handler.next(options);
      },
      onResponse: (response, handler) {
        return handler.next(response);
      },
      onError: (DioError e, handler) {
        // ErrorEntity eInfo = createErrorEntity(e);

        switch (e.response!.statusCode) {
          case 401: // No permission to log in again
            setLoginStatus(false);
            break;
          default:
        }
        return handler.next(e);
      },
    ));
  }

  // ErrorEntity createErrorEntity(DioError error) {
  //   switch (error.type) {
  //     case DioErrorType.cancel:
  //       return ErrorEntity(code: -1, message: 'Request cancellation');
  //     case DioErrorType.connectTimeout:
  //       return ErrorEntity(code: -1, message: 'Connection timed out');
  //     case DioErrorType.sendTimeout:
  //       return ErrorEntity(code: -1, message: 'Request timed out');
  //     case DioErrorType.receiveTimeout:
  //       return ErrorEntity(code: -1, message: 'Response timeout');
  //     case DioErrorType.response:
  //       {
  //         try {
  //           int? errCode = error.response?.statusCode;
  //           switch (errCode) {
  //             case 400:
  //               return ErrorEntity(code: errCode, message: 'Request syntax error');
  //             case 401:
  //               return ErrorEntity(code: errCode, message: 'Username or password incorrect');
  //             case 403:
  //               return ErrorEntity(code: errCode, message: 'Server refused to execute');
  //             case 404:
  //               return ErrorEntity(code: errCode, message: 'Can not reach server');
  //             case 405:
  //               return ErrorEntity(code: errCode, message: 'Request method is forbidden');
  //             case 405:
  //               return ErrorEntity(code: errCode, message: 'User already registered');
  //             case 500:
  //               return ErrorEntity(code: errCode, message: 'Server internal error');
  //             case 502:
  //               return ErrorEntity(code: errCode, message: 'Invalid request');
  //             case 503:
  //               return ErrorEntity(code: errCode, message: 'Server hung up');
  //             case 505:
  //               return ErrorEntity(code: errCode, message: 'Does not support HTTP protocol request');
  //             default:
  //               {
  //                 // return ErrorEntity(code: errCode, message: 'Unknown error');
  //                 return ErrorEntity(
  //                   code: errCode,
  //                   message: error.response?.statusMessage,
  //                 );
  //               }
  //           }
  //         } on Exception catch (_) {
  //           return ErrorEntity(code: -1, message: 'Unknown error');
  //         }
  //       }
  //     default:
  //       {
  //         return ErrorEntity(code: -1, message: error.message);
  //       }
  //   }
  // }

  void cancelRequests(CancelToken token) {
    token.cancel('cancelled');
  }

  Future<Map<String, dynamic>> getAuthorizationHeader() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    Map<String, dynamic> headers;
    String? accessToken = prefs.getString(Constants.TOKEN);
    headers = {
      'Authorization': 'Bearer $accessToken',
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    };
    return headers;
  }

  /// restful get
  /// refresh false
  /// noCache true
  /// list  false
  /// cacheKey
  /// cacheDisk
  Future get({
    required String path,
    Map<String, dynamic>? queryParameters,
    Options? options,
    bool refresh = false,
    bool noCache = false,
    bool list = false,
    String cacheKey = '',
    bool cacheDisk = false,
  }) async {
    Options requestOptions = options ?? Options();
    if (requestOptions.extra == null) {
      requestOptions.extra = Map();
    }
    requestOptions.extra!.addAll({
      'refresh': refresh,
      'noCache': noCache,
      'list': list,
      'cacheKey': cacheKey,
      'cacheDisk': cacheDisk,
    });
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);

    var response =
        await dio.get(path, queryParameters: queryParameters, options: requestOptions, cancelToken: cancelToken);

    return response.data;
  }

  /// restful post
  Future post(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    requestOptions.headers?.addAll({'Accept': 'application/json'});
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);

    var response = await dio.post(
      path,
      data: data,
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );

    return response.data;
  }

  /// restful put
  Future put(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);
    var response = await dio.put(
      path,
      data: data,
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );
    return response.data;
  }

  /// restful patch
  Future patch(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);
    var response = await dio.patch(
      path,
      data: data,
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );
    return response.data;
  }

  /// restful delete
  Future delete(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);
    var response = await dio.delete(
      path,
      data: data,
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );
    return response.data;
  }

  /// restful post form
  Future postForm(
    String path, {
    required Map<String, dynamic> data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = await getAuthorizationHeader();
    requestOptions.headers!.addAll(authorization);
    var response = await dio.post(
      path,
      data: FormData.fromMap(data),
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );
    return response.data;
  }

  /// restful post Stream
/*Future postStream(
    String path, {
    dynamic data,
    int dataLength = 0,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions.headers = requestOptions.headers ?? {};
    Map<String, dynamic>? authorization = getAuthorizationHeader();
    if (authorization != null) {
      requestOptions.headers!.addAll(authorization);
    }
    requestOptions.headers!.addAll({
      Headers.contentLengthHeader: dataLength.toString(),
    });
    var response = await dio.post(
      path,
      data: Stream.fromIterable(data.map((e) => [e])),
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
    );
    return response.data;
  } */
}

// Exception handling
class ErrorEntity implements Exception {
  int? code;
  String? message;

  ErrorEntity({this.code, this.message});

  @override
  String toString() {
    if (message == null) return 'Exception';
    return 'Exception: code $code, $message';
  }
}
