import 'dart:async';

import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';

abstract class AuthenticationRepo {
  Future<Either<Failure, GenericApiModel<dynamic>>> getServicePrivilege();

  Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({required String firebaseToken});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq});

  Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
      {required dynamic newRequest,
      required String? activationCode,
      required bool isRegister,
      bool isFormFamilyFile = false,
      int? patientShareRequestID,
      int? responseID,
      bool isSwitchUser = false,
      int? patientID,
      int? loginType});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> getUserAgreementContent({required dynamic commonAuthanticatedRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientForRegistration({required dynamic commonAuthanticatedRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkUserStatus({required dynamic commonAuthanticatedRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> registerUser({required dynamic registrationPayloadDataModelRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientIMEIData({required dynamic patientIMEIDataRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> getPatientDeviceData({required dynamic patientDeviceDataRequest});

  Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType();
}

class AuthenticationRepoImp implements AuthenticationRepo {
  final ApiClient apiClient;
  final LoggerService loggerService;

  AuthenticationRepoImp({required this.loggerService, required this.apiClient});

  @override
  Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({required String firebaseToken}) async {
    Map<String, dynamic> mapDevice = {"IMEI": firebaseToken};
    try {
      GenericApiModel<SelectDeviceByImeiRespModelElement>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.selectDeviceImei,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['Patient_SELECTDeviceIMEIbyIMEIList'];
            if (list == null || list.isEmpty) {
              apiResponse = GenericApiModel<SelectDeviceByImeiRespModelElement>(
                messageStatus: messageStatus,
                statusCode: statusCode,
                errorMessage: errorMessage,
                data: null,
              );
              return;
            }

            final model = SelectDeviceByImeiRespModelElement.fromJson(list[0] as Map<String, dynamic>);
            apiResponse = GenericApiModel<SelectDeviceByImeiRespModelElement>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: model,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq, String? languageID}) async {
    int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1;
    checkPatientAuthenticationReq.patientOutSA = isOutKsa;

    // Determine which API to use based on zipCode
    final isOthersCountry = checkPatientAuthenticationReq.zipCode == '0';
    final apiEndpoint = isOthersCountry ? ApiConsts.checkPatientAuthOthers : ApiConsts.checkPatientAuth;

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        apiEndpoint,
        body: checkPatientAuthenticationReq.toJson(),
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({
    required dynamic sendActivationCodeReq,
    String? languageID,
    bool isRegister = false,
    bool isFormFamilyFile = false,
  }) async {



    int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1;
    sendActivationCodeReq.patientOutSA = isOutKsa;
    sendActivationCodeReq.isDentalAllowedBackend = false;
    final payload = sendActivationCodeReq.toJson();
    if (isFormFamilyFile) {
      payload.remove("MobileNo");
      payload.remove("NationalID");
      payload.remove("SMSSignature");
      // payload.remove("ResponseID");
    }

    // Determine which API to use based on zipCode
    final isOthersCountry = sendActivationCodeReq.zipCode == '0';
    String apiEndpoint;
    if (isFormFamilyFile) {
      apiEndpoint = ApiConsts.sendFamilyFileActivation;
    } else if (isRegister) {
      apiEndpoint = ApiConsts.sendActivationCodeRegister;
    } else if (isOthersCountry) {
      apiEndpoint = ApiConsts.sendActivationCodeOthers;
    } else {
      apiEndpoint = ApiConsts.sendActivationCode;
    }

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;

      await apiClient.post(
        apiEndpoint,
        body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(),
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
      {required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq
      required String? activationCode,
      required bool isRegister,
      bool isFormFamilyFile = false,
      int? patientShareRequestID,
      int? responseID,
      bool isSwitchUser = false,
      int? patientID,
      int? loginType}) async {
    AppState appState = getIt.get<AppState>();

    if (isRegister) {
      newRequest["activationCode"] = activationCode ?? "0000";
      newRequest["isSilentLogin"] = activationCode == null;
    } else {
      newRequest.activationCode = activationCode ?? "0000";
      newRequest.isSilentLogin = activationCode == null;
      newRequest.projectOutSA = newRequest.zipCode != '966';
      newRequest.isDentalAllowedBackend = false;
      newRequest.forRegisteration = newRequest.isRegister ?? false;
      newRequest.isRegister = false;
      //silent login case removed token and login token
      if (newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType == 1 || newRequest.loginType == 4)) {
        newRequest.logInTokenID = null;
        newRequest.deviceToken = null;
      }
    }

    Map<String, dynamic> familyRequest = {};
    if (isFormFamilyFile) {
      familyRequest = {
        'PatientShareRequestID': patientShareRequestID,
        'ResponseID': responseID,
        'Status': 3,
        // 'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0,
        'LogInTokenID': appState.getFamilyFileTokenID,
        'activationCode': activationCode ?? "0000",
        'PatientMobileNumber': newRequest.patientMobileNumber,
        'PatientIdentificationID': newRequest.patientIdentificationID,
      };
    }

    Map<String, dynamic> switchRequest = {};
    if (isSwitchUser) {
      switchRequest = newRequest.toJson();
      switchRequest.addAll({
        'PatientID': responseID,
        'IsSilentLogin': true,
        'LogInTokenID': null,
        'SearchType': 2,
      });

      if (loginType != 0) {
        switchRequest['SuperUser'] = patientID;
        switchRequest['DeviceToken'] = null;
      } else {
        switchRequest['LoginType'] = 2;
      }

      if (appState.getSuperUserID == responseID) {
        switchRequest['LoginType'] = loginType;
        switchRequest['PatientIdentificationID'] = "";
        switchRequest['DeviceToken'] = newRequest.deviceToken;
        switchRequest.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ProjectOutSA', 'ForRegisteration'].contains(key));
      }
    }

    final zipCode = _getZipCodeFromRequest(newRequest);
    final isOthersCountry = zipCode == '0';
    final endpoint = isFormFamilyFile
        ? ApiConsts.checkActivationCodeForFamily
        : isRegister
            ? ApiConsts.checkActivationCodeRegister
            : isOthersCountry
                ? ApiConsts.checkActivationCodeOthers
                : ApiConsts.checkActivationCode;

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;

      // Determine the payload to send
      var requestBody = isFormFamilyFile
          ? familyRequest
          : isRegister
              ? newRequest
              : isSwitchUser
                  ? switchRequest
                  : newRequest.toJson();

      if (isSwitchUser) {
        Map<String, dynamic> allUsersPayload;
        var switchingPatientID;
        var superUserPatientID;
        bool isSwitchingToSuperUser = false;

        allUsersPayload = appState.getUserSwitchPayloads;
        switchingPatientID = switchRequest["PatientID"];
        superUserPatientID = allUsersPayload['superUser']['PatientID'];
        isSwitchingToSuperUser = (appState.getSuperUserID == responseID) || (allUsersPayload.containsKey('superUser') && superUserPatientID == switchingPatientID);

        if (isSwitchingToSuperUser) {
          var superUserPayload = appState.getUserSwitchPayloadByIndex(isSuperUser: true);
          if (superUserPayload != null) {
            requestBody['DeviceToken'] = newRequest.deviceToken;
            requestBody['ProjectOutSA'] = 0;
            requestBody['PatientIdentificationID'] = "";
            requestBody.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ForRegisteration'].contains(key));
            appState.setIsChildLoggedIn = false;
            appState.clearChildUsersKeepSuperUser();
          }
        } else {
          appState.setIsChildLoggedIn = true;
        }
      }

      await apiClient.post(
        endpoint,
        body: requestBody,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}) async {
    commonAuthanticatedRequest['Region'] = 1;
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.checkUsageAgreement,
        body: commonAuthanticatedRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> getUserAgreementContent({required dynamic commonAuthanticatedRequest}) async {
    commonAuthanticatedRequest['Region'] = 1;
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getUserAgreementContent,
        body: commonAuthanticatedRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientForRegistration({required dynamic commonAuthanticatedRequest}) async {
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.checkPatientForRegistration,
        body: commonAuthanticatedRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> checkUserStatus({required dynamic commonAuthanticatedRequest}) async {
    commonAuthanticatedRequest['Region'] = 1;
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.checkUserStatus,
        body: commonAuthanticatedRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> registerUser({required dynamic registrationPayloadDataModelRequest}) async {
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.registerUser,
        body: registrationPayloadDataModelRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );

      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel>> insertPatientIMEIData({required patientIMEIDataRequest}) {
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;

      // Determine which API to use based on zipCode
      final zipCode = patientIMEIDataRequest['ZipCode'];
      final isOthersCountry = zipCode == '0';
      final apiEndpoint = isOthersCountry ? ApiConsts.insertPatientDeviceIMEIDataOthers : ApiConsts.insertPatientDeviceIMEIData;

      return apiClient.post(
        apiEndpoint,
        body: patientIMEIDataRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      ).then((_) {
        if (failure != null) return Left(failure!);
        if (apiResponse == null) return Left(ServerFailure("Unknown error"));
        return Right(apiResponse!);
      });
    } catch (e) {
      return Future.value(Left(UnknownFailure(e.toString())));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel>> insertPatientDeviceData({required patientDeviceDataRequest}) {
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      return apiClient.post(
        ApiConsts.insertPatientMobileData,
        body: patientDeviceDataRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      ).then((_) {
        if (failure != null) return Left(failure!);
        if (apiResponse == null) return Left(ServerFailure("Unknown error"));
        return Right(apiResponse!);
      });
    } catch (e) {
      return Future.value(Left(UnknownFailure(e.toString())));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel>> getServicePrivilege() {
    Map<String, dynamic> mapDevice = {};
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      return apiClient.post(
        ApiConsts.getPrivileges,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      ).then((_) {
        if (failure != null) return Left(failure!);
        if (apiResponse == null) return Left(ServerFailure("Unknown error"));
        return Right(apiResponse!);
      });
    } catch (e) {
      return Future.value(Left(UnknownFailure(e.toString())));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel>> getPatientDeviceData({required patientDeviceDataRequest}) {
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      return apiClient.post(
        ApiConsts.getPatientMobileData,
        body: patientDeviceDataRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      ).then((_) {
        if (failure != null) return Left(failure!);
        if (apiResponse == null) return Left(ServerFailure("Unknown error"));
        return Right(apiResponse!);
      });
    } catch (e) {
      return Future.value(Left(UnknownFailure(e.toString())));
    }
  }

  /// Helper method to safely get zipCode from dynamic request (Map or Object)
  String _getZipCodeFromRequest(dynamic request) {
    if (request is Map) {
      return request['ZipCode']?.toString() ?? request['zipCode']?.toString() ?? '';
    } else {
      try {
        return request.zipCode?.toString() ?? '';
      } catch (e) {
        return '';
      }
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType() async {
    // Map<String, dynamic> requestBody = {"PatientType": 1, "PatientTypeID": 1, "TokenID": "@dm!n", "PatientID": 5957754};
    Map<String, dynamic> requestBody = {};
    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getPatientBloodTypeNew,
        body: requestBody,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: response,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }
}
