

import 'dart:async';
import 'dart:convert';

import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import '../store/store.dart';
import 'utils.dart';
import 'package:hive/hive.dart';
import 'package:universal_io/io.dart';

import '../constants.dart';

typedef void PostCallback(Response response);

String generateSignature(String string, {String? key}) {
  key ??= Constants.API_SECRET;
  Hmac mac = new Hmac(sha256, utf8.encode(key));
  Digest digest = mac.convert(utf8.encode(string + key));
  String base64Mac = base64.encode(utf8.encode("$digest"));
  return base64Mac;
}

class HttpUtil {
  static String platformName = Utils.getPlatformName();

  static final Map<String, String> headers = {
    'Accept': 'application/json',
//    'Content-Type': 'application/json',
    'Http-Signature': '',
//    'Http-Group-Id': Constants.GROUP_ID,
    'Http-Business-Id': '0',
    'Http-App-Key': '',
    'Http-Contact-Authorization': '',
    'Http-Device-Type': Utils.getOs(checkWeb: true),
    'Http-Api-Branch': 'flutter',
    'Http-Language-Code': store.state.locale.languageCode,
    'Http-Country-Code': store.state.locale.countryCode ?? '',
  };

  static Future<dynamic> httpGet(String url,
      {
        Map<String, dynamic>? queryParameters,
        int businessId = 0,
        Function(int, int)? receiveProgress,
        bool returnError = false,
        Map<String, String>? additionalHeaders,
      }) async {
    Map<String, dynamic> localHeaders = json.decode(json.encode(headers));
    if (additionalHeaders != null) {
      localHeaders.addAll(additionalHeaders);
    }

    if (!url.startsWith('http')) {
      localHeaders['Http-Signature'] = generateSignature(url);
    }

    localHeaders['Http-Business-Id'] = businessId == 0 ? '${Constants.BUSINESS_ID}' : businessId.toString();
    Box box = await Utils.getBox();
    localHeaders['Http-Contact-Authorization'] = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
    localHeaders['Http-App-Key'] = platformName;
    localHeaders['Http-Device-Type'] = platformName;
    String requestUrl = Constants.BASE_API_URL + url;

    if (url.startsWith('http')) {
      requestUrl = url;
    }

    Utils.jsonPrettyPrint(queryParameters ?? {});

    Dio dio = Dio();
    try {
      Response response = await dio.get(requestUrl,
        queryParameters: queryParameters,
        options: Options(
          headers: localHeaders
        ),
        onReceiveProgress: receiveProgress,
      ).timeout(const Duration(seconds: 30));

      // print('response data: ${response.data}');
//      Utils.jsonPrettyPrint(response.data);

      return response.data;
    } on DioException catch(e) {
      print('HttpGet failed, request: $requestUrl, headers: $localHeaders, error ${e.response}');
      if (returnError) {
        return e;
      }
      throw e;
    }
  }

  static Future<dynamic> httpPost(String url, PostCallback callback,
      {
        Map<String, dynamic>? queryParameters,
        int businessId = 0,
        bool isFormData = false,
        Map<String, String>? additionalHeaders,
        Map<String, dynamic>? body,
        FormData? formData,
        Function(int, int)? sendProgress,
        Function(int, int)? receiveProgress,
        bool returnError = false,
      }) async {

    Map<String, dynamic> localHeaders = json.decode(json.encode(headers));
    if (additionalHeaders != null) {
      localHeaders.addAll(additionalHeaders);
    }
    if (!url.startsWith('http')) {
      localHeaders['Http-Signature'] = generateSignature(url);
    }
    localHeaders['Http-Business-Id'] = businessId == 0 ? '${Constants.BUSINESS_ID}' : businessId.toString();
    Box box = await Utils.getBox();
    localHeaders['Http-Contact-Authorization'] = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
    localHeaders['Http-App-Key'] = platformName;
    localHeaders['Http-Device-Type'] = platformName;

    // Add the line below will not work.
    //localHeaders['Content-Type'] = isFormData ? Headers.formUrlEncodedContentType : Headers.jsonContentType;

    String requestUrl = Constants.BASE_API_URL + url;
    if (url.startsWith('http')) {
      requestUrl = url;
    }

    // Don't print body will cause upload Multipart file failed
    //Utils.jsonPrettyPrint(body ?? {});

    Dio dio = Dio();
    dio.interceptors.add(LogInterceptor());
    try {
      Response response = await dio.post(
        requestUrl,
        queryParameters: queryParameters ?? {},
        data: isFormData ? ((formData != null)?formData:FormData.fromMap(body ?? {})) : json.encode(body),
        options: Options(
          headers: localHeaders,
          contentType: isFormData ? Headers.formUrlEncodedContentType : Headers.jsonContentType,
        ),
        onSendProgress: sendProgress,
        onReceiveProgress: receiveProgress,
      ).timeout(Duration(seconds: 30));

//      Utils.jsonPrettyPrint(response.data);

      if (callback != null) {
        callback(response);
      }
      return response.data;
    } on DioException catch(e, stacktrace) {
      if (e.response != null) {
        try {
          Utils.jsonPrettyPrint(e.response!.data);
        } catch (err) {
          print(e.response!.data);
        }
      }
      if (returnError) {
        return e;
      }
      throw e;
    }
  }

