
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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';

import 'models/resp_models/laser_body_parts.dart';

abstract class BookAppointmentsRepo {
  Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinics();

  Future<Either<Failure, dynamic>> getProjects();

  Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName, {isContinueDentalPlan = false});

  Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsListByHealthCal(int calculationID);

  Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
      {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertSpecificAppointment(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      String? procedureID,
      num? testTypeEnum,
      num? testProcedureEnum,
      int? invoiceNumber,
      int? lineItemNo,
      String? invoiceNoVP,
      Function(dynamic)? onSuccess,
      Function(String)? onError});

  Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();

  Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID);

  Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID);

  Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
      {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertSpecificAppointmentForLiveCare(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      required int serviceID,
      Function(dynamic)? onSuccess,
      Function(String)? onError});

  Future<Either<Failure, GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
      {required int projectID, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<List<DentalChiefComplaintsListResponseModel>>>> getDentalChiefComplaintsList(
      {required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
      {bool isGetNearestAppointments, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareWalkInAppointment({required int projectID, required int clinicID, required int doctorID});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertSpecificAppointmentForWalkIn(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      Function(dynamic)? onSuccess,
      Function(String)? onError});

  Future<Either<Failure, GenericApiModel<AppointmentNearestGateResponseModel>>> getAppointmentNearestGate({required int projectID, required int clinicID});

  Future<Either<Failure, GenericApiModel<dynamic>>> isFavouriteDoctor(
      {required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> insertFavouriteDoctor(
      {required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError});

  Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorSchedule({required int doctorID, required int projectID, required int clinicID, int doctorWorkingHoursDays = 90, Function(dynamic)? onSuccess, Function(String)? onError});
}

class BookAppointmentsRepoImp implements BookAppointmentsRepo {
  final ApiClient apiClient;
  final LoggerService loggerService;

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

  @override
  Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinics() async {
    Map<String, dynamic> mapDevice = {};

    try {
      GenericApiModel<List<GetClinicsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_CLINICS_LIST_URL,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListClinicCentralized'];
            // if (list == null || list.isEmpty) {
            //   throw Exception("lab list is empty");
            // }

            final clinicsList = list.map((item) => GetClinicsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetClinicsListResponseModel>();

            apiResponse = GenericApiModel<List<GetClinicsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: clinicsList,
            );
          } 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, dynamic>> getProjects() {
    // TODO: implement getProjects
    throw UnimplementedError();
  }

  @override
  Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName,
      {isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "ClinicID": (doctorName == "") ? clinicID : 0,
      "ProjectID": projectID,
      "DoctorName": doctorName, //!= null ? doctorId : 0,
      "ContinueDentalPlan": isContinueDentalPlan,
      "IsSearchAppointmnetByClinicID": isContinueDentalPlan ? false : true,
      "isDentalAllowedBackend": clinicID == 17 ? true : isContinueDentalPlan,
      "IsGetNearAppointment": isNearest,
      if (isNearest) "SelectedDate": DateUtil.convertDateToString(DateTime.now()),
      "License": true
    };

    try {
      GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTORS_LIST_URL,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          // onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['DoctorList'];

            final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();

            apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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<List<DoctorsListResponseModel>>>> getDoctorsListByHealthCal(int calculationID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {"CalculationID": calculationID};

    try {
      GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTOR_LIST_CALCULATION,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['List_CalculationTable'];

            final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();

            apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "ClinicID": clinicID,
      "ProjectID": projectID,
      "doctorID": doctorId,
      "License": true,
      "IsRegistered": true,
    };

    try {
      GenericApiModel<DoctorsProfileResponseModel>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTOR_PROFILE,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final doctorProfileList = response['DoctorProfileList'];
            if (doctorProfileList == null || doctorProfileList.isEmpty) {
              //   throw Exception("lab list is empty");
              onError!("Doctor profile not found");
            }

            final doctorsList = DoctorsProfileResponseModel.fromJson(doctorProfileList[0]);

            apiResponse = GenericApiModel<DoctorsProfileResponseModel>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "DoctorID": doctorId,
      "PatientID": 0,
      "License": true,
      "IsRegistered": true,
      "isDentalAllowedBackend": false,
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTOR_RATING_DETAILS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          if (onError != null) {
            onError(error);
          }
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response,
            );
            if (onSuccess != null) {
              onSuccess(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()));
    }
  }

  //TODO: Implement the logic for Dental & laser clinics
  @override
  Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
      {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "DoctorID": doctorId,
      "IsBookingForLiveCare": isBookingForLiveCare,
      "ClinicID": clinicID,
      "ProjectID": projectID,
      "OriginalClinicID": clinicID,
      "days": 0,
      "isReschadual": false,
      "ContinueDentalPlan": continueDentalPlan
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTOR_FREE_SLOTS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              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()));
    }
  }

  //TODO: Handle the cases for LiveCare Schedule, Dental & Laser Clinics
  @override
  Future<Either<Failure, GenericApiModel>> insertSpecificAppointment(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      String? procedureID,
      num? testTypeEnum,
      num? testProcedureEnum,
      int? invoiceNumber,
      int? lineItemNo,
      String? invoiceNoVP,
      Function(dynamic p1)? onSuccess,
      Function(String p1)? onError}) async {
    // {"AppointmentID":2016072771,"ClinicID":1485,"ProjectID":15,"CancelToReschadual":true,"EndTime":"21:15:00","StartTime":"21:00:00","DoctorID":1485,"IsForLiveCare":false,"OriginalClinicID":0,"OriginalProjectID":0,"StrAppointmentDate":"Tuesday, 16 September 2025","VersionID":19.0,"Channel":3,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"jTarYomiIEKOruN8Z4YINA==","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4770714,"PatientTypeID":1,"PatientType":1,"LanguageID":2,"Latitude":24.698381,"Longitude":46.6804279,"TokenID":"jTarYomiIEKOruN8Z4YINA=="}

    Map<String, dynamic> mapDevice = {
      "IsForLiveCare": false,
      "ProjectID": projectID,
      "ClinicID": clinicID,
      "DoctorID": docID,
      "StartTime": selectedTime,
      "SelectedTime": selectedTime,
      "EndTime": selectedTime,
      "ProcedureID": procedureID,
      "TestTypeEnum": testTypeEnum,
      "TestProcedureEnum": testProcedureEnum,
      "InitialSlotDuration": initialSlotDuration,
      "StrAppointmentDate": selectedDate,
      "IsVirtual": false,
      "BookedBy": 102,
      "VisitType": 1,
      "VisitFor": 1,
      "GenderID": genderID,
      "Age": userAge,
      "InvoiceNo": invoiceNumber,
      "InvoiceNo_VP": invoiceNoVP,
      "LineItemNo": lineItemNo,
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        INSERT_SPECIFIC_APPOINTMENT,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final appointmentNo = response['AppointmentNo'];

            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: response["ErrorEndUserMessage"],
              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>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
    Map<String, dynamic> requestBody = {
      "AppointmentID": patientAppointmentHistoryResponseModel.appointmentNo,
      "ClinicID": patientAppointmentHistoryResponseModel.clinicID,
      "ProjectID": patientAppointmentHistoryResponseModel.projectID,
      "CancelToReschadual": false,
      "EndTime": patientAppointmentHistoryResponseModel.endTime,
      "StartTime": patientAppointmentHistoryResponseModel.startTime,
      "DoctorID": patientAppointmentHistoryResponseModel.doctorID,
      "IsForLiveCare": patientAppointmentHistoryResponseModel.isLiveCareAppointment,
      "OriginalClinicID": patientAppointmentHistoryResponseModel.originalClinicID,
      "OriginalProjectID": patientAppointmentHistoryResponseModel.originalProjectID
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        CANCEL_APPOINTMENT,
        body: requestBody,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              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<List<HospitalsModel>>>> getProjectList() async {
    Map<String, dynamic> request = {};

    try {
      GenericApiModel<List<HospitalsModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_PROJECT_LIST,
        body: request,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListProject'];

            final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map<String, dynamic>)).toList().cast<HospitalsModel>();

            apiResponse = GenericApiModel<List<HospitalsModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: appointmentsList,
            );
          } 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<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID) async {
    Map<String, dynamic> mapDevice = {"ProjectID": projectID};

    try {
      GenericApiModel<List<GetClinicsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_CLINICS_LIST_WRT_HOSPITAL_ID_URL,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListClinic'];

            final clinicsList = list.map((item) => GetClinicsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetClinicsListResponseModel>();

            apiResponse = GenericApiModel<List<GetClinicsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: clinicsList,
            );
          } 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<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID) async {
    Map<String, dynamic> mapDevice = {"Age": age, "Gender": genderID};

    try {
      GenericApiModel<List<GetLiveCareClinicsResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_LIVECARE_SCHEDULE_CLINICS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ClinicsHaveScheduleList'];
            // if (list == null || list.isEmpty) {
            //   throw Exception("lab list is empty");
            // }

            final clinicsList = list.map((item) => GetLiveCareClinicsResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetLiveCareClinicsResponseModel>();

            apiResponse = GenericApiModel<List<GetLiveCareClinicsResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: clinicsList,
            );
          } 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<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID,
      {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "ServiceID": serviceID,
      "Age": age,
      "Gender": genderID,
    };

    try {
      GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_LIVECARE_SCHEDULE_CLINIC_DOCTOR_LIST,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['DoctorByClinicIDList'];

            final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();

            apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
      {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "DoctorID": doctorId,
      "IsBookingForLiveCare": true,
      "ClinicID": clinicID,
      "ServiceID": serviceID,
      "ProjectID": projectID,
      "OriginalClinicID": clinicID,
      "days": 50,
      "isReschadual": false,
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_LIVECARE_SCHEDULE_DOCTOR_TIME_SLOTS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              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>> insertSpecificAppointmentForLiveCare(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      required int serviceID,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "IsForLiveCare": true,
      "ProjectID": projectID,
      "ClinicID": clinicID,
      "DoctorID": docID,
      "ServiceID": serviceID,
      "StartTime": selectedTime,
      "SelectedTime": selectedTime,
      "EndTime": selectedTime,
      "InitialSlotDuration": initialSlotDuration,
      "StrAppointmentDate": selectedDate,
      "IsVirtual": false,
      "BookedBy": 102,
      "VisitType": 1,
      "VisitFor": 1,
      "GenderID": genderID,
      "Age": userAge
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        INSERT_LIVECARE_SCHEDULE_APPOINTMENT,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final appointmentNo = response['AppointmentNo'];

            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: response["ErrorEndUserMessage"],
              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<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
      {required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "ProjectID": projectID,
    };

    try {
      GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        HAS_DENTAL_PLAN,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['List_IsPatientHasOnGoingEstimation'];
            final estimationList = list.map((item) => PatientDentalPlanEstimationResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientDentalPlanEstimationResponseModel>();
            apiResponse = GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: estimationList,
            );
          } 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<List<DentalChiefComplaintsListResponseModel>>>> getDentalChiefComplaintsList(
      {required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "PatientID": patientID,
      "ClinicID": clinicID,
      "ProjectID": projectID,
      "isDentalAllowedBackend": true,
      "ContinueDentalPlan": false,
      "IsSearchAppointmnetByClinicID": false,
    };

    try {
      GenericApiModel<List<DentalChiefComplaintsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DOCTORS_LIST_URL,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError?.call(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['List_DentalChiefComplain'];

            final chiefComplaintsList = list.map((item) => DentalChiefComplaintsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DentalChiefComplaintsListResponseModel>();

            apiResponse = GenericApiModel<List<DentalChiefComplaintsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: chiefComplaintsList,
            );
          } 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<List<DoctorsListResponseModel>>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
      {bool isGetNearestAppointments = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "ProjectID": projectID,
      "ChiefComplaintID": chiefComplaintID,
      "isDentalAllowedBackend": true,
      "IsPublicRequest": true,
      "IsGetNearAppointment": isGetNearestAppointments
    };

    try {
      GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_DENTAL_DOCTORS_LIST_URL,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['List_DentalDoctorChiefComplaintMapping'];

            final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();

            apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
      {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "LaserCategoryID": laserCategoryID,
      "ProjectID": projectID,
      "LanguageID": languageID,
    };

    try {
      GenericApiModel<List<LaserBodyPart>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        LASER_BODY_PARTS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          onError!(error);
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['Laser_GetBodyPartsByCategoryList'];

            final doctorsList = list.map((item) => LaserBodyPart.fromJson(item as Map<String, dynamic>)).toList().cast<LaserBodyPart>();

            apiResponse = GenericApiModel<List<LaserBodyPart>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: doctorsList,
            );
          } 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>> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {"NFC_Code": nfcCode, "ProjectID": projectId};

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        CHECK_SCANNED_NFC_QR_CODE,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              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<PatientAppointmentShareResponseModel>>> getPatientShareWalkInAppointment({required int projectID, required int clinicID, required int doctorID}) async {
    Map<String, dynamic> mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID};

    late PatientAppointmentShareResponseModel patientShareObj;

    try {
      GenericApiModel<PatientAppointmentShareResponseModel>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_PATIENT_SHARE_FOR_WALKIN_APPOINTMENT,
        body: mapRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['OnlineCheckInAppointmentsWalkInModel'];
            patientShareObj = (list == null || list.isEmpty) ? PatientAppointmentShareResponseModel() : PatientAppointmentShareResponseModel.fromJson(list);
            patientShareObj.isCash = response["IsCash"];
            patientShareObj.isEligible = response["IsEligible"];
            patientShareObj.isInsured = response["IsInsured"];
            // if (list == null || list.isEmpty) {
            //   throw Exception("patient share list is empty");
            // }

            // final patientShareObj = PatientAppointmentShareResponseModel.fromJson(list);
            // patientShareObj.isCash = response["IsCash"];
            // patientShareObj.isEligible = response["IsEligible"];
            // patientShareObj.isInsured = response["IsInsured"];

            apiResponse = GenericApiModel<PatientAppointmentShareResponseModel>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: patientShareObj,
            );
          } 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>> insertSpecificAppointmentForWalkIn(
      {required int docID,
      required int clinicID,
      required int projectID,
      required String selectedTime,
      required String selectedDate,
      required int initialSlotDuration,
      required int genderID,
      required int userAge,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    Map<String, dynamic> mapDevice = {
      "IsForLiveCare": true,
      "ProjectID": projectID,
      "ClinicID": clinicID,
      "DoctorID": docID,
      "StartTime": selectedTime,
      "SelectedTime": selectedTime,
      "EndTime": selectedTime,
      "InitialSlotDuration": initialSlotDuration,
      "StrAppointmentDate": selectedDate,
      "IsVirtual": false,
      "BookedBy": 102,
      "VisitType": 1,
      "VisitFor": 1,
      "GenderID": genderID,
      "Age": userAge
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        INSERT_WALKIN_APPOINTMENT,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final appointmentNo = response['AppointmentNo'];

            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: response["ErrorEndUserMessage"],
              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<AppointmentNearestGateResponseModel>>> getAppointmentNearestGate({required int projectID, required int clinicID}) async {
    Map<String, dynamic> mapRequest = {"ProjectID": projectID, "ClinicID": clinicID};

    try {
      GenericApiModel<AppointmentNearestGateResponseModel>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_APPOINTMENT_NEAREST_GATE,
        body: mapRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final nearestGateResponse = AppointmentNearestGateResponseModel.fromJson(response['getGateByProjectIDandClinicIDList'][0]);

            apiResponse = GenericApiModel<AppointmentNearestGateResponseModel>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: nearestGateResponse,
            );
          } 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>>> isFavouriteDoctor(
      {required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID};

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        IS_FAVOURITE_DOCTOR,
        body: mapRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          if (onError != null) {
            onError(error);
          }
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response["IsFavouriteDoctor"],
            );
            if (onSuccess != null) {
              onSuccess(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>>> insertFavouriteDoctor(
      {required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID, "IsActive": isActive};

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        INSERT_FAVOURITE_DOCTOR,
        body: mapRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          if (onError != null) {
            onError(error);
          }
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response,
            );
            if (onSuccess != null) {
              onSuccess(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>>> getDoctorSchedule({required int doctorID, required int projectID, required int clinicID, int doctorWorkingHoursDays = 90, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    Map<String, dynamic> mapRequest = {
      "DoctorID": doctorID,
      "ProjectID": projectID,
      "ClinicID": clinicID,
      "DoctorWorkingHoursDays": doctorWorkingHoursDays,
    };

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        DOCTOR_SCHEDULE_URL,
        body: mapRequest,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
          if (onError != null) {
            onError(error);
          }
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response,
            );
            if (onSuccess != null) {
              onSuccess(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()));
    }
  }
}
