import 'dart:async';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.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/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.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/doctor_rating_details_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_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.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/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';

import 'models/resp_models/get_livecare_clinics_response_model.dart';

class BookAppointmentsViewModel extends ChangeNotifier {
  final DialogService dialogService;
  int selectedTabIndex = 0;

  bool isClinicsListLoading = false;
  bool isDoctorsListLoading = false;
  bool isDoctorProfileLoading = false;
  bool isDoctorSearchByNameStarted = false;
  bool isAppointmentNearestGateLoading = false;

  bool isLiveCareSelectedFromHomePage = false;
  bool isLiveCareSchedule = false;
  bool isGetDocForHealthCal = false;
  bool showSortFilterButtons = false;
  int? calculationID = 0;
  bool isSortByClinic = true;

  // Accordion expansion state
  int? expandedGroupIndex;

  int initialSlotDuration = 0;

  bool isNearestAppointmentSelected = false;

  LocationUtils locationUtils;

  List<GetClinicsListResponseModel> clinicsList = [];
  List<GetClinicsListResponseModel> _filteredClinicsList = [];

  List<GetLiveCareClinicsResponseModel> liveCareClinicsList = [];

  List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList;

  List<DoctorsListResponseModel> doctorsList = [];
  List<DoctorsListResponseModel> filteredDoctorList = [];

  // Grouped doctors lists
  List<List<DoctorsListResponseModel>> doctorsListByClinic = [];
  List<List<DoctorsListResponseModel>> doctorsListByHospital = [];
  List<List<DoctorsListResponseModel>> doctorsListGrouped = [];

  List<DoctorsListResponseModel> liveCareDoctorsList = [];

  List<PatientDentalPlanEstimationResponseModel> patientDentalPlanEstimationList = [];
  List<DentalChiefComplaintsListResponseModel> dentalChiefComplaintsList = [];
  int totalTimeNeededForDentalProcedure = 0;
  bool isContinueDentalPlan = false;
  bool isChiefComplaintsListLoading = false;
  int selectedChiefComplaintID = 0;

  GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel();
  DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
  GetLiveCareClinicsResponseModel selectedLiveCareClinic = GetLiveCareClinicsResponseModel();

  late DoctorsProfileResponseModel doctorsProfileResponseModel;

  bool isDoctorRatingDetailsLoading = false;
  List<DoctorRateDetails> doctorDetailsList = [];

  bool isFavouriteDoctor = false;

  List<FreeSlot> slotsList = [];
  List<TimeSlot> docFreeSlots = [];
  List<TimeSlot> dayEvents = [];
  List<TimeSlot> nextDayEvents = [];

  String selectedAppointmentDate = "";
  String selectedAppointmentTime = "";
  String selectedAppointmentDisplayDate = "";

  dynamic freeSlotsResponse;

  BookAppointmentsRepo bookAppointmentsRepo;
  ErrorHandlerService errorHandlerService;
  final NavigationService navigationService;
  MyAppointmentsViewModel myAppointmentsViewModel;

  late AppState _appState;
  RegionList? hospitalList;
  RegionList? filteredHospitalList;
  FacilitySelection currentlySelectedFacility = FacilitySelection.ALL;
  bool isRegionListLoading = false;

  ///this will be used to call the clinic call when navigating from my REGION SELECTION to book appointment screen
  bool shouldLoadSpecificClinic = false;
  String? currentlySelectedHospitalFromRegionFlow;

  ///variables for doctor filter
  List<String> searchedRegionList = [];
  List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"];
  List<String> searchedHospitalList = [];
  List<PatientDoctorAppointmentList> searchedPatientDoctorAppointmentHospitalsList = [];
  List<String> searchedClinicList = [];

  PatientDoctorAppointmentList? selectedHospitalForFilters;
  List<String>? selectedFacilityForFilters = [], selectedRegionForFilters = [];
  String? selectedClinicForFilters;
  bool applyFilters = false;

  bool isWaitingAppointmentAvailable = false;
  bool isPatientRescheduleAppointment = false;

  bool isWaitingAppointmentSelected = false;
  int waitingAppointmentProjectID = 0;
  DoctorsListResponseModel? waitingAppointmentDoctor;
  String waitingAppointmentNFCCode = "";

  PatientAppointmentShareResponseModel? patientWalkInAppointmentShareResponseModel;

  AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel;

  ///variables for laser clinic
  bool isLaserHospitalsLoading = false;
  List<PatientDoctorAppointmentList> laserHospitalsList = [];
  int laserHospitalHmgCount = 0;
  int laserHospitalHmcCount = 0;