  static Future<dynamic> httpPut(String url, PostCallback callback,
      {
        Map<String, dynamic>? queryParameters,
        int businessId = 0,
        Map<String, String>? additionalHeaders,
        Map<String, dynamic>? body,
        Function(int, int)? sendProgress,
        Function(int, int)? receiveProgress,
        bool returnError = false,
      }) async {

    Map<String, dynamic> localHeaders = json.decode(json.encode(headers));
    if (additionalHeaders != null) {
      localHeaders.addAll(additionalHeaders);
    }

    if (!url.startsWith('http')) {
      localHeaders['Http-Signature'] = generateSignature(url);
    }

    localHeaders['Http-Business-Id'] = businessId == 0 ? '${Constants.BUSINESS_ID}' : businessId.toString();
    Box box = await Utils.getBox();
    localHeaders['Http-Contact-Authorization'] = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
    localHeaders['Http-App-Key'] = platformName;
    localHeaders['Http-Device-Type'] = platformName;

    localHeaders['Content-Type'] = Headers.jsonContentType;

    String requestUrl = Constants.BASE_API_URL + url;

    if (url.startsWith('http')) {
      requestUrl = url;
    }

    Dio dio = Dio();
    try {
      Response response = await dio.put(
        requestUrl,
        queryParameters: queryParameters ?? {},
        data: json.encode(body),
        options: Options(
          headers: localHeaders,
        ),
        onSendProgress: sendProgress,
        onReceiveProgress: receiveProgress,
      ).timeout(Duration(seconds: 30));

//      Utils.jsonPrettyPrint(response.data);

      if (callback != null) {
        callback(response);
      }
      return response.data;
    } on DioException catch(e) {
      if (e.response != null) {
        Utils.jsonPrettyPrint(e.response!.data);
      }
      if (returnError) {
        return e;
      }
      throw e;
    }
  }

  static Future<dynamic> httpPatch(String url, PostCallback callback,
      {
        Map<String, dynamic>? queryParameters,
        int businessId = 0,
        Map<String, String>? additionalHeaders,
        Map<String, dynamic>? body,
        Function(int, int)? sendProgress,
        Function(int, int)? receiveProgress,
        bool returnError = false,
      }) async {

    Map<String, dynamic> localHeaders = json.decode(json.encode(headers));
    if (additionalHeaders != null) {
      localHeaders.addAll(additionalHeaders);
    }

    if (!url.startsWith('http')) {
      localHeaders['Http-Signature'] = generateSignature(url);
    }

    localHeaders['Http-Business-Id'] = businessId == 0 ? '${Constants.BUSINESS_ID}' : businessId.toString();
    Box box = await Utils.getBox();
    localHeaders['Http-Contact-Authorization'] = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
    localHeaders['Http-App-Key'] = platformName;
    localHeaders['Http-Device-Type'] = platformName;

    localHeaders['Content-Type'] = Headers.jsonContentType;

    String requestUrl = Constants.BASE_API_URL + url;

    if (url.startsWith('http')) {
      requestUrl = url;
    }

    Utils.jsonPrettyPrint(body ?? {});

    Dio dio = Dio();
    try {
      Response response = await dio.patch(
        requestUrl,
        queryParameters: queryParameters ?? {},
        data: json.encode(body),
        options: Options(
          headers: localHeaders,
        ),
        onSendProgress: sendProgress,
        onReceiveProgress: receiveProgress,
      ).timeout(Duration(seconds: 30));

//      Utils.jsonPrettyPrint(response.data);

      if (callback != null) {
        callback(response);
      }
      return response.data;
    } on DioException catch(e) {
      if (e.response != null) {
        Utils.jsonPrettyPrint(e.response!.data);
      }
      if (returnError) {
        return e;
      }
      throw e;
    }
  }

  static Future<dynamic> httpDelete(String url, PostCallback callback,
      {
        Map<String, dynamic>? queryParameters,
        int businessId = 0,
        Map<String, String>? additionalHeaders,
        Map<String, dynamic>? body,
        bool returnError = false,
      }) async {

    Map<String, dynamic> localHeaders = json.decode(json.encode(headers));
    if (additionalHeaders != null) {
      localHeaders.addAll(additionalHeaders);
    }

    if (!url.startsWith('http')) {
      localHeaders['Http-Signature'] = generateSignature(url);
    }

    localHeaders['Http-Business-Id'] = businessId == 0 ? '${Constants.BUSINESS_ID}' : businessId.toString();
    Box box = await Utils.getBox();
    localHeaders['Http-Contact-Authorization'] = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
    localHeaders['Http-App-Key'] = platformName;
    localHeaders['Http-Device-Type'] = platformName;

    localHeaders['Content-Type'] = Headers.jsonContentType;

    String requestUrl = Constants.BASE_API_URL + url;

    if (url.startsWith('http')) {
      requestUrl = url;
    }

    Utils.jsonPrettyPrint(body ?? {});

    Dio dio = Dio();
    try {
      Response response = await dio.delete(
        requestUrl,
        queryParameters: queryParameters ?? {},
        data: json.encode(body),
        options: Options(
          headers: localHeaders,
        ),
      ).timeout(Duration(seconds: 30));

//      Utils.jsonPrettyPrint(response.data);

      if (callback != null) {
        callback(response);
      }
      return response.data;
    } on DioException catch(e) {
      if (e.response != null) {
        Utils.jsonPrettyPrint(e.response!.data);
      }
      if(returnError) {
        return e;
      }
      throw e;
    }
  }
}