import 'dart:convert';
import 'dart:typed_data';

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/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/reminder_type.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_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/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.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/theme/colors.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 'package:open_filex/open_filex.dart';

class MyAppointmentsViewModel extends ChangeNotifier {
  int selectedTabIndex = 0;
  int previouslySelectedTab = -1;

  int eyeMeasurementsTabSelectedIndex = 0;

  MyAppointmentsRepo myAppointmentsRepo;
  ErrorHandlerService errorHandlerService;
  AppState appState;

  int activeAppointmentsCount = 0;

  bool isMyAppointmentsLoading = false;
  bool isAppointmentPatientShareLoading = false;
  bool isTimeLineAppointmentsLoading = false;
  bool isPatientMyDoctorsLoading = false;
  bool isPatientFavouriteDoctorsLoading = false;
  bool isFavouriteDoctorsDataFetched = false;

  bool isAppointmentDataToBeLoaded = true;
  bool isMyDoctorsDataToBeLoaded = true;
  bool isArrivedAppointmentDataLoaded = false;
  bool isFullArrivedAppointmentsLoaded = false;

  bool isEyeMeasurementsAppointmentsLoading = false;

  List<AppointmentListingFilters> availableFilters = [];
  List<AppointmentListingFilters>? selectedFilter = [];
  bool isDateFilterSelected = false;
  DateTime? start = null;
  DateTime? end = null;

  bool isAppointmentQueueDetailsLoading = false;
  bool isPatientHasQueueAppointment = false;
  int currentQueueStatus = 0;
  List<PatientInQueueList> patientQueueDetailsList = [];
  late PatientInQueueList currentPatientQueueDetails;

  List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList = [];
  List<PatientAppointmentHistoryResponseModel> filteredAppointmentList = [];

  List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = [];
  List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = [];

  // List<PatientAppointmentHistoryResponseModel> patientAllArrivedAppointmentsHistoryList = [];

