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

import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:http/http.dart' as http;

import '../exceptions/api_failure.dart';

abstract class ApiClient {
  Future<void> post(
    String endPoint, {
    required Map<String, dynamic> body,
    required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
    required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
    bool isAllowAny,
    bool isExternal,
    bool isRCService,
    bool bypassConnectionCheck,
  });

  Future<void> get(
    String endPoint, {
    required Function(dynamic response, int statusCode) onSuccess,
    required Function(String error, int statusCode) onFailure,
    Map<String, dynamic>? queryParams,
    bool isExternal,
    bool isRCService,
  });

  String getSessionId(String id);

// Future<void> simplePost(
//   String fullUrl, {
//   required Map<dynamic, dynamic> body,
//   required Map<String, String> headers,
//   required Function(dynamic response, int statusCode) onSuccess,
//   required Function(String error, int statusCode) onFailure,
// });

//
// Future<void> simpleGet(
//   String fullUrl, {
//   Function(dynamic response, int statusCode)? onSuccess,
//   Function(String error, int statusCode)? onFailure,
//   Map<String, dynamic>? queryParams,
//   Map<String, String>? headers,
// });
//
// Future<void> simplePut(
//   String fullUrl, {
//   Map<String, dynamic>? body,
//   Map<String, String>? headers,
//   Function(dynamic response, int statusCode)? onSuccess,
//   Function(String error, int statusCode)? onFailure,
// });
//
// Future<void> simpleDelete(
//   String fullUrl, {
//   Function(dynamic response, int statusCode)? onSuccess,
//   Function(String error, int statusCode)? onFailure,
//   Map<String, String>? queryParams,
//   Map<String, String>? headers,
// });

// Future<bool> handleUnauthorized(int statusCode, {required String forUrl});
// Future<String> generatePackagesToken();
}

class ApiClientImp implements ApiClient {
  final _analytics = getIt<GAnalytics>();
  final LoggerService _loggerService;
  final AppState _appState;

  ApiClientImp({
    required LoggerService loggerService,
    required AppState appState,
  })  : _appState = appState,
        _loggerService = loggerService;