  Future<void> getLaserHospitals({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    isLaserHospitalsLoading = true;
    laserHospitalsList.clear();
    laserHospitalHmgCount = 0;
    laserHospitalHmcCount = 0;
    notifyListeners();

    final result = await bookAppointmentsRepo.getDoctorsList(253, 0, false, 0, '');

    result.fold(
      (failure) async {
        isLaserHospitalsLoading = false;
        notifyListeners();
        onError?.call(failure.message);
      },
      (apiResponse) async {
        if (apiResponse.messageStatus == 1) {
          var doctorList = apiResponse.data!;
          var regionList = await DoctorMapper.getMappedDoctor(
            doctorList,
            isArabic: _appState.isArabic(),
            lat: _appState.userLat,
            long: _appState.userLong,
          );

          var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
          regionList = await DoctorMapper.sortList(isLocationEnabled, regionList);

          // Flatten all hospitals across all regions into a single list
          laserHospitalsList.clear();
          Set<String> addedHospitals = {};
          regionList.registeredDoctorMap?.forEach((region, regionData) {
            for (var hospital in regionData?.hmgDoctorList ?? <PatientDoctorAppointmentList>[]) {
              if (!addedHospitals.contains(hospital.filterName)) {
                addedHospitals.add(hospital.filterName ?? '');
                laserHospitalsList.add(hospital);
              }
            }
            for (var hospital in regionData?.hmcDoctorList ?? <PatientDoctorAppointmentList>[]) {
              if (!addedHospitals.contains(hospital.filterName)) {
                addedHospitals.add(hospital.filterName ?? '');
                laserHospitalsList.add(hospital);
              }
            }
          });

          laserHospitalHmgCount = laserHospitalsList.where((h) => h.isHMC != true).length;
          laserHospitalHmcCount = laserHospitalsList.where((h) => h.isHMC == true).length;

          isLaserHospitalsLoading = false;
          notifyListeners();
          onSuccess?.call(apiResponse);
        } else {
          isLaserHospitalsLoading = false;
          notifyListeners();
          onError?.call(apiResponse.errorMessage ?? 'Unknown error');
        }
      },
    );
  }

  List<LaserCategoryType> femaleLaserCategory = [
    LaserCategoryType(1, 'bodyString'),
    LaserCategoryType(2, 'face'),
    LaserCategoryType(10, 'bikini'),
    LaserCategoryType(11, 'retouch'),
  ];
  List<LaserCategoryType> maleLaserCategory = [
    LaserCategoryType(1, 'bodyString'),
    LaserCategoryType(2, 'face'),
    LaserCategoryType(11, 'retouch'),
  ];
  List<List<LaserCategoryType>> bodyTypes = [];

  List<LaserBodyPart> laserBodyPartsList = [];
  List<LaserBodyPart> selectedBodyPartList = [];
  int selectedBodyTypeIndex = 0;
  int selectedCategory = 0;
  bool isBodyPartsLoading = false;
  int duration = 0;

  setIsSortByClinic(bool value) {
    isSortByClinic = value;
    doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
    notifyListeners();
  }

  // Toggle accordion expansion
  void toggleGroupExpansion(int index) {
    if (expandedGroupIndex == index) {
      expandedGroupIndex = null;
    } else {
      expandedGroupIndex = index;
    }
    notifyListeners();
  }

  // Reset accordion expansion
  void resetGroupExpansion() {
    expandedGroupIndex = null;
    notifyListeners();
  }

  // Sort filtered doctor list by clinic or hospital
  void sortFilteredDoctorList(bool sortByClinic) {
    isSortByClinic = sortByClinic;
    if (sortByClinic) {
      // Sort by clinic name
      filteredDoctorList.sort((a, b) {
        final clinicA = (a.clinicName ?? 'Unknown').toLowerCase();
        final clinicB = (b.clinicName ?? 'Unknown').toLowerCase();
        return clinicA.compareTo(clinicB);
      });
    } else {
      // Sort by hospital/project name
      filteredDoctorList.sort((a, b) {
        final hospitalA = (a.projectName ?? 'Unknown').toLowerCase();
        final hospitalB = (b.projectName ?? 'Unknown').toLowerCase();
        return hospitalA.compareTo(hospitalB);
      });
    }
    notifyListeners();
  }

  // Group filtered doctors list for accordion display
  List<List<DoctorsListResponseModel>> getGroupedFilteredDoctorsList() {
    final clinicMap = <String, List<DoctorsListResponseModel>>{};
    final hospitalMap = <String, List<DoctorsListResponseModel>>{};

    for (var doctor in filteredDoctorList) {
      final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
      clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);

      final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
      hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
    }

    return isSortByClinic ? clinicMap.values.toList() : hospitalMap.values.toList();
  }

  // Group doctors by clinic and hospital
  void _groupDoctorsList() {
    final clinicMap = <String, List<DoctorsListResponseModel>>{};
    final hospitalMap = <String, List<DoctorsListResponseModel>>{};

    for (var doctor in doctorsList) {
      final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
      clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);

      final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
      hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
    }