  List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];

  List<GetFavoriteDoctorsListModel> patientFavouriteDoctorsList = [];

  List<PatientAppointmentHistoryResponseModel> patientEyeMeasurementsAppointmentsHistoryList = [];

  // Grouping by Clinic and Hospital
  List<PatientAppointmentList> patientAppointmentsByClinic = [];
  List<PatientAppointmentList> patientAppointmentsByHospital = [];
  List<PatientAppointmentList> patientAppointmentsViewList = [];
  bool isAppointmentsSortByClinic = true;

  List<AskDocRequestType> askDoctorRequestTypeList = [];

  PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;

  GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
  bool isTamaraDetailsLoading = false;

  bool isShowNotEligibleDialog = false;
  String notEligibleErrorMsg = "";

  bool isAppointmentAlreadyRated = false;

  AppointmentRatedResponseModel? appointmentRatedResponseModel;
  bool isAppointmentRatedResponseLoading = false;

  AppointmentParkingQRResponseModel? appointmentParkingQRResponseModel;

  Uint8List? parkingQRImageBytes;

  MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState});

  void onTabChange(int index) {
    previouslySelectedTab = selectedTabIndex;
    selectedTabIndex = index;
    start = null;
    end = null;
    // if (index == 0) {
    //   filteredAppointmentList.clear();
    //   filteredAppointmentList.addAll(patientAppointmentsHistoryList);
    // }
    notifyListeners();
  }

  void onEyeMeasurementsTabChanged(int index) {
    eyeMeasurementsTabSelectedIndex = index;
    notifyListeners();
  }

  setIsAppointmentsSortByClinic(bool value) {
    isAppointmentsSortByClinic = value;
    _groupAppointmentsByClinicAndHospital();
    notifyListeners();
  }

  void _groupAppointmentsByClinicAndHospital() {
    patientAppointmentsByClinic.clear();
    patientAppointmentsByHospital.clear();

    for (var element in filteredAppointmentList) {
      // Group by clinic
      List<PatientAppointmentList> appointmentsByClinic = patientAppointmentsByClinic.where((elementClinic) => elementClinic.filterName == element.clinicName).toList();

      if (appointmentsByClinic.isNotEmpty) {
        patientAppointmentsByClinic[patientAppointmentsByClinic.indexOf(appointmentsByClinic[0])].patientDoctorAppointmentList!.add(element);
      } else {
        patientAppointmentsByClinic.add(PatientAppointmentList(filterName: element.clinicName, patientDoctorAppointment: element));
      }

      // Group by hospital
      List<PatientAppointmentList> appointmentsByHospital = patientAppointmentsByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();

      if (appointmentsByHospital.isNotEmpty) {
        patientAppointmentsByHospital[patientAppointmentsByHospital.indexOf(appointmentsByHospital[0])].patientDoctorAppointmentList!.add(element);
      } else {
        patientAppointmentsByHospital.add(PatientAppointmentList(filterName: element.projectName, patientDoctorAppointment: element));
      }
    }

    if (isAppointmentsSortByClinic) {
      patientAppointmentsViewList = patientAppointmentsByClinic;
    } else {
      patientAppointmentsViewList = patientAppointmentsByHospital;
    }
  }

  initAppointmentsViewModel() {
    if (isAppointmentDataToBeLoaded) {
      // Default view is grouped by clinic on first open.
      isAppointmentsSortByClinic = true;
      patientAppointmentsHistoryList.clear();
      patientUpcomingAppointmentsHistoryList.clear();
      patientArrivedAppointmentsHistoryList.clear();
      // patientAllArrivedAppointmentsHistoryList.clear();
      patientEyeMeasurementsAppointmentsHistoryList.clear();
      isMyAppointmentsLoading = true;
      isTimeLineAppointmentsLoading = true;
      patientMyDoctorsList.clear();
      isFullArrivedAppointmentsLoaded = false;
    }
    isTamaraDetailsLoading = true;
    isAppointmentPatientShareLoading = true;
    isEyeMeasurementsAppointmentsLoading = true;
    isShowNotEligibleDialog = false;

    notifyListeners();
  }

  setCurrentQueueStatus(int currentQueueStatus) {
    this.currentQueueStatus = currentQueueStatus;
    notifyListeners();
  }

  setIsAppointmentsHistoryLoading(bool val) {
    isMyAppointmentsLoading = val;
    notifyListeners();
  }

  setIsPatientAppointmentShareLoading(bool val) {
    isAppointmentPatientShareLoading = val;
    notifyListeners();
  }

  setIsPatientTimeLineAppointmentLoading(bool val) {
    isTimeLineAppointmentsLoading = val;
    notifyListeners();
  }

  setIsPatientMyDoctorsLoading(bool val) {
    isPatientMyDoctorsLoading = val;
    notifyListeners();
  }

  setIsMyDoctorsDataToBeLoaded(bool val) {
    isMyDoctorsDataToBeLoaded = val;
    notifyListeners();
  }

  setIsAppointmentDataToBeLoaded(bool val) {
    if (val) {
      isMyAppointmentsLoading = true;
      isMyDoctorsDataToBeLoaded = true;
    }
    isAppointmentDataToBeLoaded = val;
    notifyListeners();
  }

  setIsTamaraDetailsLoading(bool val) {
    isTamaraDetailsLoading = val;
    notifyListeners();
  }

  setIsEyeMeasurementsAppointmentsLoading(bool val) {
    isEyeMeasurementsAppointmentsLoading = val;
    notifyListeners();
  }

  setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) {
    int index = patientAppointmentsHistoryList.indexOf(item);
    if (index != -1) {
      patientAppointmentsHistoryList[index].hasReminder = value;
      notifyListeners();
    }
  }

  Future<void> getPatientEyeMeasurementAppointments({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    patientEyeMeasurementsAppointmentsHistoryList.clear();
    notifyListeners();

    final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true, isForEyeMeasurement: true);

    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) {
          patientEyeMeasurementsAppointmentsHistoryList = apiResponse.data!;
          isEyeMeasurementsAppointmentsLoading = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {bool isForTimeLine = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    if (patientArrivedAppointmentsHistoryList.isNotEmpty) {
      isPatientHasQueueAppointment = false;
      notifyListeners();
      if (Utils.isDateToday(DateUtil.convertStringToDate(patientArrivedAppointmentsHistoryList.first.appointmentDate))) {
        // getPatientAppointmentQueueDetails(appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID);
        getPatientAppointmentQueueDetails();
      }
    }

    // Skip API call if full data is already loaded and we're not requesting timeline view
    if (!isForTimeLine && isFullArrivedAppointmentsLoaded && !isAppointmentDataToBeLoaded) {
      return;
    }

    if (!isAppointmentDataToBeLoaded) return;

    patientAppointmentsByClinic.clear();
    patientAppointmentsByHospital.clear();
    patientAppointmentsViewList.clear();

    // patientAllArrivedAppointmentsHistoryList.clear();
    filteredAppointmentList.clear();
    patientAppointmentsHistoryList.clear();
    patientUpcomingAppointmentsHistoryList.clear();
    patientArrivedAppointmentsHistoryList.clear();
    notifyListeners();

    final results = await Future.wait([
      myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments),
      myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true, isForTimeLine: isForTimeLine),
    ]);

    results[0].fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          patientUpcomingAppointmentsHistoryList = apiResponse.data!;
          isMyAppointmentsLoading = false;
          isAppointmentDataToBeLoaded = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );

    results[1].fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          patientArrivedAppointmentsHistoryList = apiResponse.data!;
          isMyAppointmentsLoading = false;
          isAppointmentDataToBeLoaded = false;
          if (!isForTimeLine) {
            // patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
            isArrivedAppointmentDataLoaded = true;
            isFullArrivedAppointmentsLoaded = true;
          }
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );

    patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList);
    patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList);
    filteredAppointmentList.addAll(patientAppointmentsHistoryList);

    // Build grouped list immediately so the UI has data for the default (By Clinic) view.
    _groupAppointmentsByClinicAndHospital();

    // if (patientArrivedAppointmentsHistoryList.isNotEmpty) {
    isPatientHasQueueAppointment = false;
    notifyListeners();
    if (patientArrivedAppointmentsHistoryList.isNotEmpty) {
      if (Utils.isDateToday(DateUtil.convertStringToDate(patientArrivedAppointmentsHistoryList.first.appointmentDate))) {
        // getPatientAppointmentQueueDetails(appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID);
        getPatientAppointmentQueueDetails();
      }
    }
    // }

    getFiltersForSelectedAppointmentList(filteredAppointmentList);
    notifyListeners();
  }

  changeTabToArrived() {
    if (isFullArrivedAppointmentsLoaded && patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
      selectedTabIndex = 1;
      updateListWRTTab(1);
      notifyListeners();
    }
  }

  /// Loads full arrived appointments list if not already loaded
  /// This is called when navigating to My Appointments page from landing page
  Future<void> loadFullArrivedAppointmentsIfNeeded({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    // Skip if already loaded
    if (isFullArrivedAppointmentsLoaded) {
      // Auto-switch to Arrived tab if no upcoming appointments (even when data is already loaded)
      if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
        selectedTabIndex = 1;
        updateListWRTTab(1);
        notifyListeners();
      }
      return;
    }

    setIsAppointmentsHistoryLoading(true);

    // Fetch full arrived appointments (isForTimeLine = false)
    final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true, isForTimeLine: false);

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // Handle error
        } else if (apiResponse.messageStatus == 1) {
          patientArrivedAppointmentsHistoryList = apiResponse.data!;
          // patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
          isArrivedAppointmentDataLoaded = true;
          isFullArrivedAppointmentsLoaded = true;

          // Rebuild the combined list
          patientAppointmentsHistoryList.clear();
          patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList);
          patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList);

          isMyAppointmentsLoading = false;

          // Auto-switch to Arrived tab if no upcoming appointments
          if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
            selectedTabIndex = 1;
            updateListWRTTab(1);
          } else {
            // Update filtered list based on current tab
            updateListWRTTab(selectedTabIndex);
          }

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

  void getFiltersForSelectedAppointmentList(List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
    availableFilters.clear();
    if (filteredAppointmentList.isEmpty == true) return;
    availableFilters.add(AppointmentListingFilters.DATESELECTION);
    if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == true)) {
      availableFilters.add(AppointmentListingFilters.LIVECARE);
    }

    // if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == false)) {
    //   availableFilters.add(AppointmentListingFilters.WALKIN);
    // }

    if (filteredAppointmentList.any((element) => AppointmentType.isArrived(element) == true)) {
      availableFilters.add(AppointmentListingFilters.ARRIVED);
    }

    if (filteredAppointmentList.any((element) => AppointmentType.isBooked(element) == true)) {
      availableFilters.add(AppointmentListingFilters.BOOKED);
    }

    if (filteredAppointmentList.any((element) => AppointmentType.isConfirmed(element) == true)) {
      availableFilters.add(AppointmentListingFilters.CONFIRMED);
    }
    notifyListeners();
  }

  Future<void> getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, bool isLiveCareAppointment, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo, isLiveCareAppointment: isLiveCareAppointment);

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

          if ((patientAppointmentShareResponseModel!.isCash!)) {
            isShowNotEligibleDialog = false;
          } else {
            if (patientAppointmentShareResponseModel!.isInsured! && patientAppointmentShareResponseModel!.isEligible!) {
              isShowNotEligibleDialog = false;
            } else {
              if (patientAppointmentShareResponseModel!.isInsured! && patientAppointmentShareResponseModel!.isEligible!) {
                isShowNotEligibleDialog = true;
                notEligibleErrorMsg = LocaleKeys.invalidEligibility.tr(context: GetIt.instance<NavigationService>().navigatorKey.currentContext!);
              } else {
                isShowNotEligibleDialog = true;
                notEligibleErrorMsg = LocaleKeys.invalidInsurance.tr(context: GetIt.instance<NavigationService>().navigatorKey.currentContext!);
              }
            }
          }
          isAppointmentPatientShareLoading = false;

          if (isShowNotEligibleDialog) {
            showCommonBottomSheetWithoutHeight(GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                child: Padding(
                  padding: EdgeInsets.only(
                    left: 24.w,
                    top: 24.h,
                    right: 24.w,
                    bottom: 12.h,
                  ),
                  child: Utils.getWarningWidget(
                      loadingText: notEligibleErrorMsg,
                      confirmText: LocaleKeys.updateInsuranceInfo.tr(context: GetIt.instance<NavigationService>().navigatorKey.currentContext!),
                      cancelText: LocaleKeys.continueCash.tr(context: GetIt.instance<NavigationService>().navigatorKey.currentContext!),
                      isShowActionButtons: true,
                      onCancelTap: () {
                        Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);
                        LoaderBottomSheet.showLoader();
                        convertPatientToCash(
                            projectID: projectID,
                            onSuccess: (val) {
                              LoaderBottomSheet.hideLoader();
                              setIsPatientAppointmentShareLoading(true);
                              getPatientShareAppointment(projectID, clinicID, appointmentNo, isLiveCareAppointment);
                            },
                            onError: (err) {
                              LoaderBottomSheet.hideLoader();
                              showCommonBottomSheetWithoutHeight(
                                title: LocaleKeys.notice.tr(context: GetIt.instance<NavigationService>().navigatorKey.currentContext!),
                                GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                                child: Utils.getErrorWidget(loadingText: err.toString()),
                                callBackFunc: () {
                                  Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);
                                  Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);
                                },
                                isFullScreen: false,
                                isCloseButtonVisible: true,
                              );
                            });
                      },
                      onConfirmTap: () async {
                        Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);

                        //Call the API to attempt updating patient insurance
                        LoaderBottomSheet.showLoader();
                        sendPatientUpdateRequest(onSuccess: (val) {
                          LoaderBottomSheet.hideLoader();
                          setIsPatientAppointmentShareLoading(true);
                          getPatientShareAppointment(projectID, clinicID, appointmentNo, isLiveCareAppointment);
                        }, onError: (err) {
                          LoaderBottomSheet.hideLoader();
                          Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);
                          Navigator.push(
                            GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                            CustomPageRoute(
                              page: InsuranceHomePage(),
                            ),
                          );
                        });
                      }),
                ), callBackFunc: () {
              // Navigator.pop(GetIt.instance<NavigationService>().navigatorKey.currentContext!);
            }, isFullScreen: false, isCloseButtonVisible: false, isDismissible: false);
          }

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

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

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

  Future<void> convertPatientToCash({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.convertPatientToCash(projectID: projectID);

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

  Future<void> addAdvanceNumberRequest(
      {required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo);

    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) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> generateAppointmentQR(
      {required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.generateAppointmentQR(clinicID: clinicID, projectID: projectID, appointmentNo: appointmentNo, isFollowUp: isFollowUp);

    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) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

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

    result.fold(
      // (failure) async => await errorHandlerService.handleError(failure: failure),
      (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) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

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

    result.fold(
      // (failure) async => await errorHandlerService.handleError(failure: failure),
      (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) {
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> createAdvancePayment(
      {required String paymentMethodName,
      required int projectID,
      required int clinicID,
      required String appointmentNo,
      required num payedAmount,
      required String paymentReference,
      required String patientID,
      required int patientType,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    final result = await myAppointmentsRepo.createAdvancePayment(
        paymentMethodName: paymentMethodName,
        projectID: projectID,
        clinicID: clinicID,
        appointmentNo: appointmentNo,
        payedAmount: payedAmount,
        paymentReference: paymentReference,
        patientID: patientID,
        patientType: patientType);

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

  Future<void> sendCheckInNfcRequest(
      {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
      required String scannedCode,
      required int checkInType,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    final result = await myAppointmentsRepo.sendCheckInNfcRequest(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, scannedCode: scannedCode, checkInType: checkInType);

    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);
          }
        }
      },
    );
  }

  Future<void> getPatientMyDoctors({bool isTop8 = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    if (!isMyDoctorsDataToBeLoaded) return;
    isPatientMyDoctorsLoading = true;
    patientMyDoctorsList.clear();
    notifyListeners();

    final result = await myAppointmentsRepo.getPatientDoctorsList(isTop8: isTop8);

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

          if (!isTop8) {
            isMyDoctorsDataToBeLoaded = false;
          }

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

  Future<void> getPatientFavouriteDoctors({bool forceRefresh = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    // If data is already fetched and not forcing refresh, skip API call
    // if (isFavouriteDoctorsDataFetched && !forceRefresh) {
    //   return;
    // }

    isPatientFavouriteDoctorsLoading = true;
    patientFavouriteDoctorsList.clear();
    notifyListeners();

    final result = await myAppointmentsRepo.getFavouriteDoctorsList();

    result.fold(
      (failure) async {
        isPatientFavouriteDoctorsLoading = false;
        notifyListeners();
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          isPatientFavouriteDoctorsLoading = false;
          notifyListeners();
        } else if (apiResponse.messageStatus == 1) {
          patientFavouriteDoctorsList = apiResponse.data!;
          isFavouriteDoctorsDataFetched = true;
          isPatientFavouriteDoctorsLoading = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getParkingQR(num appointmentNum, int projectID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.getParkingQR(appointmentNum, projectID);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        appointmentParkingQRResponseModel = apiResponse.data!;
        parkingQRImageBytes = base64Decode(
            appointmentParkingQRResponseModel!.ticketBase64.contains(',') ? appointmentParkingQRResponseModel!.ticketBase64.split(',').last : appointmentParkingQRResponseModel!.ticketBase64);
        // parkingQRImageBytes = appointmentParkingQRResponseModel!.ticketBase64;
        notifyListeners();
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
      },
    );
  }

  // Method to force refresh favorite doctors list
  void refreshFavouriteDoctors() {
    isFavouriteDoctorsDataFetched = false;
    getPatientFavouriteDoctors(forceRefresh: true);
  }

  // Method to reset favorite doctors cache
  void resetFavouriteDoctorsCache() {
    isFavouriteDoctorsDataFetched = false;
  }

  Future<void> insertLiveCareVIDARequest(
      {required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.insertLiveCareVIDARequest(clientRequestID: clientRequestID, 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);
          }
        }
      },
    );
  }

  void updateListWRTTab(int index) {
    isDateFilterSelected = false;
    selectedFilter = [];
    // if(previouslySelectedTab == selectedTabIndex ) return;
    switch (index) {
      // case 0:
      //   filteredAppointmentList.clear();
      //   filteredAppointmentList.addAll(patientAppointmentsHistoryList);
      //   break;
      case 0:
        filteredAppointmentList.clear();
        filteredAppointmentList.addAll(patientUpcomingAppointmentsHistoryList);
        break;
      case 1:
        filteredAppointmentList.clear();
        filteredAppointmentList.addAll(patientArrivedAppointmentsHistoryList);
        break;
    }
    getFiltersForSelectedAppointmentList(filteredAppointmentList);
    _groupAppointmentsByClinicAndHospital();
    notifyListeners();
  }

  void setSelectedFilter(AppointmentListingFilters availableFilter) {
    if (selectedFilter?.contains(availableFilter) == true) {
      selectedFilter?.remove(availableFilter);
      notifyListeners();

      return;
    }
    selectedFilter?.add(availableFilter);
    notifyListeners();
  }

  void getSelectedDateRange(DateTime? start, DateTime? end) {
    this.start = start;
    this.end = end;
    isDateFilterSelected = true;
    List<PatientAppointmentHistoryResponseModel> sourceList = [];
    // if (selectedTabIndex == 0) {
    //   sourceList = patientAppointmentsHistoryList;
    // } else
      if (selectedTabIndex == 0) {
      sourceList = patientUpcomingAppointmentsHistoryList;
    } else if (selectedTabIndex == 1) {
      sourceList = patientArrivedAppointmentsHistoryList;
    }
    // if (isDateFilterSelected) sourceList = filteredAppointmentList;
    if (start == null && end == null) {
      isDateFilterSelected = false;
      filteredAppointmentList.clear();
      for (var element in sourceList) {
        if (isUnderFilter(element)) {
          filteredAppointmentList.add(element);
        }
      }
    } else {
      filteredAppointmentList.clear();
      for (var element in sourceList) {
        try {
          var dateTime = DateUtil.convertStringToDate(element.appointmentDate).provideDateOnly();

          if (start != null && end == null) {
            if (dateTime.isAtSameMomentAs(start)) {
              if (isUnderFilter(element)) {
                filteredAppointmentList.add(element);
              }
            }
          } else if (start != null && end != null) {
            if ((dateTime.isAfter(start)) && ((dateTime.isBefore(end)) || ((dateTime.isAtSameMomentAs(end))))) {
              if (isUnderFilter(element)) {
                filteredAppointmentList.add(element);
              }
            }
          }
        } catch (e) {}
      }
    }
    _groupAppointmentsByClinicAndHospital();
    notifyListeners();
  }

  void filterTheListAsPerSelection() {
    getSelectedDateRange(start, end);
  }

  bool isUnderFilter(PatientAppointmentHistoryResponseModel element) {
    if (selectedFilter == null || selectedFilter!.isEmpty) return true;
    for (var filter in selectedFilter ?? []) {
      switch (filter) {
        case AppointmentListingFilters.WALKIN:
          if (element.isLiveCareAppointment == false) return true;
        case AppointmentListingFilters.BOOKED:
          if (AppointmentType.isBooked(element)) return true;

        case AppointmentListingFilters.CONFIRMED:
          if (AppointmentType.isConfirmed(element)) return true;

        case AppointmentListingFilters.ARRIVED:
          if (AppointmentType.isArrived(element)) return true;

        case AppointmentListingFilters.LIVECARE:
          if (element.isLiveCareAppointment == true) return true;

        case AppointmentListingFilters.DATESELECTION:
      }
    }
    return false;
  }

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

    result.fold(
      // (failure) async => await errorHandlerService.handleError(failure: failure),
      (failure) async {
        isTamaraDetailsLoading = false;
        notifyListeners();
      },
      (apiResponse) {
        getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
        isTamaraDetailsLoading = false;
        notifyListeners();
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
      },
    );
  }

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

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          activeAppointmentsCount = 0;
        } else if (apiResponse.messageStatus == 1) {
          activeAppointmentsCount = apiResponse.data ?? 0;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> isDoctorAvailable({required int projectID, required int doctorId, required int clinicId, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await myAppointmentsRepo.isDoctorAvailable(projectID: projectID, doctorID: doctorId, clinicID: clinicId);

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

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

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

  Future<void> getPatientAppointmentQueueDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    //TODO: Discuss With Haroon, Is the User Has no data it return No Element Bad State;
    isPatientHasQueueAppointment = false;
    isAppointmentQueueDetailsLoading = true;
    patientQueueDetailsList.clear();
    notifyListeners();

    final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails(
        clinicID: patientArrivedAppointmentsHistoryList.first.clinicID,
        appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo,
        patientID: patientArrivedAppointmentsHistoryList.first.patientID);

    isAppointmentQueueDetailsLoading = false;

    result.fold(
      // (failure) async => await errorHandlerService.handleError(failure: failure),
      (failure) async {
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data != null) {
            patientQueueDetailsList.clear();
            patientQueueDetailsList = apiResponse.data!.patientQueueData!.patientInQueueList!;

            if (apiResponse.data!.patientQueueData!.isClinicConfigured == 1) {
              currentPatientQueueDetails = PatientInQueueList();
              isPatientHasQueueAppointment = true;
              for (var element in patientQueueDetailsList) {
                if (element.patientID == patientArrivedAppointmentsHistoryList.first.patientID) {
                  currentPatientQueueDetails = element;
                  currentQueueStatus = element.callType!;
                  // currentQueueStatus = 2;
                  break;
                }
              }
              // patientQueueDetailsList.first.callType = 1;
              // if (patientQueueDetailsList.length > 1) {
                patientQueueDetailsList.removeWhere((element) => element.patientID == patientArrivedAppointmentsHistoryList.first.patientID);
              // }
            } else {
              patientQueueDetailsList.clear();
              isPatientHasQueueAppointment = false;
            }
          } else {
            patientQueueDetailsList.clear();
            isPatientHasQueueAppointment = false;
          }

          // if (apiResponse.data != null && apiResponse.data!.isNotEmpty) {
          //   patientQueueDetailsList = apiResponse.data!;
          //
          //   // if (patientQueueDetailsList.first.isClinicConfigured == 1) {
          //   isPatientHasQueueAppointment = true;
          //   for (var element in patientQueueDetailsList) {
          //     if (element.patientID == patientArrivedAppointmentsHistoryList.first.patientID) {
          //       currentPatientQueueDetails = element;
          //       currentQueueStatus = element.callType!;
          //       // currentQueueStatus = 2;
          //       break;
          //     }
          //   }
          //   // patientQueueDetailsList.first.callType = 1;
          //   patientQueueDetailsList.removeWhere((element) => element.patientID == patientArrivedAppointmentsHistoryList.first.patientID);
          //   // } else {
          //   //   isPatientHasQueueAppointment = false;
          //   // }
          // } else {
          //   isPatientHasQueueAppointment = false;
          // }
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse.data);
          }
        }
      },
    );
  }

  Future<void> sendAskDocCallRequest({
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    required String requestType,
    required String remarks,
    required String userMobileNumber,
    Function(dynamic)? onSuccess,
    Function(String)? onError,
  }) async {
    final result = await myAppointmentsRepo.sendAskDocCallRequest(
        patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, requestType: requestType, remarks: remarks, userMobileNumber: userMobileNumber);

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

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

    final result = await myAppointmentsRepo.getDoctorsRatingCheck(
      doctorID: doctorID,
      projectID: projectID,
      clinicID: clinicID,
      appointmentNo: appointmentNo,
    );

    result.fold(
      (failure) async {
        // await errorHandlerService.handleError(failure: failure);
        if (onError != null) onError(failure.message);
        isAppointmentRatedResponseLoading = false;
        notifyListeners();
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) onError(apiResponse.errorMessage ?? "Unknown error");
          isAppointmentRatedResponseLoading = false;
          notifyListeners();
        } else if (apiResponse.messageStatus == 1) {
          appointmentRatedResponseModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse.data);
          }
          isAppointmentRatedResponseLoading = false;
        }
      },
    );
  }

  Future<void> submitDoctorRating(
      {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
      required int docRate,
      required String docNote,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    final result = await myAppointmentsRepo.sendDoctorRate(
        docRate,
        patientAppointmentHistoryResponseModel.appointmentNo!,
        patientAppointmentHistoryResponseModel.projectID!,
        patientAppointmentHistoryResponseModel.doctorID!,
        patientAppointmentHistoryResponseModel.clinicID!,
        docNote,
        patientAppointmentHistoryResponseModel.appointmentDate!,
        patientAppointmentHistoryResponseModel.doctorNameObj!,
        patientAppointmentHistoryResponseModel.projectName!,
        patientAppointmentHistoryResponseModel.clinicName!);

    result.fold(
      (failure) async => await errorHandlerService.handleError(failure: failure),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError?.call(apiResponse.errorMessage ?? 'Unknown error');
        } else if (apiResponse.messageStatus == 1) {
          notifyListeners();
          if (onSuccess != null) {
            // onSuccess(apiResponse.data);
          }
        }
      },
    );
  }

  /// Creates a reminder for the appointment
  /// Shows a bottom sheet to select reminder time and creates calendar event
  /// [reminderType] specifies the type of reminder (appointment, payment, or check-in)
  /// For payment and check-in reminders, shows only 1, 2, 3 hours before appointment
  /// Automatically removes previous reminders when creating a new one
  Future<void> createReminder({
    required BuildContext context,
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    ReminderType reminderType = ReminderType.appointment,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) async {
    try {
      CalenderUtilsNew calender = CalenderUtilsNew.instance;
      DateTime endDate = DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate);

      // Generate title and description based on reminder type
      String title;
      String description;
      String eventIdPrefix = reminderType.eventIdPrefix;

      switch (reminderType) {
        case ReminderType.appointment:
          title = "Appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj}";
          description = "${patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${patientAppointmentHistoryResponseModel.appointmentDate}";
          break;
        case ReminderType.payment:
          title = "Payment Due for Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
          description = "Payment reminder for appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj} at ${patientAppointmentHistoryResponseModel.projectName}";
          break;
        case ReminderType.checkIn:
          title = "Check-In Reminder for Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
          description = "Don't forget to check in for your appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj}";
          break;
      }

      // For payment and check-in, remove existing reminders first
      if (reminderType == ReminderType.payment || reminderType == ReminderType.checkIn) {
        // Remove all existing reminder types
        await _removeAllRemindersForAppointment(patientAppointmentHistoryResponseModel);
      }

      // For payment and check-in reminders, use custom hour-based options
      if (reminderType == ReminderType.payment || reminderType == ReminderType.checkIn) {
        _showHourBasedReminderSheet(
          context: context,
          patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
          reminderType: reminderType,
          title: title,
          description: description,
          eventIdPrefix: eventIdPrefix,
          endDate: endDate,
          onSuccess: onSuccess,
          onError: onError,
        );
      } else {
        // For appointment reminders, use the standard reminder sheet with all options
        BottomSheetUtils().showReminderBottomSheet(
          context,
          endDate,
          patientAppointmentHistoryResponseModel.doctorNameObj ?? "",
          "${patientAppointmentHistoryResponseModel.appointmentNo}",
          "",
          "",
          title: title,
          description: description,
          onSuccess: () {
            setAppointmentReminder(true, patientAppointmentHistoryResponseModel);
            if (onSuccess != null) {
              onSuccess(true);
            }
          },
          isMultiAllowed: true,
          onMultiDateSuccess: (int selectedIndex) async {
            String eventTitle;
            String eventDescription;

            switch (reminderType) {
              case ReminderType.appointment:
                eventTitle =
                    "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
                eventDescription = "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} in ${patientAppointmentHistoryResponseModel.projectName}";
                break;
              case ReminderType.payment:
                eventTitle = "Payment Due Reminder for Appointment #${patientAppointmentHistoryResponseModel.appointmentNo} with ${patientAppointmentHistoryResponseModel.doctorNameObj}";
                eventDescription = "Payment reminder for ${patientAppointmentHistoryResponseModel.doctorNameObj} at ${patientAppointmentHistoryResponseModel.projectName}";
                break;
              case ReminderType.checkIn:
                eventTitle = "Check-In Reminder for Appointment #${patientAppointmentHistoryResponseModel.appointmentNo} with ${patientAppointmentHistoryResponseModel.doctorNameObj}";
                eventDescription = "Check-in reminder for appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj} at ${patientAppointmentHistoryResponseModel.projectName}";
                break;
            }

            bool isEventAddedOrRemoved = await calender.createOrUpdateEvent(
              title: eventTitle,
              description: eventDescription,
              scheduleDateTime: DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
              eventId: "${eventIdPrefix}_${patientAppointmentHistoryResponseModel.appointmentNo}",
              location: patientAppointmentHistoryResponseModel.projectName ?? '',
              reminderMinutes: selectedIndex,
            );

            setAppointmentReminder(isEventAddedOrRemoved, patientAppointmentHistoryResponseModel);

            if (onSuccess != null) {
              onSuccess(isEventAddedOrRemoved);
            }
          },
        );
      }
    } catch (e) {
      if (onError != null) {
        onError(e.toString());
      }
    }
  }

  /// Removes all reminder types for an appointment (internal helper)
  Future<void> _removeAllRemindersForAppointment(PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel) async {
    CalenderUtilsNew calender = CalenderUtilsNew.instance;

    // Remove all possible reminder types
    for (var type in ReminderType.values) {
      try {
        await calender.checkAndRemove(
          id: "${type.eventIdPrefix}_${patientAppointmentHistoryResponseModel.appointmentNo}",
        );
      } catch (e) {
        // Ignore errors if reminder doesn't exist
      }
    }
  }

  /// Shows hour-based reminder selection for payment and check-in reminders
  void _showHourBasedReminderSheet({
    required BuildContext context,
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    required ReminderType reminderType,
    required String title,
    required String description,
    required String eventIdPrefix,
    required DateTime endDate,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) {
    showCommonBottomSheetWithoutHeight(
      context,
      title: "Select Reminder Time",
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Center(
              child: Text(
                "When would you like to be reminded?",
                style: TextStyle(
                  fontSize: 14.f,
                  color: AppColors.greyTextColor,
                ),
                textAlign: TextAlign.center,
              ),
            ),
            SizedBox(height: 24.h),
            _buildHourOption(
              context: context,
              hours: 1,
              icon: Icons.alarm,
              patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
              reminderType: reminderType,
              title: title,
              description: description,
              eventIdPrefix: eventIdPrefix,
              endDate: endDate,
              onSuccess: onSuccess,
              onError: onError,
            ),
            SizedBox(height: 12.h),
            _buildHourOption(
              context: context,
              hours: 2,
              icon: Icons.alarm,
              patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
              reminderType: reminderType,
              title: title,
              description: description,
              eventIdPrefix: eventIdPrefix,
              endDate: endDate,
              onSuccess: onSuccess,
              onError: onError,
            ),
            SizedBox(height: 12.h),
            _buildHourOption(
              context: context,
              hours: 3,
              icon: Icons.alarm,
              patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
              reminderType: reminderType,
              title: title,
              description: description,
              eventIdPrefix: eventIdPrefix,
              endDate: endDate,
              onSuccess: onSuccess,
              onError: onError,
            ),
          ],
        ),
      ),
      callBackFunc: () {},
      isFullScreen: false,
      isCloseButtonVisible: true,
    );
  }

  /// Builds an hour option button for payment/check-in reminders
  Widget _buildHourOption({
    required BuildContext context,
    required int hours,
    required IconData icon,
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    required ReminderType reminderType,
    required String title,
    required String description,
    required String eventIdPrefix,
    required DateTime endDate,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) {
    return GestureDetector(
      onTap: () async {
        Navigator.of(context).pop(); // Close the selection sheet

        // Create reminder with selected hours (converted to minutes)
        await _createReminderWithHours(
          hours: hours,
          patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
          reminderType: reminderType,
          title: title,
          description: description,
          eventIdPrefix: eventIdPrefix,
          endDate: endDate,
          onSuccess: onSuccess,
          onError: onError,
        );
      },
      child: Container(
        padding: EdgeInsets.all(16.h),
        decoration: BoxDecoration(
          color: AppColors.whiteColor,
          border: Border.all(color: AppColors.borderOnlyColor),
          borderRadius: BorderRadius.circular(12.r),
        ),
        child: Row(
          children: [
            Container(
              padding: EdgeInsets.all(12.h),
              decoration: BoxDecoration(
                color: AppColors.primaryRedColor.withAlpha(26),
                borderRadius: BorderRadius.circular(8.r),
              ),
              child: Icon(
                icon,
                color: AppColors.primaryRedColor,
                size: 24.h,
              ),
            ),
            SizedBox(width: 12.w),
            Expanded(
              child: Text(
                "$hours ${hours == 1 ? 'Hour' : 'Hours'} Before Appointment",
                style: TextStyle(
                  fontSize: 16.f,
                  fontWeight: FontWeight.w600,
                  color: AppColors.textColor,
                ),
              ),
            ),
            Icon(
              Icons.arrow_forward_ios,
              size: 16.h,
              color: AppColors.greyTextColor,
            ),
          ],
        ),
      ),
    );
  }

  /// Creates reminder with specific hours before appointment
  Future<void> _createReminderWithHours({
    required int hours,
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    required ReminderType reminderType,
    required String title,
    required String description,
    required String eventIdPrefix,
    required DateTime endDate,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) async {
    try {
      CalenderUtilsNew calender = CalenderUtilsNew.instance;

      // Convert hours to minutes
      int reminderMinutes = hours * 60;

      String eventTitle;
      String eventDescription;

      switch (reminderType) {
        case ReminderType.appointment:
          eventTitle =
              "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
          eventDescription = "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} in ${patientAppointmentHistoryResponseModel.projectName}";
          break;
        case ReminderType.payment:
          eventTitle = "Payment Due - $hours ${hours == 1 ? 'hour' : 'hours'} before Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
          eventDescription = "Payment reminder for ${patientAppointmentHistoryResponseModel.doctorNameObj} at ${patientAppointmentHistoryResponseModel.projectName}";
          break;
        case ReminderType.checkIn:
          eventTitle = "Check-In Now - $hours ${hours == 1 ? 'hour' : 'hours'} before Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}";
          eventDescription = "Check-in reminder for appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj} at ${patientAppointmentHistoryResponseModel.projectName}";
          break;
      }

      bool isEventAddedOrRemoved = await calender.createOrUpdateEvent(
        title: eventTitle,
        description: eventDescription,
        scheduleDateTime: DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
        eventId: "${eventIdPrefix}_${patientAppointmentHistoryResponseModel.appointmentNo}",
        location: patientAppointmentHistoryResponseModel.projectName ?? '',
        reminderMinutes: reminderMinutes,
      );

      setAppointmentReminder(isEventAddedOrRemoved, patientAppointmentHistoryResponseModel);

      if (onSuccess != null) {
        onSuccess(isEventAddedOrRemoved);
      }
    } catch (e) {
      if (onError != null) {
        onError(e.toString());
      }
    }
  }

  /// Deletes the reminder for the appointment
  /// Removes the calendar event associated with the appointment
  /// [reminderType] specifies the type of reminder to delete (appointment, payment, or check-in)
  Future<void> deleteReminder({
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    ReminderType reminderType = ReminderType.appointment,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) async {
    try {
      CalenderUtilsNew calender = CalenderUtilsNew.instance;
      String eventIdPrefix = reminderType.eventIdPrefix;

      bool isEventRemoved = await calender.checkAndRemove(
        id: "${eventIdPrefix}_${patientAppointmentHistoryResponseModel.appointmentNo}",
      );

      // Update the reminder status (inverted because we're removing)
      setAppointmentReminder(!isEventRemoved, patientAppointmentHistoryResponseModel);

      if (onSuccess != null) {
        onSuccess(isEventRemoved);
      }
    } catch (e) {
      if (onError != null) {
        onError(e.toString());
      }
    }
  }

  /// Toggle reminder for an appointment
  /// Creates or deletes reminder based on the current state
  /// [reminderType] specifies the type of reminder (appointment, payment, or check-in)
  Future<void> toggleReminder({
    required BuildContext context,
    required bool shouldCreateReminder,
    required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
    required ReminderType reminderType,
    Function(bool)? onSuccess,
    Function(String)? onError,
  }) async {
    if (shouldCreateReminder) {
      await createReminder(
        context: context,
        patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
        reminderType: reminderType,
        onSuccess: onSuccess,
        onError: onError,
      );
    } else {
      await deleteReminder(
        patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
        reminderType: reminderType,
        onSuccess: onSuccess,
        onError: onError,
      );
    }
  }

  Future<void> getAppointmentInvoice({required num appointmentNum, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    String? downloadInvoicePDFBase64 = "";
    final result = await myAppointmentsRepo.getAppointmentInvoice(appointmentNum);

    result.fold(
      (failure) async {
        if (onError != null) {
          onError(failure.message ?? "Unknown error");
        }
      },
      (apiResponse) async {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Unknown error");
          }
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data!.isNotEmpty) {
            GetInvoicesListResponseModel getInvoicesListResponseModel = apiResponse.data!.first;
            final result = await getIt
                .get<MyInvoicesRepo>()
                .downloadInvoice(setupId: getInvoicesListResponseModel.setupId!, invoiceNo: getInvoicesListResponseModel.invoiceNo!, projectID: getInvoicesListResponseModel.projectId!);

            result.fold(
              (failure) async {
                if (onError != null) {
                  onError(failure.message);
                }
              },
              (apiResponse) async {
                if (apiResponse.messageStatus == 2) {
                  // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
                } else if (apiResponse.messageStatus == 1) {
                  if (apiResponse.data != null && apiResponse.data!.isNotEmpty) {
                    downloadInvoicePDFBase64 = apiResponse.data!;
                    String path = await Utils.createFileFromString(downloadInvoicePDFBase64!, "pdf");
                    try {
                      OpenFilex.open(path);
                    } catch (ex) {
                      showCommonBottomSheetWithoutHeight(
                        GetIt.instance<NavigationService>().navigatorKey.currentContext!,
                        child: Utils.getErrorWidget(loadingText: "Cannot open file"),
                        callBackFunc: () {},
                        isFullScreen: false,
                        isCloseButtonVisible: true,
                      );
                    }
                  } else {
                    downloadInvoicePDFBase64 = "";
                  }
                  if (onSuccess != null) {
                    onSuccess(apiResponse);
                  }
                }
              },
            );
          } else {
            showCommonBottomSheetWithoutHeight(
              GetIt.instance<NavigationService>().navigatorKey.currentContext!,
              child: Utils.getErrorWidget(loadingText: "Cannot open invoice"),
              callBackFunc: () {},
              isFullScreen: false,
              isCloseButtonVisible: true,
            );
          }
        }
      },
    );
  }
}