  @override
  post(
    String endPoint, {
    required Map<String, dynamic> body,
    required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
    required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
    bool isAllowAny = true,
    bool isExternal = false,
    bool isRCService = false,
    bool bypassConnectionCheck = false,
  }) async {
    String url;
    if (isExternal) {
      url = endPoint;
    } else {
      if (isRCService) {
        url = RC_BASE_URL + endPoint;
      } else {
        url = ApiConsts.baseUrl + endPoint;
      }
    }
    try {
      var user = _appState.getAuthenticatedUser;
      Map<String, String> headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
      if (!isExternal) {
        String? token = _appState.appAuthToken;

        if (body.containsKey('SetupID')) {
          body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
        } else {}

        if (body.containsKey('isDentalAllowedBackend')) {
          body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
        }

        if (!body.containsKey('IsPublicRequest')) {
          // if (!body.containsKey('PatientType')) {
          if (user != null && user.patientType != null) {
            body['PatientType'] = user.patientType;
          } else {
            body['PatientType'] = PATIENT_TYPE.toString();
          }

          if (user != null && user.patientType != null) {
            body['PatientTypeID'] = user.patientType;
          } else {
            body['PatientType'] = PATIENT_TYPE_ID.toString();
          }

          // TODO : These should be from the appState
          if (user != null) {
            body['TokenID'] = body['TokenID'] ?? token;
            body['PatientID'] = body['PatientID'] ?? user.patientID;

            body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSA : user.outSA;
            body['SessionID'] = getSessionId(body['TokenID'] ?? ""); //getSe
          }
        }
      }

      //    request.versionID = VERSION_ID;
      //     request.channel = CHANNEL;
      //     request.iPAdress = IP_ADDRESS;
      //     request.generalid = GENERAL_ID;
      //     request.languageID = (languageID == 'ar' ? 1 : 2);
      //     request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;

      // body['VersionID'] = ApiConsts.appVersionID.toString();
      if (!isExternal) {
        body['VersionID'] = "50.0";
        body['Channel'] = ApiConsts.appChannelId.toString();
        body['IPAdress'] = ApiConsts.appIpAddress;
        body['generalid'] = ApiConsts.appGeneralId;

        body['LanguageID'] = _appState.getLanguageID().toString();
        body['Latitude'] = _appState.userLat.toString();
        body['Longitude'] = _appState.userLong.toString();
        body['DeviceTypeID'] = _appState.deviceTypeID;
        if (_appState.appLoginTokenID.isNotEmpty) {
          body['LogInTokenID'] = _appState.appLoginTokenID;
        }

        body['TokenID'] = "@dm!n";
        body['PatientID'] = "4767477";
      }

      body.removeWhere((key, value) => value == null);
      log("body: ${json.encode(body)}");
      log("uri: ${Uri.parse(url.trim())}");

      final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);

      if (!networkStatus) {
        onFailure(
          'Please Check The Internet Connection 1',
          -1,
          failureType: ConnectivityFailure("Please Check The Internet Connection 1"),
        );
        _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
        return;
      }

      final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
      final int statusCode = response.statusCode;
      log("response.body: ${response.body}");
      if (statusCode < 200 || statusCode >= 400) {
        var parsed = json.decode(utf8.decode(response.bodyBytes));
        onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
        logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
      } else {
        var parsed = json.decode(utf8.decode(response.bodyBytes));
        if (isAllowAny) {
          onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
        } else {
          if (parsed['Response_Message'] != null) {
            onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
          } else {
            if (parsed['ErrorType'] == 4) {
              //TODO : handle app update
              logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
            }
            if (parsed['ErrorType'] == 2) {
              // todo: handle Logout
              logApiEndpointError(endPoint, "session logged out", statusCode);
            }
            if (isAllowAny) {
              onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
            } else if (parsed['IsAuthenticated'] == null) {
              if (parsed['isSMSSent'] == true) {
                onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
              } else if (parsed['MessageStatus'] == 1) {
                onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
              } else if (parsed['Result'] == 'OK') {
                onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
              } else {
                onFailure(
                  parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
                  statusCode,
                  failureType: MessageStatusFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
                );
                logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
              }
            } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
              onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
            } else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
              if (parsed['SameClinicApptList'] != null) {
                onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
              } else {
                if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
                  if (parsed['ErrorSearchMsg'] == null) {
                    onFailure(
                      "Server Error found with no available message",
                      statusCode,
                      failureType: ServerFailure("Error While Fetching data"),
                    );
                    logApiEndpointError(endPoint, "Server Error found with no available message", statusCode);
                  } else {
                    onFailure(
                      parsed['ErrorSearchMsg'],
                      statusCode,
                      failureType: ServerFailure("Error While Fetching data"),
                    );
                    logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
                  }
                } else {
                  onFailure(
                    parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
                    statusCode,
                    failureType: UserIntimationFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
                  );
                  logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
                }
              }
            } else {
              if (parsed['SameClinicApptList'] != null) {
                onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
              } else {
                if (parsed['message'] != null) {
                  onFailure(
                    parsed['message'] ?? parsed['message'],
                    statusCode,
                    failureType: ServerFailure("Error While Fetching data"),
                  );
                  logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
                } else {
                  onFailure(
                    parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
                    statusCode,
                    failureType: ServerFailure("Error While Fetching data"),
                  );
                  logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
                }
              }
            }
          }
        }
      }
    } catch (e, stackTrace) {
      _loggerService.errorLogs(stackTrace.toString());
      if (e.toString().contains("ClientException")) {
        onFailure('ClientException: Something went wrong, Please try again', -1, failureType: InvalidCredentials('ClientException: Something went wrong, plase try again'));
        _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
      } else {
        onFailure(e.toString(), -1);
      }
      _analytics.errorTracking.log(endPoint, error: "api exception: $e - API Path: $url");
    }
  }

  @override
  get(String endPoint,
      {required Function(dynamic response, int statusCode) onSuccess,
      required Function(String error, int statusCode) onFailure,
      Map<String, dynamic>? queryParams,
      bool isExternal = false,
      bool isRCService = false}) async {
    String url;
    if (isExternal) {
      url = endPoint;
    } else {
      if (isRCService) {
        url = RC_BASE_URL + endPoint;
      } else {
        url = BASE_URL + endPoint;
      }
    }
    if (queryParams != null) {
      String queryString = Uri(queryParameters: queryParams).query;
      url += '?$queryString';
    }

    debugPrint("URL : $url");
    // print("Body : ${json.encode(body)}");

    if (await Utils.checkConnection()) {
      final response = await http.get(
        Uri.parse(url.trim()),
        headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
      );
      final int statusCode = response.statusCode;
      // print("statusCode :$statusCode");

      if (statusCode < 200 || statusCode >= 400) {
        onFailure('Error While Fetching data', statusCode);
        logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
      } else {
        var parsed = json.decode(utf8.decode(response.bodyBytes));
        onSuccess(parsed, statusCode);
      }
    } else {
      onFailure('Please Check The Internet Connection', -1);
      _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
    }
  }

  // @override
  // simplePost(
  //   String fullUrl, {
  //   required Map<dynamic, dynamic> body,
  //   required Map<String, String> headers,
  //   required Function(dynamic response, int statusCode) onSuccess,
  //   required Function(String error, int statusCode) onFailure,
  // }) async {
  //   String url = fullUrl;
  //   // print("URL Query String: $url");
  //   // print("body: $body");
  //
  //   if (await Utils.checkConnection()) {
  //     headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
  //     final response = await http.post(
  //       Uri.parse(url.trim()),
  //       body: json.encode(body),
  //       headers: headers,
  //     );
  //     final int statusCode = response.statusCode;
  //     // print("statusCode :$statusCode");
  //     if (await handleUnauthorized(statusCode, forUrl: fullUrl)) {
  //       simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers);
  //     }
  //
  //     // print(response.body.toString());
  //
  //     if (statusCode < 200 || statusCode >= 400) {
  //       onFailure!('Error While Fetching data', statusCode);
  //       logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
  //     } else {
  //       onSuccess!(response.body.toString(), statusCode);
  //     }
  //   } else {
  //     onFailure!('Please Check The Internet Connection', -1);
  //     _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
  //   }
  // }

  // simpleGet(String fullUrl,
  //     {Function(dynamic response, int statusCode)? onSuccess,
  //     Function(String error, int statusCode)? onFailure,
  //     Map<String, dynamic>? queryParams,
  //     Map<String, String>? headers}) async {
  //   headers = headers ?? {};
  //   String url = fullUrl;
  //
  //   var haveParams = (queryParams != null);
  //   if (haveParams) {
  //     String queryString = Uri(queryParameters: queryParams).query;
  //     url += '?$queryString';
  //     // print("URL Query String: $url");
  //   }
  //
  //   if (await Utils.checkConnection()) {
  //     headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
  //     final response = await http.get(
  //       Uri.parse(url.trim()),
  //       headers: headers,
  //     );
  //
  //     final int statusCode = response.statusCode;
  //     // print("statusCode :$statusCode");
  //     if (await handleUnauthorized(statusCode, forUrl: fullUrl))
  //       simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams);
  //
  //     if (statusCode < 200 || statusCode >= 400) {
  //       onFailure!('Error While Fetching data', statusCode);
  //       logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
  //     } else {
  //       onSuccess!(response.body.toString(), statusCode);
  //     }
  //   } else {
  //     onFailure!('Please Check The Internet Connection', -1);
  //     _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
  //   }
  // }

  // simplePut(String fullUrl,
  //     {Map<String, dynamic>? body,
  //     Map<String, String>? headers,
  //     Function(dynamic response, int statusCode)? onSuccess,
  //     Function(String error, int statusCode)? onFailure}) async {
  //   String url = fullUrl;
  //   // print("URL Query String: $url");
  //
  //   if (await Utils.checkConnection()) {
  //     headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
  //     final response = await http.put(
  //       Uri.parse(url.trim()),
  //       body: json.encode(body),
  //       headers: headers,
  //     );
  //
  //     final int statusCode = response.statusCode;
  //     // print("statusCode :$statusCode");
  //     if (await handleUnauthorized(statusCode, forUrl: fullUrl))
  //       simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body);
  //
  //     if (statusCode < 200 || statusCode >= 400) {
  //       onFailure!('Error While Fetching data', statusCode);
  //       logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
  //     } else {
  //       onSuccess!(response.body.toString(), statusCode);
  //     }
  //   } else {
  //     onFailure!('Please Check The Internet Connection', -1);
  //     _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
  //   }
  // }
  //
  // simpleDelete(String fullUrl,
  //     {Function(dynamic response, int statusCode)? onSuccess,
  //     Function(String error, int statusCode)? onFailure,
  //     Map<String, String>? queryParams,
  //     Map<String, String>? headers}) async {
  //   String url = fullUrl;
  //   // print("URL Query String: $url");
  //
  //   var haveParams = (queryParams != null);
  //   if (haveParams) {
  //     String queryString = Uri(queryParameters: queryParams).query;
  //     url += '?$queryString';
  //     // print("URL Query String: $url");
  //   }
  //
  //   if (await Utils.checkConnection()) {
  //     headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
  //     final response = await http.delete(
  //       Uri.parse(url.trim()),
  //       headers: headers,
  //     );
  //
  //     final int statusCode = response.statusCode;
  //     // print("statusCode :$statusCode");
  //     if (await handleUnauthorized(statusCode, forUrl: fullUrl))
  //       simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers);
  //
  //     if (statusCode < 200 || statusCode >= 400) {
  //       onFailure!('Error While Fetching data', statusCode);
  //       logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
  //     } else {
  //       onSuccess!(response.body.toString(), statusCode);
  //     }
  //   } else {
  //     onFailure!('Please Check The Internet Connection', -1);
  //     _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
  //   }
  // }

  @override
  String getSessionId(String id) {
    return id.replaceAll(RegExp('/[^a-zA-Z]'), '');
  }

  logApiFullUrlError(String fullUrl, error, code) {
    final endpoint = Uri.parse(fullUrl).pathSegments.last;
    logApiEndpointError(endpoint, error, code);
  }

  logApiEndpointError(String endPoint, error, code) {
    _analytics.errorTracking.log(endPoint, error: error);
  }
}