    doctorsListByClinic = clinicMap.values.toList();
    doctorsListByHospital = hospitalMap.values.toList();
    doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
  }

  BookAppointmentsViewModel(
      {required this.bookAppointmentsRepo,
      required this.errorHandlerService,
      required this.navigationService,
      required this.myAppointmentsViewModel,
      required this.locationUtils,
      required this.dialogService}) {
    initBookAppointmentViewModel();
  }

  void initializeFilteredList() {
    _filteredClinicsList = List.from(clinicsList);
    notifyListeners();
  }

  void filterDoctors(String query) {
    if (query.isEmpty) {
      filteredDoctorList = List.from(doctorsList);
      _groupDoctorsList();
      setIsNearestAppointmentSelected(isNearestAppointmentSelected);
      notifyListeners();
      return;
    }

    filteredDoctorList = doctorsList.where((doctor) {
      final name = (doctor.name ?? '').toLowerCase();
      final clinicName = (doctor.clinicName ?? '').toLowerCase();
      final projectName = (doctor.projectName ?? '').toLowerCase();
      final q = query.toLowerCase();
      return name.contains(q) || clinicName.contains(q) || projectName.contains(q);
    }).toList();

    // Re-group filtered list
    final clinicMap = <String, List<DoctorsListResponseModel>>{};
    final hospitalMap = <String, List<DoctorsListResponseModel>>{};

    for (var doctor in filteredDoctorList) {
      final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
      clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);

      final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
      hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
    }

    doctorsListByClinic = clinicMap.values.toList();
    doctorsListByHospital = hospitalMap.values.toList();
    doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;

    notifyListeners();
  }

  void filterClinics(String? query) {
    if (query!.isEmpty) {
      _filteredClinicsList = List.from(clinicsList);
      showSortFilterButtons = false;
    } else {
      // Normalize the search query for better Arabic text matching
      String normalizedQuery = Utils.normalizeArabicText(query.toLowerCase());

      _filteredClinicsList = clinicsList.where((clinic) {
        String clinicName = clinic.clinicDescription?.toLowerCase() ?? '';
        String normalizedClinicName = Utils.normalizeArabicText(clinicName);
        return normalizedClinicName.contains(normalizedQuery);
      }).toList();

      showSortFilterButtons = query.length >= 3;
    }
    notifyListeners();
  }

  initBookAppointmentViewModel() {
    _appState = getIt<AppState>();
    isClinicsListLoading = true;
    isDoctorsListLoading = true;
    isDoctorProfileLoading = true;
    isLiveCareSchedule = false;
    currentlySelectedHospitalFromRegionFlow = null;
    expandedGroupIndex = null; // Reset accordion state
    clinicsList.clear();
    doctorsList.clear();
    liveCareClinicsList.clear();
    patientDentalPlanEstimationList.clear();
    dentalChiefComplaintsList.clear();
    isContinueDentalPlan = false;
    isChiefComplaintsListLoading = true;
    isWaitingAppointmentSelected = false;
    isPatientRescheduleAppointment = false;
    bodyTypes = [maleLaserCategory, femaleLaserCategory];
    selectedChiefComplaintID = 0;
    // getLocation();
    notifyListeners();
  }

  refreshDoctorsList() {
    setIsDoctorsListLoading(true);
    if(selectedClinic.clinicID == 17) {
      getDentalChiefComplaintDoctorsList(projectID: currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0);
    } else {
      getDoctorsList(isNearest: isNearestAppointmentSelected);
    }
  }

  setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) {
    this.isNearestAppointmentSelected = isNearestAppointmentSelected;
    if (isNearestAppointmentSelected) {
      // for (var group in doctorsListGrouped) {
      //   group.sort((a, b) {
      //     var aSlot = a.nearestFreeSlot;
      //     var bSlot = b.nearestFreeSlot;
      //     if (aSlot == null || bSlot == null) return 0;
      //     return DateUtil.convertStringToDate(aSlot).compareTo(DateUtil.convertStringToDate(bSlot));
      //   });
      // }
    } else {
      // for (var group in doctorsListGrouped) {
      //   group.sort((a, b) {
      //     var aSlot = a.decimalDoctorRate;
      //     var bSlot = b.decimalDoctorRate;
      //     if (aSlot == null || bSlot == null) return 0;
      //     return bSlot.compareTo(aSlot);
      //   });
      // }
      // doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
    }

    notifyListeners();
  }

  setIsLiveCareSelectedFromHomePage(bool isLiveCareSelectedFromHomePage) {
    this.isLiveCareSelectedFromHomePage = isLiveCareSelectedFromHomePage;
    notifyListeners();
  }

  setIsWaitingAppointmentSelected(bool isWaitingAppointmentSelected) {
    this.isWaitingAppointmentSelected = isWaitingAppointmentSelected;
    notifyListeners();
  }

  setWaitingAppointmentDoctor(DoctorsListResponseModel waitingAppointmentDoctor) {
    this.waitingAppointmentDoctor = waitingAppointmentDoctor;
    notifyListeners();
  }

  setWaitingAppointmentNFCCode(String waitingAppointmentNFCCode) {
    this.waitingAppointmentNFCCode = waitingAppointmentNFCCode;
    notifyListeners();
  }

  setWaitingAppointmentProjectID(int projectID) {
    waitingAppointmentProjectID = projectID;
    notifyListeners();
  }

  setIsDoctorsListLoading(bool value) {
    if (value) {
      doctorsList.clear();
    }
    isDoctorsListLoading = value;
    notifyListeners();
  }

  setIsLiveCareDoctorsListLoading(bool value) {
    if (value) {
      liveCareDoctorsList.clear();
    }
    isDoctorsListLoading = value;
    notifyListeners();
  }

  setIsClinicsListLoading(bool value) {
    if (value) {
      clinicsList.clear();
    }
    isClinicsListLoading = value;
    // notifyListeners();
  }

  setSelectedClinic(GetClinicsListResponseModel clinic) {
    selectedClinic = clinic;
    notifyListeners();
  }

  setSelectedDoctor(DoctorsListResponseModel doctor) {
    selectedDoctor = doctor;
    notifyListeners();
  }

  setIsDoctorProfileLoading(bool value) {
    isDoctorProfileLoading = value;
    notifyListeners();
  }

  setIsDoctorSearchByNameStarted(bool value) {
    isDoctorSearchByNameStarted = value;
    notifyListeners();
  }

  setDoctorsProfile(DoctorsProfileResponseModel profile) {
    doctorsProfileResponseModel = profile;
    notifyListeners();
  }

  setSelectedAppointmentDateTime(String date, String time, String displayDate) {
    selectedAppointmentDate = date;
    selectedAppointmentTime = time;
    selectedAppointmentDisplayDate = displayDate;
    notifyListeners();
  }

  setIsLiveCareSchedule(bool value) {
    isLiveCareSchedule = value;
    notifyListeners();
  }

  setLiveCareSelectedClinic(GetLiveCareClinicsResponseModel clinic) {
    selectedLiveCareClinic = clinic;
    notifyListeners();
  }

  setIsContinueDentalPlan(bool value) {
    isContinueDentalPlan = value;
    notifyListeners();
  }

  setIsChiefComplaintsListLoading(bool value) {
    isChiefComplaintsListLoading = value;
    notifyListeners();
  }

  setSelectedChiefComplaintID(int id) {
    selectedChiefComplaintID = id;
    notifyListeners();
  }

  setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) {
    this.isPatientRescheduleAppointment = isPatientRescheduleAppointment;
    notifyListeners();
  }

  void onTabChanged(int index) {
    calculationID = null;
    isGetDocForHealthCal = false;
    selectedTabIndex = index;
    checkLiveCareSymptomCheckerStatus();
    notifyListeners();
  }

  bool checkLiveCareSymptomCheckerStatus() {
    bool isAllowed = false;

    if (selectedTabIndex == 1) {
      if (_appState.isAuthenticated) {
        isAllowed = true;
      } else {
        isAllowed = false;
      }
    } else {
      isAllowed = true;
    }
    return isAllowed;
  }

  /// this function will decide which clinic api to be called
  /// either api for region flow or the select clinic api
  Future<void> getClinics() async {
    if (shouldLoadSpecificClinic) {
      getRegionSelectedClinics();
    } else if (isLiveCareSchedule) {
      getLiveCareScheduleClinics();
    } else {
      getAllClinics();
    }
  }

  Future<void> getAllClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.getClinics();

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          clinicsList = apiResponse.data!;
          isClinicsListLoading = false;
          initializeFilteredList();
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    liveCareClinicsList.clear();
    final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);

    result.fold(
      // (failure) async => await errorHandlerService.handleError(failure: failure),
      (failure) async {
        isClinicsListLoading = false;
        notifyListeners();
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          liveCareClinicsList = apiResponse.data!;
          isClinicsListLoading = false;
          initializeFilteredList();
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    liveCareDoctorsList.clear();
    notifyListeners();
    final result =
        await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
    result.fold(
      (failure) async {
        onError!(LocaleKeys.noDoctorFound.tr());
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          liveCareDoctorsList = apiResponse.data!;
          isDoctorsListLoading = false;
          // initializeFilteredList();
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
  Future<void> getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
    doctorsList.clear();
    filteredDoctorList.clear();
    doctorsListGrouped.clear();
    notifyListeners();
    projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
    final result =
        await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, doctorName.isNotEmpty ? false : isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);

    result.fold(
      (failure) async {
        isDoctorsListLoading = false;
        if (onError != null) onError(LocaleKeys.noDoctorFound.tr());
        notifyListeners();
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          doctorsList = apiResponse.data!;
          filteredDoctorList = doctorsList;
          isDoctorsListLoading = false;
          // doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
          initializeFilteredList();
          clearSearchFilters();
          getFiltersFromDoctorList();
          _groupDoctorsList();
          // setIsNearestAppointmentSelected(isNearest);
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  //TODO: GetDockets & Calculations For Health Calculator
  Future<void> getDoctorsListByHealthCal({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    doctorsList.clear();
    final result = await bookAppointmentsRepo.getDoctorsListByHealthCal(calculationID!);
    result.fold(
      (failure) async {
        isDoctorsListLoading = false;
        if (onError != null) onError(LocaleKeys.noDoctorFound.tr());

        notifyListeners();
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          doctorsList = apiResponse.data!;
          setIsSortByClinic(true);
          filteredDoctorList = doctorsList;
          isDoctorsListLoading = false;
          initializeFilteredList();
          clearSearchFilters();
          getFiltersFromDoctorList();
          _groupDoctorsList();
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getMappedDoctors(
      {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    filteredHospitalList = null;
    hospitalList = null;
    isRegionListLoading = true;
    notifyListeners();
    projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
    final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError.call(selectedClinic.clinicID == 23 ? failure.message : LocaleKeys.noDoctorFound.tr());
        }
      },
      (apiResponse) async {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          var doctorList = apiResponse.data!;
          hospitalList = await DoctorMapper.getMappedDoctor(
            doctorList,
            isArabic: _appState.isArabic(),
            lat: _appState.userLat,
            long: _appState.userLong,
          );

          var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
          hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!);

          isRegionListLoading = false;
          filteredHospitalList = hospitalList;
          notifyListeners();
        }
      },
    );
  }

  Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);

    result.fold(
      (failure) async {},
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage ?? "Unknown error occurred");
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          doctorsProfileResponseModel = apiResponse.data!;
          notifyListeners();

          // Check if doctor is favorite after getting profile
          if(_appState.isAuthenticated) {
            checkIsFavouriteDoctor(
              patientID: _appState.getAuthenticatedUser()!.patientId!,
              projectID: doctorsProfileResponseModel.projectID ?? 0,
              clinicID: doctorsProfileResponseModel.clinicID ?? 0,
              doctorID: doctorsProfileResponseModel.doctorID ?? 0,
            );
          }

          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getDoctorRatingDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    isDoctorRatingDetailsLoading = true;
    doctorDetailsList.clear();
    notifyListeners();

    final result = await bookAppointmentsRepo.getDoctorRatingDetails(selectedDoctor.doctorID ?? 0, onSuccess: onSuccess, onError: onError);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError("Failed to load doctor rating details");
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Unknown error occurred");
          }
          isDoctorRatingDetailsLoading = false;
        } else if (apiResponse.messageStatus == 1) {
          try {
            apiResponse.data['DoctorRatingDetailsList'].forEach((v) {
              doctorDetailsList.add(DoctorRateDetails.fromJson(v));
            });
            isDoctorRatingDetailsLoading = false;
            // doctorRatingDetails = DoctorRateDetails.fromJson(apiResponse.data);
            notifyListeners();
            if (onSuccess != null) {
              onSuccess(apiResponse.data);
            }
          } catch (e) {
            if (onError != null) {
              onError("Failed to parse rating details: ${e.toString()}");
            }
          }
        }
      },
    );
  }

  Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    docFreeSlots.clear();
    DateTime date;
    final DateFormat formatter = DateFormat('HH:mm', "en-US");
    final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
    Map<DateTime, List> _eventsParsed;

    final result = await bookAppointmentsRepo.getDoctorFreeSlots(
      selectedDoctor.clinicID ?? 0,
      selectedDoctor.projectID ?? 0,
      selectedDoctor.doctorID ?? 0,
      isBookingForLiveCare,
      continueDentalPlan: isContinueDentalPlan,
      onError: onError,
    );

    result.fold(
      (failure) async {
        print(failure);
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage ?? "Unknown error occurred");
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data['FreeTimeSlots'] == null || apiResponse.data['FreeTimeSlots'].isEmpty) {
            onError!("No free slots available".tr());
            return;
          }
          initialSlotDuration = apiResponse.data["InitialSlotDuration"];
          freeSlotsResponse = apiResponse.data['FreeTimeSlots'];
          isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];

          // isWaitingAppointmentAvailable = true;

          freeSlotsResponse.forEach((element) {
            // date = (isLiveCareSchedule != null && isLiveCareSchedule)
            //     ? DateUtil.convertStringToDate(element)
            //     :
            date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
            slotsList.add(FreeSlot(date, ['slot']));
            docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
          });

          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getLiveCareDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    docFreeSlots.clear();
    DateTime date;
    final DateFormat formatter = DateFormat('HH:mm', "en-US");
    final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
    Map<DateTime, List> _eventsParsed;

    final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(
        selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
        onError: onError);

    result.fold(
      (failure) async {
        print(failure);
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage ?? "Unknown error occurred");
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data['PatientER_DoctorFreeSlots'] == null || apiResponse.data['PatientER_DoctorFreeSlots'].isEmpty) {
            onError!("No free slots available".tr());
            return;
          }
          initialSlotDuration = apiResponse.data["InitialSlotDuration"];
          freeSlotsResponse = apiResponse.data['PatientER_DoctorFreeSlots'];
          freeSlotsResponse.forEach((element) {
            // date = (isLiveCareSchedule != null && isLiveCareSchedule)
            //     ? DateUtil.convertStringToDate(element)
            //     :
            date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
            slotsList.add(FreeSlot(date, ['slot']));
            docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
          });

          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  //TODO: Handle the cases for Dental & Laser Clinics
  Future<void> insertSpecificAppointment(
      {String? procedureID,
      num? testTypeEnum,
      num? testProcedureEnum,
      int? invoiceNumber,
      int? lineItemNo,
      String? invoiceNoVP,
      Function(dynamic p1)? onSuccess,
      Function(dynamic p1)? onError}) async {
    _appState = getIt<AppState>();
    final result = await bookAppointmentsRepo.insertSpecificAppointment(
        docID: selectedDoctor.doctorID!,
        clinicID: selectedDoctor.clinicID!,
        projectID: selectedDoctor.projectID!,
        selectedDate: selectedAppointmentDate,
        selectedTime: selectedAppointmentTime,
        initialSlotDuration: initialSlotDuration,
        genderID: _appState.getAuthenticatedUser()!.gender!,
        userAge: _appState.getAuthenticatedUser()!.age!,
        onError: onError);

    result.fold(
      (failure) async {
        print(failure);
        onError!(failure.message);
      },
      (apiResponse) async {
        if (apiResponse.messageStatus == 2) {
          // onError!(apiResponse);
          if (isPatientRescheduleAppointment) {
            PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
              appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
              clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
              projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
              endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
              startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
              doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
              isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
              originalClinicID: 0,
              originalProjectID: 0,
              appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
            );

            await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
              // LoaderBottomSheet.hideLoader();
              Future.delayed(Duration(milliseconds: 50)).then((value) async {});
              // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
              await insertSpecificAppointment(
                  onError: (err) {},
                  onSuccess: (apiResp) async {
                    LoaderBottomSheet.hideLoader();
                    await Future.delayed(Duration(milliseconds: 50)).then((value) async {
                      showCommonBottomSheetWithoutHeight(
                          GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                          child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
                          callBackFunc: () {
                            setIsPatientRescheduleAppointment(false);
                            Navigator.pushAndRemoveUntil(
                                navigationService.navigatorKey.currentContext!,
                                CustomPageRoute(
                                  page: LandingNavigation(),
                                ),
                                    (r) => false);
                          },
                          isFullScreen: false,
                          isCloseButtonVisible: false,
                          isAutoDismiss: true
                      );
                    });
                  });
            });
          } else {
            LoaderBottomSheet.hideLoader();
            showCommonBottomSheetWithoutHeight(
              title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
              navigationService.navigatorKey.currentContext!,
              child: Utils.getWarningWidget(
                  loadingText: apiResponse.data["ErrorEndUserMessage"],
                  isShowActionButtons: true,
                  onCancelTap: () {
                    navigationService.pop();
                  },
                  onConfirmTap: () async {
                    navigationService.pop();
                    PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
                      appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
                      clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
                      projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
                      endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
                      startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
                      doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
                      isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
                      originalClinicID: 0,
                      originalProjectID: 0,
                      appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
                    );

                    LoaderBottomSheet.showLoader(
                      // loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
                      loadingText: LocaleKeys.loadingText.tr(context: navigationService.navigatorKey.currentContext!),
                    );
                    await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
                      LoaderBottomSheet.hideLoader();
                      Future.delayed(Duration(milliseconds: 50)).then((value) async {});
                      LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
                      await insertSpecificAppointment(
                          onError: (err) {},
                          onSuccess: (apiResp) async {
                            LoaderBottomSheet.hideLoader();
                            await Future.delayed(Duration(milliseconds: 50)).then((value) async {
                              showCommonBottomSheetWithoutHeight(
                                  GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                                  child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
                                  callBackFunc: () {
                                    setIsPatientRescheduleAppointment(false);
                                    Navigator.pushAndRemoveUntil(
                                        navigationService.navigatorKey.currentContext!,
                                        CustomPageRoute(
                                          page: LandingNavigation(),
                                        ),
                                            (r) => false);
                                  },
                                  isFullScreen: false,
                                  isCloseButtonVisible: false,
                                  isAutoDismiss: true
                              );
                            });
                          });
                    });
                  }),
              callBackFunc: () {},
              isFullScreen: false,
              isCloseButtonVisible: true,
            );
          }
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data == null || apiResponse.data!.isEmpty) {
            onError!("No free slots available".tr());
            return;
          }

          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> insertSpecificAppointmentForLiveCare({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async {
    _appState = getIt<AppState>();
    final result = await bookAppointmentsRepo.insertSpecificAppointmentForLiveCare(
        docID: selectedDoctor.doctorID!,
        clinicID: selectedDoctor.clinicID!,
        projectID: selectedDoctor.projectID!,
        serviceID: selectedLiveCareClinic.serviceID ?? 0,
        selectedDate: selectedAppointmentDate,
        selectedTime: selectedAppointmentTime,
        initialSlotDuration: initialSlotDuration,
        genderID: _appState.getAuthenticatedUser()!.gender!,
        userAge: _appState.getAuthenticatedUser()!.age!,
        onError: onError);

    result.fold(
      (failure) async {
        if(onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // onError!(apiResponse);
          LoaderBottomSheet.hideLoader();
          showCommonBottomSheetWithoutHeight(
            title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
            navigationService.navigatorKey.currentContext!,
            child: Utils.getWarningWidget(
                loadingText: apiResponse.data["ErrorEndUserMessage"],
                isShowActionButtons: true,
                onCancelTap: () {
                  navigationService.pop();
                },
                onConfirmTap: () async {
                  navigationService.pop();
                  PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
                    appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
                    clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
                    projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
                    endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
                    startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
                    doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
                    isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
                    originalClinicID: 0,
                    originalProjectID: 0,
                    appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
                  );

                  showCommonBottomSheet(navigationService.navigatorKey.currentContext!,
                      child: Utils.getLoadingWidget(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr()),
                      callBackFunc: (str) {},
                      title: "",
                      height: ResponsiveExtension.screenHeight * 0.3,
                      isCloseButtonVisible: false,
                      isDismissible: false,
                      isFullScreen: false);
                  await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
                    navigationService.pop();
                    Future.delayed(Duration(milliseconds: 50)).then((value) async {});
                    LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
                    await insertSpecificAppointment(
                        onError: (err) {},
                        onSuccess: (apiResp) async {
                          LoaderBottomSheet.hideLoader();
                          await Future.delayed(Duration(milliseconds: 50)).then((value) async {
                            showCommonBottomSheetWithoutHeight(
                                GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                                child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
                                callBackFunc: () {
                                  setIsPatientRescheduleAppointment(false);
                                  Navigator.pushAndRemoveUntil(
                                      navigationService.navigatorKey.currentContext!,
                                      CustomPageRoute(
                                        page: LandingNavigation(),
                                      ),
                                          (r) => false);
                                },
                                isFullScreen: false,
                                isCloseButtonVisible: false,
                                isAutoDismiss: true
                            );
                          });
                        });
                  });
                }),
            callBackFunc: () {},
            isFullScreen: false,
            isCloseButtonVisible: true,
          );
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data == null || apiResponse.data!.isEmpty) {
            onError!("No free slots available".tr());
            return;
          }

          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getRegionMappedProjectList() async {
    isRegionListLoading = true;
    notifyListeners();
    final result = await bookAppointmentsRepo.getProjectList();

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) async {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          var projectList = apiResponse.data!;
          hospitalList = await DoctorMapper.getMappedHospitals(
            projectList,
            isArabic: _appState.isArabic(),
            lat: _appState.userLat,
            lng: _appState.userLong,
          );

          var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
          hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!);

          isRegionListLoading = false;
          filteredHospitalList = hospitalList;
          notifyListeners();
        }
      },
    );
  }

  void setSelectedFacility(FacilitySelection selection) {
    currentlySelectedFacility = selection;
    notifyListeners();
  }

  void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) {
    if (value == null || value.isEmpty) {
      filteredHospitalList = hospitalList;
    } else {
      filteredHospitalList = RegionList();

      var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;

      if (list != null && list.isEmpty) {
        notifyListeners();
        return;
      }

      var filteredList = list!.where((element) => element.filterName!.toLowerCase().contains(value.toLowerCase())).toList();
      var regionData = PatientDoctorAppointmentListByRegion();
      if (isHMG) {
        regionData.hmgDoctorList = filteredList;
        regionData.hmgSize = filteredList.length;
      } else {
        regionData.hmcDoctorList = filteredList;
        regionData.hmcSize = filteredList.length;
      }

      filteredHospitalList?.registeredDoctorMap = {selectedRegionId!: regionData};
    }
    notifyListeners();
  }

  bool isLocationEnabled() {
    return _appState.userLong != 0.0 && _appState.userLong != 0.0;
  }

  // bool getLocationStatus() {
  //   bool isLocationAvaiable =  false;
  //   isLocationEnabled().then((value) => isLocationAvaiable = value);
  //   return isLocationAvaiable;
  // }

  void setLoadSpecificClinic(bool status) {
    shouldLoadSpecificClinic = status;
  }

  void setProjectID(String? mainProjectID) {
    currentlySelectedHospitalFromRegionFlow = mainProjectID;
  }

  Future<void> getRegionSelectedClinics() async {
    final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow ?? "");

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          clinicsList = apiResponse.data!;
          isClinicsListLoading = false;
          initializeFilteredList();
          notifyListeners();
        }
      },
    );
  }

  void resetFilterList() {
    filteredHospitalList = hospitalList;
  }

  void getLocation() {
    locationUtils.getLocation();
  }

  void clearSearchFilters() {
    searchedRegionList.clear();
    searchedHospitalList.clear();
    searchedPatientDoctorAppointmentHospitalsList.clear();
    searchedClinicList.clear();
    notifyListeners();
  }

  void clearSelection() {
    selectedFacilityForFilters = [];
    selectedClinicForFilters = null;
    selectedHospitalForFilters = null;
    selectedRegionForFilters = [];
    applyFilters = false;
    notifyListeners();
  }

  void setSelections(
      List<String>? selectedFacilityForFilters, List<String>? selectedRegionForFilters, String? selectedClinicForFilters, PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
    this.selectedFacilityForFilters = selectedFacilityForFilters;
    this.selectedClinicForFilters = selectedClinicForFilters;
    this.selectedHospitalForFilters = selectedHospitalForFilters;
    this.selectedRegionForFilters = selectedRegionForFilters;
    this.applyFilters = applyFilters;
    notifyListeners();
  }

  void getFiltersFromDoctorList() {
    doctorsList.forEach((element) {
      if (!searchedRegionList.contains(element.getRegionName(_appState.isArabic()))) {
        searchedRegionList.add(element.getRegionName(_appState.isArabic()) ?? "");
      }
      if (!searchedHospitalList.contains(element.projectName)) {
        searchedPatientDoctorAppointmentHospitalsList.add(PatientDoctorAppointmentList()
          ..filterName = element.projectName
          ..isHMC = element.isHMC
          ..distanceInKMs = "0");
        searchedHospitalList.add(element.projectName ?? "");
      }
      if (!searchedClinicList.contains(element.clinicName)) {
        searchedClinicList.add(element.clinicName ?? "");
      }
    });
  }

  void updateApplyFilters(bool applyFilters) {
    this.applyFilters = applyFilters;
    notifyListeners();
  }

  void setSelectedRegion(String region) {
    if (selectedRegionForFilters?.contains(region) == true) {
      selectedRegionForFilters?.remove(region);
    } else {
      selectedRegionForFilters?.add(region);
    }
    notifyListeners();
  }

  void setSelectedHospital(PatientDoctorAppointmentList? hospital) {
    selectedHospitalForFilters = hospital;
    notifyListeners();
  }

  void setSelectedFacilityForFilter(String facility) {
    if (selectedFacilityForFilters?.contains(facility) == true) {
      selectedFacilityForFilters?.remove(facility);
    } else {
      selectedFacilityForFilters?.add(facility);
    }

    notifyListeners();
  }

  void setSelectedClinicForFilter(String? clinic) {
    selectedClinicForFilters = clinic;
    notifyListeners();
  }

  bool isArabic() {
    return _appState.isArabic();
  }

  List<DoctorsListResponseModel> getDoctorListAsPerSelection() {
    if (!applyFilters) return doctorsList;

    if ((selectedRegionForFilters?.isEmpty == true) && (selectedFacilityForFilters?.isEmpty == true) && selectedClinicForFilters == null && selectedHospitalForFilters == null) {
      return doctorsList;
    }
    var list = doctorsList.where((element) {
      var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
      var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true;
      var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
      var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName;
      var isInSelectedHospital = (selectedHospitalForFilters == null) ? true : element.projectName == selectedHospitalForFilters?.filterName;
      var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true);

      return (isInSelectedRegion ?? true) && (facilityFilter ?? true) && isInSelectedClinic && isInSelectedHospital;
    }).toList();
    return list;
  }

  void updateList() {
    filteredDoctorList = getDoctorListAsPerSelection();
    notifyListeners();
  }

  Future<void> getPatientDentalEstimation({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    patientDentalPlanEstimationList.clear();
    totalTimeNeededForDentalProcedure = 0;
    isContinueDentalPlan = false;
    notifyListeners();

    final result = await bookAppointmentsRepo.getPatientDentalEstimation(projectID: projectID);

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          patientDentalPlanEstimationList = apiResponse.data!;

          patientDentalPlanEstimationList.forEach((v) {
            totalTimeNeededForDentalProcedure += (v.neededTime ?? 0);
          });

          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getDentalChiefComplaintsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    dentalChiefComplaintsList.clear();
    notifyListeners();
    int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1;
    final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError?.call(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          dentalChiefComplaintsList = apiResponse.data!;
          isChiefComplaintsListLoading = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getDentalChiefComplaintDoctorsList({int projectID = 0, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    doctorsList.clear();
    notifyListeners();

    projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
    final result = await bookAppointmentsRepo.getDentalChiefComplaintDoctorsList(projectID, selectedChiefComplaintID, isGetNearestAppointments: isNearestAppointmentSelected);

    result.fold(
      (failure) async {
        if(onError != null) {
          onError(failure.message);
        }
        onError!(LocaleKeys.noDoctorFound.tr());
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          doctorsList = apiResponse.data!;
          filteredDoctorList = doctorsList;
          isDoctorsListLoading = false;
          // initializeFilteredList();
          // clearSearchFilters();
          // getFiltersFromDoctorList();
          _groupDoctorsList();
          setIsNearestAppointmentSelected(isNearestAppointmentSelected);
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  setBodyType(int bodyType) {
    selectedBodyTypeIndex = bodyType;
    selectedCategory = 0;
    selectedBodyPartList = [];
    duration = 0;
    notifyListeners();
  }

  FutureOr<void> getLaserClinic() async {
    isBodyPartsLoading = true;
    int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID;
    int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0;
    int languageID = _appState.isArabic() ? 1 : 0;
    final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID);
    result.fold(
      (failure) {
        isBodyPartsLoading = false;
        notifyListeners();
      },
      (apiResponse) {
        isBodyPartsLoading = false;
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          List<LaserBodyPart> response = apiResponse.data!;
          if (response.first.category == 2 || response.first.category == 10) response.remove(response.first);
          laserBodyPartsList = response;
        }
        notifyListeners();
      },
    );
  }

  int getDuration() {
    var duration = 0;

    var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? [];
    var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? [];

    if (selectedBodyPartList.isNotEmpty) {
      duration = selectedBodyPartList.fold(0, (previousValue, element) => previousValue + int.parse(element.timeDuration!));
    }
    if (lowerUpperLegsList.length == 2) {
      duration -= 30;
    }
    if (upperLowerArmsList.length == 2) {
      duration -= 15;
    }

    return duration;
  }

  void setSelectedCategory(category) {
    selectedCategory = category;
    notifyListeners();
    getLaserClinic();
  }

  void setSelectedBodyPart(LaserBodyPart part) {
    if (selectedBodyPartList.contains(part)) {
      selectedBodyPartList.remove(part);
      this.duration = getDuration();

      notifyListeners();
    } else {
      // if (this.duration == 90) {
      //   dialogService.showErrorBottomSheet(
      //     message: "Duration can not exceed 90 min".needTranslation,
      //   );
      //   return;
      // }
      selectedBodyPartList.add(part);
      var duration = getDuration();
      if (duration > 90) {
        selectedBodyPartList.remove(part);
        dialogService.showErrorBottomSheet(
          message: LocaleKeys.durationCannotExceed90.tr(),
        );
        return;
      }
      this.duration = duration;
      notifyListeners();
    }
  }

  readCurrentLaserCategoryID() {
    return bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID;
  }

  String getLaserProcedureNameWRTLanguage(LaserBodyPart part) {
    if (_appState.isArabic()) {
      return part.bodyPartN ?? "";
    } else {
      return part.bodyPart ?? "";
    }
  }

  void resetLaserData() {
    selectedCategory = 0;
    selectedBodyTypeIndex = 0;
    selectedBodyPartList = [];
    laserBodyPartsList = [];
    duration = 0;
  }

  Future<void> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.checkScannedNFCAndQRCode(nfcCode, projectId);

    result.fold(
      (failure) async {
        onError!("Invalid verification point scanned.");
      },
      (apiResponse) {
        // if (apiResponse.data['returnValue'] == 0) {
        //   onError!("Invalid verification point scanned.".needTranslation);
        // } else if (apiResponse.data['returnValue'] == 1) {
        notifyListeners();
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
        // }
      },
    );
  }

  Future<void> getWalkInPatientShareAppointment({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.getPatientShareWalkInAppointment(projectID: waitingAppointmentProjectID, clinicID: selectedDoctor.clinicID!, doctorID: selectedDoctor.doctorID!);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
        // await errorHandlerService.handleError(
        //     failure: failure,
        //     onOkPressed: () {
        //       onError!(failure.message);
        //     });
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          patientWalkInAppointmentShareResponseModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> insertSpecificAppointmentForWalkIn({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async {
    _appState = getIt<AppState>();
    final result = await bookAppointmentsRepo.insertSpecificAppointmentForWalkIn(
        docID: selectedDoctor.doctorID!,
        clinicID: selectedDoctor.clinicID!,
        projectID: selectedDoctor.projectID!,
        selectedDate: selectedAppointmentDate,
        selectedTime: selectedAppointmentTime,
        initialSlotDuration: initialSlotDuration,
        genderID: _appState.getAuthenticatedUser()!.gender!,
        userAge: _appState.getAuthenticatedUser()!.age!,
        onError: onError);

    result.fold(
      (failure) async {
        print(failure);
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          LoaderBottomSheet.hideLoader();
          showCommonBottomSheetWithoutHeight(
            title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
            navigationService.navigatorKey.currentContext!,
            child: Utils.getErrorWidget(loadingText: apiResponse.errorMessage),
            callBackFunc: () {},
            isFullScreen: false,
            isCloseButtonVisible: true,
          );
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data == null || apiResponse.data!.isEmpty) {
            onError!(LocaleKeys.unexpectedError.tr());
            return;
          }
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getAppointmentNearestGate({required int projectID, required int clinicID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    isAppointmentNearestGateLoading = true;
    notifyListeners();

    final result = await bookAppointmentsRepo.getAppointmentNearestGate(projectID: projectID, clinicID: clinicID);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          appointmentNearestGateResponseModel = apiResponse.data!;
          isAppointmentNearestGateLoading = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  void toggleFavouriteDoctor() {
    isFavouriteDoctor = !isFavouriteDoctor;
    notifyListeners();
  }

  void setIsFavouriteDoctor(bool value) {
    isFavouriteDoctor = value;
    notifyListeners();
  }

  Future<void> checkIsFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.isFavouriteDoctor(
      patientID: patientID,
      projectID: projectID,
      clinicID: clinicID,
      doctorID: doctorID,
      onSuccess: onSuccess,
      onError: onError,
    );

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to check favorite doctor");
          }
        } else if (apiResponse.messageStatus == 1) {
          // Check the response for IsFavouriteDoctor flag
          bool isFavorite = apiResponse.data;
          setIsFavouriteDoctor(isFavorite);
          if (onSuccess != null) {
            onSuccess(apiResponse.data);
          }
        }
      },
    );
  }

  Future<void> insertFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await bookAppointmentsRepo.insertFavouriteDoctor(
      patientID: patientID,
      projectID: projectID,
      clinicID: clinicID,
      doctorID: doctorID,
      isActive: isActive,
      onSuccess: onSuccess,
      onError: onError,
    );

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to update favorite doctor");
          }
        } else if (apiResponse.messageStatus == 1) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse.data);
          }
        }
      },
    );
  }

  bool isDoctorScheduleLoading = false;
  List<String> doctorFreeSlotDates = [];
  dynamic doctorScheduleResponse;

  Future<void> getDoctorSchedule({required int doctorID, required int projectID, required int clinicID, int doctorWorkingHoursDays = 90, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    isDoctorScheduleLoading = true;
    notifyListeners();

    final result = await bookAppointmentsRepo.getDoctorSchedule(
      doctorID: doctorID,
      projectID: projectID,
      clinicID: clinicID,
      doctorWorkingHoursDays: doctorWorkingHoursDays,
    );

    result.fold(
      (failure) async {
        isDoctorScheduleLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        isDoctorScheduleLoading = false;
        if (apiResponse.messageStatus == 2) {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to fetch doctor schedule");
          }
        } else if (apiResponse.messageStatus == 1) {
          doctorScheduleResponse = apiResponse.data;
          if (doctorScheduleResponse != null && doctorScheduleResponse['FreeTimeSlots'] != null) {
            doctorFreeSlotDates = List<String>.from(doctorScheduleResponse['FreeTimeSlots']);
          }
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse.data);
          }
        }
      },
    );
  }
}
