import 'dart:developer';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/appointments_models/appointment_slots.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_review_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/repositories/appointment_repo.dart';
import 'package:mc_common_app/repositories/branch_repo.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/views/appointments/book_appointment_schedules_view.dart';
import 'package:mc_common_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';

class AppointmentsVM extends BaseVM {
  final CommonRepo commonRepo;
  final CommonAppServices commonServices;
  final BranchRepo branchRepo;
  final AppointmentRepo appointmentRepo;

  AppointmentsVM({required this.commonServices, required this.appointmentRepo, required this.commonRepo, required this.branchRepo});

  bool isUpcomingEnabled = true;
  bool isFetchingLists = false;
  int selectedBranch = 0;
  int selectedAppointmentIndex = 0;
  int selectedAppointmentSubIndex = 0;
  int selectedAppointmentId = 0;

  List<AppointmentListModel> myAppointments = [];
  List<AppointmentListModel> myUpComingAppointments = [];
  List<AppointmentListModel> myFilteredAppointments = [];
  List<FilterListModel> appointmentsFilterOptions = [];
  List<CustomerData> customersAppointments = [];
  List<AppointmentListModel> myFilteredAppointments2 = [];

  // List<ScheduleData> availableSchedules = [];

  bool isFetchingServices = false;

  List<DropValue> branchCategories = [];

  bool isHomeTapped = false;
  bool isShowEmptyMessage = false;

  List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = [];

  List<ItemData> allSelectedItemsInAppointments = [];

  bool ifItemAlreadySelected(int id) {
    int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
    log("already selected at: $indexFound");
    if (indexFound != -1) {
      return true;
    }
    return false;
  }

  Future<void> onItemsSelectedInService() async {
    if (currentServiceSelection != null) {
      int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);

      if (index == -1) {
        double totalPrice = 0.0;
        for (var element in currentServiceSelection!.serviceItems!) {
          totalPrice = totalPrice + double.parse(element.price ?? "0.0");
        }
        currentServiceSelection!.currentTotalServicePrice = totalPrice;
        servicesInCurrentAppointment.insert(0, currentServiceSelection!);
      }

      resetCategorySelectionBottomSheet();

      notifyListeners();
    }
  }

  Future<void> onPayNowPressedForAppointment({required BuildContext context, required int appointmentID}) async {
    context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentID]);
    navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.partialAppointment);
  }

  Future<void> onBookAppointmentPressed(BuildContext context) async {
    Utils.showLoading(context);
    bool isSuccess = false;
    List<int> appointmentIdsList = [];
    try {
      GenericRespModel genericRespModel = await appointmentRepo.createServiceAppointment(
        schedules: serviceAppointmentScheduleList,
        serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0,
      );

      if (genericRespModel.data.isEmpty) {
        Utils.hideLoading(context);
        Utils.showToast(genericRespModel.message.toString());
        return;
      }

      if (genericRespModel.data != null && genericRespModel.data.isNotEmpty) {
        genericRespModel.data.forEach((element) {
          if (element['appointmentID'] != 0) {
            appointmentIdsList.add(element['appointmentID']);
            isSuccess = true;
          } else {
            isSuccess = false;
            Utils.showToast(element['message']);
            return;
          }
        });
      }

      context.read<DashboardVmCustomer>().onNavbarTapped(1);
      applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
      Utils.hideLoading(context);
      resetAfterBookingAppointment();
      if (isSuccess) {
        if (amountToPayForAppointment > 0) {
          context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: appointmentIdsList);
          navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
        } else {
          Utils.showToast(LocaleKeys.appointmentBookedSuccessfully.tr());
          getMyAppointmentsForCustomer();
          navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
        }
      }
    } catch (e) {
      Utils.showToast(e.toString());
    }
  }

  Future<void> onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async {
    context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentId]);
    navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
  }

  OfferRequestCommentModel selectedCancelAppointmentReason = OfferRequestCommentModel(
    index: 0,
    isSelected: true,
    title: LocaleKeys.changedMind.tr(),
  );

  List<OfferRequestCommentModel> cancelAppointmentReasonList = [
    OfferRequestCommentModel(
      index: 0,
      isSelected: true,
      title: LocaleKeys.wantToRescheduleAppointment.tr(),
    ),
    OfferRequestCommentModel(
      index: 1,
      isSelected: false,
      title: LocaleKeys.noNeedForService.tr(),
    ),
    OfferRequestCommentModel(
      index: 2,
      isSelected: false,
      title: LocaleKeys.testTheService.tr(),
    ),
    OfferRequestCommentModel(
      index: 3,
      isSelected: false,
      title: LocaleKeys.otherVar.tr(),
    ),
  ];

  void updateSelectedCancelAppointmentReason(int index) {
    for (var value in cancelAppointmentReasonList) {
      value.isSelected = false;
    }
    selectedCancelAppointmentReason = cancelAppointmentReasonList[index];
    cancelAppointmentReasonList[index].isSelected = true;
    notifyListeners();
  }

  String cancelAppointmentDescription = "";
  String cancelAppointmentDescriptionError = "";

  void updateCancelAppointmentDescription(String value) {
    cancelAppointmentDescription = value;
    if (value.isNotEmpty) {
      cancelAppointmentDescriptionError = "";
    }
  }

  Future<void> onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
    Utils.showLoading(context);
    try {
      GenericRespModel genericRespModel = await appointmentRepo.cancelOrRescheduleServiceAppointment(
        serviceAppointmentID: appointmentListModel.id ?? 0,
        serviceSlotID: appointmentListModel.serviceSlotID ?? 0,
        appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel
      );

      if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
        Utils.hideLoading(context);
        Utils.showToast(genericRespModel.message.toString());
        return;
      }
      if (genericRespModel.messageStatus == 1 && AppState().currentAppType == AppType.customer) {
        context.read<DashboardVmCustomer>().onNavbarTapped(1);
        applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
        Utils.showToast(genericRespModel.message.toString());
        await getMyAppointmentsForCustomer();
        Utils.hideLoading(context);
        getMyAppointmentsForCustomer();
        navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
      }
    } catch (e) {
      logger.e(e.toString());
      Utils.showToast(e.toString());
    }
  }

  void updateIsHomeTapped(bool value) {
    isHomeTapped = value;
    if (currentServiceSelection != null) {
      currentServiceSelection!.isHomeSelected = value;
    }
    notifyListeners();
  }

  SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateProviderCategoryId(SelectionModel id) {
    branchSelectedCategoryId = id;
    getBranchServices(categoryId: branchSelectedCategoryId.selectedId);
    notifyListeners();
  }

  List<FilterListModel> branchesCategoriesFilterOptions = [];

  List<ServiceModel> servicesInCurrentAppointment = [];
  ServiceModel? currentServiceSelection;

  CurrentLocationInfoModel? currentLocationInfoModel;

  void updateCurrentLocationInfoModel(CurrentLocationInfoModel? value) {
    currentLocationInfoModel = value;
    pickHomeLocationError = "";
    if (currentServiceSelection != null && value != null) {
      currentServiceSelection!.servicelocationInfo = value;
    }

    if (value != null) {
      for (var service in servicesInCurrentAppointment) {
        service.servicelocationInfo.address = value.address;
        service.servicelocationInfo.latitude = value.latitude;
        service.servicelocationInfo.longitude = value.longitude;
        service.servicelocationInfo.distanceToBranch = value.distanceToBranch;
        service.servicelocationInfo.homeChargesInCurrentService = double.parse(service.rangePricePerKm ?? "0.0");
      }
    }

    notifyListeners();
  }

  void updateBranchServiceId(SelectionModel id) async {
    branchSelectedServiceId = id;
    currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
    if (currentServiceSelection == null) {
      return;
    }

    if (!(currentServiceSelection!.isAllowAppointmentHome ?? false)) {
      updateIsHomeTapped(false);
      return;
    }
    if (currentLocationInfoModel != null) {
      currentLocationInfoModel!.homeChargesInCurrentService = double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0");
      currentServiceSelection!.servicelocationInfo = CurrentLocationInfoModel(
        address: currentLocationInfoModel!.address,
        latitude: currentLocationInfoModel!.latitude,
        longitude: currentLocationInfoModel!.longitude,
        distanceToBranch: currentLocationInfoModel!.distanceToBranch,
        homeChargesInCurrentService: double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0"),
      );
    }

    notifyListeners();
  }

  void removeServiceInCurrentAppointment(int index) {
    int serviceId = servicesInCurrentAppointment.elementAt(index).serviceProviderServiceId ?? -1;
    allSelectedItemsInAppointments.removeWhere((element) => element.serviceProviderServiceId == serviceId);
    servicesInCurrentAppointment[index].serviceItems!.clear();
    servicesInCurrentAppointment.removeAt(index);
    notifyListeners();
  }

  resetCategorySelectionBottomSheet() {
    selectedSubServicesCounter = 0;
    branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    isHomeTapped = false;
    branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    currentServiceSelection = null;
  }

  resetAfterBookingAppointment() {
    allSelectedItemsInAppointments.clear();
    currentServiceSelection = null;
    for (var element in servicesInCurrentAppointment) {
      if (element.serviceItems != null) {
        element.serviceItems!.clear();
        element.serviceItems = [];
      }
    }
    servicesInCurrentAppointment.clear();
    servicesInCurrentAppointment = [];
    currentLocationInfoModel = null;
    log("here servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
  }

  List<EnumsModel> myAppointmentsEnum = [];

  Future<void> populateAppointmentsFilterList() async {
    if (appointmentsFilterOptions.isNotEmpty) return;

    myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.appointmentsFilterEnumId); // 13 is to get Appointments Filter Enums

    for (int i = 0; i < myAppointmentsEnum.length; i++) {
      appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue));
    }
    appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));

    notifyListeners();
  }

  applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) {
    log("appointmentStatusEnum: $appointmentStatusEnum");
    // isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP
    if (appointmentsFilterOptions.isEmpty) return;
    for (var value in appointmentsFilterOptions) {
      value.isSelected = false;
    }

    for (var element in appointmentsFilterOptions) {
      if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
        element.isSelected = true;
      }
    }
    // appointmentsFilterOptions[
    // appointmentStatusEnum.getIdFromAppointmentStatusEnum()]
    //     .isSelected = true;

    if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) {
      myFilteredAppointments = myAppointments;
      if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers();
      notifyListeners();
      return;
    }

    myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
    if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers();
    notifyListeners();
  }

  findAppointmentsBasedOnCustomers() {
    // Use a Set to ensure uniqueness of customerIDs
    Set<int> uniqueCustomerIDs = <int>{};

    // Extract unique customerIDs
    for (var item in myFilteredAppointments) {
      uniqueCustomerIDs.add(item.customerID ?? 0);
    }

    // Create a list of CustomerData instances
    myFilteredAppointments2 = uniqueCustomerIDs.map((id) {
      List<AppointmentListModel> list = myFilteredAppointments.where((item) => item.customerID == id).toList();
      list.sort((a, b) => DateHelper.parseStringToDate(DateHelper.formatDateT(b.appointmentDate!))
          .millisecondsSinceEpoch
          .compareTo(DateHelper.parseStringToDate(DateHelper.formatDateT(a.appointmentDate!)).millisecondsSinceEpoch));
      AppointmentListModel model = list.first;
      model.customerAppointmentList = list;
      return model;
    }).toList();

    // customersAppointments = uniqueCustomerIDs.map((id) {
    //   List<AppointmentListModel> list = myFilteredAppointments
    //       .where((item) => item.customerID == id)
    //       .toList();
    //   var customerItem =
    //       myFilteredAppointments.firstWhere((item) => item.customerID == id);
    //
    //   return CustomerData(
    //     customerID: id,
    //     customerName: customerItem.customerName ?? "",
    //     appointmentList: list,
    //   );
    // }).toList();
  }

  // Future<void> getMyAppointments() async {
  //   setState(ViewState.busy);
  //
  //   myAppointments =
  //   await appointmentRepo.getMyAppointmentsForCustomersByFilters();
  //   myFilteredAppointments = myAppointments;
  //   myUpComingAppointments = myAppointments
  //       .where((element) =>
  //   element.appointmentStatusEnum == AppointmentStatusEnum.booked)
  //       .toList();
  //   applyFilterOnAppointmentsVM(
  //       appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
  //   if (myUpComingAppointments.isEmpty) {
  //     isShowEmptyMessage = true;
  //   }
  //   setState(ViewState.idle);
  //   notifyListeners();
  // }

  Future<void> getMyAppointmentsForCustomer({bool isNeedToRebuild = false}) async {
    if (isNeedToRebuild) setState(ViewState.busy);

    myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters();
    // myFilteredAppointments = myAppointments;

    setState(ViewState.idle);
    applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);

    myUpComingAppointments = myAppointments
        .where((element) =>
            (element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
            (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
        .toList();
    notifyListeners();
  }

  AppointmentSlots? appointmentSlots;

  Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, required BuildContext context, bool isNeedToRebuild = false}) async {
    if (isNeedToRebuild) setState(ViewState.busy);
    try {
      GenericRespModel genericRespModel = await appointmentRepo.getAppointmentSlots(map);
      if (genericRespModel.messageStatus == 1) {
        appointmentSlots = AppointmentSlots.fromJson(genericRespModel.data);
      } else {
        Utils.showToast(genericRespModel.message.toString());
      }
    } catch (e) {
      Utils.showToast(e.toString());
    }
  }

  Future<void> getMyAppointmentsForProvider(Map<String, dynamic> map) async {
    setState(ViewState.busy);
    myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map);
    myFilteredAppointments = myAppointments;
    myUpComingAppointments = myAppointments
        .where((element) =>
            (element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) &&
            (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
        .toList();
    notifyListeners();
    applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
    setState(ViewState.idle);
  }

  Future<bool> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async {
    if (isNeedToRebuild) setState(ViewState.busy);
    try {
      GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(appointmentId: appointmentId, appointmentStatusEnum: appointmentStatusEnum);

      if (genericRespModel.messageStatus == 1) {
        Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr());
        return true;
      } else {
        Utils.showToast(genericRespModel.message.toString());
        return false;
      }
    } catch (e) {
      Utils.showToast(e.toString());
      return false;
    }
  }

  Future<bool> updateAppointmentPaymentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
    if (isNeedToRebuild) setState(ViewState.busy);
    try {
      GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentPaymentStatus(map);

      if (genericRespModel.messageStatus == 1) {
        Utils.showToast(LocaleKeys.paymentStatusUpdated.tr());
        return true;
      } else {
        Utils.showToast(genericRespModel.message.toString());
        return false;
      }
    } catch (e) {
      Utils.showToast(e.toString());
      return false;
    }
  }

  bool isShowMergeButton() {
    return myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList!.every((appointment) => appointment.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
        myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList!.length > 1;
  }

  Future<GenericRespModel> createMergeAppointment(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
    if (isNeedToRebuild) setState(ViewState.busy);
    GenericRespModel genericRespModel = await appointmentRepo.createMergeAppointment(map);

    return genericRespModel;
  }

  bool inNeedToEnableMergeButton = false;

  void updateCheckBoxInMergeRequest(int currentIndex) {
    myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
        !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);

    int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []);
    if (count > 1) {
      inNeedToEnableMergeButton = true;
    } else {
      inNeedToEnableMergeButton = false;
    }
    notifyListeners();
  }

  int countSelected(List<AppointmentListModel> appointments) {
    return appointments.where((appointment) => appointment.isSelected == true).toList().length;
  }

  updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
    for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
      element.date!.isSelected = false;
    }

    serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true;
    serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex;
    final date = TimeSlotModel(
      date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date,
      slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId,
      isSelected: true,
      slot: "",
    );
    serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
    notifyListeners();
  }

  updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) {
    for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
      for (var element in element.availableSlots!) {
        element.isSelected = false;
      }
    }
    int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
    serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
    serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
    notifyListeners();
  }

  double amountToPayForAppointment = 0.0;
  double totalAmount = 0.0;

  List<ItemData> serviceItemsFromApi = [];

  ProviderProfileModel? providerProfileModel;

  int selectedSubServicesCounter = 0;

  void onItemUpdateOrSelected(int index, bool selected, int itemId) {
    int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);

    serviceItemsFromApi[index].isUpdateOrSelected = selected;
    serviceItemsFromApi[index].isHomeSelected = isHomeTapped;
    log(" serviceIndex: $serviceIndex");

    if (selected) {
      selectedSubServicesCounter = selectedSubServicesCounter + 1;
      selectSubServicesError = "";

      if (!ifItemAlreadySelected(serviceItemsFromApi[index].id!)) {
        currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
        allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
        log("Here with: ${serviceItemsFromApi[index].description}");
        if (serviceIndex != -1) {
          // servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
          servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
              servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
        }
      }
    } else {
      selectedSubServicesCounter = selectedSubServicesCounter - 1;
      currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
      allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
      if (serviceIndex != -1) {
        servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
            servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
        if (servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice < 0.0) {
          servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice * -1;
        }
        // servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
      }
    }

    notifyListeners();
  }

  Future<void> applyFilterOnBranches({required int index}) async {
    if (branchesCategoriesFilterOptions.isEmpty) return;
    for (var value in branchesCategoriesFilterOptions) {
      value.isSelected = false;
    }
    branchesCategoriesFilterOptions[index].isSelected = true;

    await getBranchesBasedOnCategoryFilters(categoryId: branchesCategoriesFilterOptions[index].id);
    notifyListeners();
  }

  Future<List<ItemData>> getServiceItems({required int serviceId}) async {
    setState(ViewState.busy);
    serviceItemsFromApi.clear();
    serviceItemsFromApi = await branchRepo.getServiceItems(serviceId: serviceId);
    selectedSubServicesCounter = 0;
    for (var item in serviceItemsFromApi) {
      if (ifItemAlreadySelected(item.id!)) {
        item.isUpdateOrSelected = true;
        selectedSubServicesCounter++;
      }
    }
    setState(ViewState.idle);
    return serviceItemsFromApi;
  }

  String pickHomeLocationError = "";
  String selectSubServicesError = "";

  SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updatePickHomeLocationError(String value) {
    pickHomeLocationError = value;
    // notifyListeners();
  }

  bool isServiceSelectionValidated() {
    if (branchSelectedServiceId.selectedId == -1) {
      return false;
    }

    if (isHomeTapped) {
      if (currentLocationInfoModel == null || currentLocationInfoModel!.address == "") {
        updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError);
        return false;
      }
    }
    return true;
  }

  bool validateItemsSelection() {
    for (var value in serviceItemsFromApi) {
      if (value.isUpdateOrSelected!) {
        return true;
      }
    }
    selectSubServicesError = LocaleKeys.selectOneSubService.tr();
    notifyListeners();
    return false;
  }

  String getTotalPrice(List<ServiceModel> serviceItems) {
    var totalPrice = 0.0;
    for (var element in serviceItems) {
      totalPrice = totalPrice + (element.currentTotalServicePrice);
    }
    return totalPrice.toString();
  }

  void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      enableDrag: true,
      builder: (BuildContext context) {
        return const AppointmentServicePickBottomSheet();
      },
    );
  }

  void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) {
    showModalBottomSheet(
        context: context,
        isScrollControlled: true,
        enableDrag: true,
        builder: (BuildContext context) {
          double totalKms = double.parse(selectedService.servicelocationInfo.distanceToBranch.toStringAsFixed(2));
          double pricePerKm = double.parse(selectedService.servicelocationInfo.homeChargesInCurrentService.toStringAsFixed(2));
          return InfoBottomSheet(
              title: LocaleKeys.chargesBreakdown.tr().toText(fontSize: 24, isBold: true),
              description: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  LocaleKeys.services.tr().toText(fontSize: 16, isBold: true),
                  Column(
                    children: List.generate(
                      selectedService.serviceItems!.length,
                      (index) => Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
                          "${selectedService.serviceItems![index].price} ${LocaleKeys.sar.tr()}".toText(fontSize: 12, isBold: true),
                        ],
                      ),
                    ),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      "${selectedService.currentTotalServicePrice} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true),
                    ],
                  ),
                  if (selectedService.isHomeSelected) ...[
                    20.height,
                    LocaleKeys.homeLocation.tr().toText(fontSize: 16, isBold: true),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        "${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
                        "$pricePerKm x $totalKms".toText(fontSize: 12, isBold: true),
                      ],
                    ),
                    8.height,
                    Row(
                      mainAxisAlignment: MainAxisAlignment.end,
                      children: [
                        "${((pricePerKm) * totalKms).toStringAsFixed(2)} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true),
                      ],
                    ),
                  ],
                  30.height,
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      LocaleKeys.totalAmount.tr().toText(fontSize: 16, isBold: true),
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.end,
                        children: [
                          (selectedService.isHomeSelected ? ((selectedService.currentTotalServicePrice) + (pricePerKm * totalKms)).toStringAsFixed(2) : "${selectedService.currentTotalServicePrice}")
                              .toText(fontSize: 29, isBold: true),
                          2.width,
                          LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
                        ],
                      )
                    ],
                  ),
                  30.height,
                ],
              ));
        });
  }

  void onReviewButtonPressed(BuildContext context) {
    bool isValidated = false;

    for (int i = 0; i < serviceAppointmentScheduleList.length; i++) {
      final schedule = serviceAppointmentScheduleList[i];
      if (schedule.selectedCustomTimeDateSlotModel == null) {
        isValidated = false;
        break;
      }
      if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
        isValidated = false;
        break;
      } else {
        if (schedule.selectedCustomTimeDateSlotModel!.availableSlots == null) {
          isValidated = false;
          break;
        } else {
          TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected);
          if (slot.date.isNotEmpty) {
            isValidated = true;
            break;
          }
        }
      }
    }
    if (!isValidated) {
      Utils.showToast(LocaleKeys.selectAppointmentTime.tr());
      return;
    }
    navigateWithName(context, AppRoutes.reviewAppointmentView);
  }

  void onServicesNextPressed(BuildContext context) async {
    if (allSelectedItemsInAppointments.isEmpty) {
      Utils.showToast(LocaleKeys.pleaseSelectService.tr());
      return;
    }
    Utils.showLoading(context);

    List<String> serviceItemIdsForHome = [];
    List<String> serviceItemIdsForWorkshop = [];
    for (var serviceItem in allSelectedItemsInAppointments) {
      if (serviceItem.isHomeSelected!) {
        serviceItemIdsForHome.add(serviceItem.id!.toString());
      } else {
        serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
      }
    }

    serviceAppointmentScheduleList = await appointmentRepo.mergeServiceIntoAvailableSchedules(
      serviceItemIdsForHome: serviceItemIdsForHome,
      serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
    );

    if (serviceAppointmentScheduleList.isEmpty) {
      Utils.hideLoading(context);
      Utils.showToast(LocaleKeys.noAppointmentAvailable.tr());
      return;
    }
    totalAmount = 0.0;
    amountToPayForAppointment = 0.0;
    for (var schedule in serviceAppointmentScheduleList) {
      amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
      totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
      for (var service in schedule.servicesListInAppointment!) {
        int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceProviderServiceId == service.serviceProviderServiceId);
        if (index != -1) {
          if (servicesInCurrentAppointment[index].isHomeSelected) {
            service.servicelocationInfo = servicesInCurrentAppointment[index].servicelocationInfo;
            schedule.locationInfoModel = servicesInCurrentAppointment[index].servicelocationInfo;
            totalAmount =
                totalAmount + ((servicesInCurrentAppointment[index].servicelocationInfo.distanceToBranch) * servicesInCurrentAppointment[index].servicelocationInfo.homeChargesInCurrentService);
            schedule.totalLocationCharges = schedule.totalLocationCharges! +
                (servicesInCurrentAppointment[index].servicelocationInfo.distanceToBranch * servicesInCurrentAppointment[index].servicelocationInfo.homeChargesInCurrentService);
          }
        }
      }
    }
    Utils.hideLoading(context);

    navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment

    notifyListeners();
  }

  Future<void> onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
    Utils.showLoading(context);

    List<String> serviceItemIdsForHome = [];
    List<String> serviceItemIdsForWorkshop = [];
    for (var service in appointmentListModel.appointmentServicesList!) {
      for (var serviceItem in service.serviceItems!) {
        serviceItemIdsForWorkshop.add(serviceItem.id!.toString());

        // if (serviceItem.isHomeSelected ?? false) {
        //   serviceItemIdsForHome.add(serviceItem.id!.toString());
        // } else {
        //   serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
        // }
      }
    }

    serviceAppointmentScheduleList = await appointmentRepo.mergeServiceIntoAvailableSchedules(
      serviceItemIdsForHome: serviceItemIdsForHome,
      serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
    );

    if (serviceAppointmentScheduleList.isEmpty) {
      Utils.hideLoading(context);
      Utils.showToast(LocaleKeys.noAppointmentAvailable.tr());
      return;
    }
    Utils.hideLoading(context);

    navigateWithName(
      context,
      AppRoutes.bookAppointmenSchedulesView,
      arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0),
    ); // 2 For Rescheduling an Appointment

    notifyListeners();
  }

  Future<void> onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async {
    Utils.showLoading(context);
    try {
      GenericRespModel genericRespModel = await appointmentRepo.cancelOrRescheduleServiceAppointment(
        serviceAppointmentID: appointmentId,
        serviceSlotID: selectedSlotId,
        appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel
      );

      if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
        Utils.hideLoading(context);
        Utils.showToast(genericRespModel.message.toString());
        return;
      }
      if (genericRespModel.messageStatus == 1) {
        context.read<DashboardVmCustomer>().onNavbarTapped(1);
        applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
        Utils.showToast(genericRespModel.message.toString());
        getMyAppointmentsForCustomer();
        Utils.hideLoading(context);
        navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
      }
    } catch (e) {
      Utils.showToast(e.toString());
    }
  }

  // REPORT A COMPLAINT

  String complaintDescription = "";
  String complaintDescriptionError = "";

  void updateComplaintDescription(String value) {
    complaintDescription = value;
    if (value.isNotEmpty) {
      complaintDescriptionError = "";
    }
  }

  Future buildComplaintBottomSheet({required int customerID, required int complainType, required String customerName, required BuildContext context}) {
    return showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      enableDrag: true,
      builder: (BuildContext context) {
        return InfoBottomSheet(
          title: LocaleKeys.reportComplaint.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
          description: Padding(
            padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (customerName.isNotEmpty) ...[
                  ("${LocaleKeys.customerName.tr()}: $customerName").toText(fontSize: 15, color: MyColors.lightTextColor),
                ],
                10.height,
                TxtField(
                  maxLines: 5,
                  value: complaintDescription,
                  errorValue: complaintDescriptionError,
                  keyboardType: TextInputType.text,
                  hint: LocaleKeys.description.tr(),
                  onChanged: (v) => updateComplaintDescription(v),
                ),
                25.height,
                ShowFillButton(
                  title: LocaleKeys.submit.tr(),
                  onPressed: () async {
                    if (complaintDescription.isEmpty) {
                      Utils.showToast(LocaleKeys.descriptionCannotBeEmpty.tr());
                      return;
                    }
                    final status = await reportComplaintAgainstCustomer(
                      customerID: customerID,
                      comment: complaintDescription,
                      complaintType: complainType, // 1 for Appointment and 2 for Requests
                      context: context,
                    );

                    if (status) {
                      updateComplaintDescription("");
                      pop(context);
                    }
                  },
                  maxWidth: double.infinity,
                ),
                19.height,
              ],
            ),
          ),
        );
      },
    );
  }

  Future<bool> reportComplaintAgainstCustomer({required int customerID, required String comment, required int complaintType, required BuildContext context}) async {
    Utils.showLoading(context);
    try {
      GenericRespModel genericRespModel = await commonRepo.createComplainFromProvider(customerId: customerID, comment: comment, complainType: complaintType);
      Utils.showToast(genericRespModel.message.toString());
      Utils.hideLoading(context);
      return genericRespModel.messageStatus == 1;
    } catch (e, s) {
      logger.e(s);
      Utils.hideLoading(context);
      Utils.showToast(e.toString());
      return false;
    }
  }

  // BRANCHES RELATED

  List<BranchDetailModel> nearbyBranches = [];
  List<BranchDetailModel> myRecentBranches = [];
  BranchDetailModel? selectedBranchModel;

  List<ServiceModel> branchServices = [];

  Future<void> populateBranchesFilterList() async {
    if (branchesCategoriesFilterOptions.isNotEmpty) return;
    branchesCategoriesFilterOptions.clear();
    setOnlyState(ViewState.busy);
    Category category = await branchRepo.fetchBranchCategory();
    category.data?.forEach((element) {
      branchesCategoriesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, iconUrl: element.serviceCategoryIconUrl ?? "", title: element.categoryName ?? "N/A"));
    });
    branchesCategoriesFilterOptions.insert(0, FilterListModel(id: 0, isSelected: true, title: "All Branches"));
    notifyListeners();
    setState(ViewState.idle);
  }

  Future<void> getAllNearBranches({bool isNeedToRebuild = false, bool isFromRefresh = false}) async {
    nearbyBranches.clear();
    if (isNeedToRebuild) setState(ViewState.busy);

    if (isFromRefresh) {
      var selectedFilter = branchesCategoriesFilterOptions.firstWhere((element) => element.isSelected);
      log("selectedFilter: ${selectedFilter.id}");

      if (selectedFilter.id == 0) {
        nearbyBranches = await branchRepo.getAllNearBranchAndServices(
          latitude: AppState().currentLocation.latitude,
          longitude: AppState().currentLocation.longitude,
        );
      } else {
        nearbyBranches = await branchRepo.getBranchesByFilters(
          categoryIdsList: [selectedFilter.id],
          latitude: AppState().currentLocation.latitude,
          longitude: AppState().currentLocation.longitude,
        );
      }
      setState(ViewState.idle);
      return;
    }
    nearbyBranches = await branchRepo.getAllNearBranchAndServices(
      latitude: AppState().currentLocation.latitude,
      longitude: AppState().currentLocation.longitude,
    );
    setState(ViewState.idle);
  }

  Future<void> getMyRecentBranches({bool isNeedToRebuild = false}) async {
    myRecentBranches.clear();
    if (isNeedToRebuild) setState(ViewState.busy);
    myRecentBranches = await branchRepo.getMyRecentBranchesWithServices();
    setState(ViewState.idle);
  }

  void getBranchCategories() async {
    for (var value in selectedBranchModel!.branchServices!) {
      if (!isCategoryAlreadyPresent(value.categoryId!)) {
        branchCategories.add(DropValue(value.categoryId!, value.categoryName!, ""));
      }
    }
    notifyListeners();
  }

  Future<void> getBranchServices({required int categoryId}) async {
    branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    isHomeTapped = false;
    pickHomeLocationError = "";
    if (categoryId != -1) {
      isFetchingServices = true;
      branchServices = getFilteredBranchServices(categoryId: categoryId);
      isFetchingServices = false;
      notifyListeners();
    }
  }

  List<ServiceModel> getFilteredBranchServices({required int categoryId}) {
    List<ServiceModel> filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList();
    return filteredServices;
  }

  Future<void> getProviderProfileWithBranchesAndServices(int providerId) async {
    double latitude = AppState().currentLocation.latitude;
    double longitude = AppState().currentLocation.longitude;
    setState(ViewState.busy);
    try {
      providerProfileModel = await branchRepo.getBranchAndServicesByProviderId(providerId, latitude: latitude, longitude: longitude);
    } catch (e, s) {
      logger.e(s);
      setState(ViewState.idle);
      Utils.showToast(e.toString());
    }
    setState(ViewState.idle);
  }

  bool isCategoryAlreadyPresent(int id) {
    final contain = branchCategories.where((element) => element.id == id);
    if (contain.isEmpty) {
      return false;
    }
    return true;
  }

  updateSelectedBranch(BranchDetailModel branchDetailModel) {
    selectedBranchModel = branchDetailModel;
    getBranchCategories();
    notifyListeners();
  }

  int branchFiltersCounter = 0;

  updateBranchFiltersCounter(int value) {
    branchFiltersCounter = value;
    notifyListeners();
  }

  double branchFilterCurrentDistance = 25.0;

  updateBranchFilterCurrentDistance(double value) {
    branchFilterCurrentDistance = value;
    notifyListeners();
  }

// Provider Filter
  List<DropValue> branchFilterProviderSearchHistory = [];

  void removeBranchFilterProviderSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      branchFilterProviderSearchHistory.clear();
      notifyListeners();
      return;
    }
    branchFilterProviderSearchHistory.removeAt(index);
    if (branchFilterProviderSearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter - 1);
      // branchFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addBranchFilterProviderSearchHistory({required DropValue value}) {
    if (branchFilterProviderSearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter + 1);
    }
    branchFilterProviderSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel branchFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateBranchFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue providerDrop = providersDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: branchFilterProviderSearchHistory, value: providerDrop)) {
        addBranchFilterProviderSearchHistory(value: providerDrop);
      }
    }

    branchFilterSelectedProviderId = id;
    notifyListeners();
  }

// Category Filter
  List<DropValue> branchFilterCategorySearchHistory = [];

  void removeBranchFilterCategorySearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      branchFilterCategorySearchHistory.clear();
      notifyListeners();
      return;
    }
    branchFilterCategorySearchHistory.removeAt(index);
    if (branchFilterCategorySearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter - 1);
      // branchFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addBranchFilterCategorySearchHistory({required DropValue value}) {
    if (branchFilterCategorySearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter + 1);
    }
    branchFilterCategorySearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel branchFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateBranchFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue categoryDrop = categoryDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: branchFilterCategorySearchHistory, value: categoryDrop)) {
        addBranchFilterCategorySearchHistory(value: categoryDrop);
      }
    }

    branchFilterSelectedCategoryId = id;
    notifyListeners();
  }

// Services Filter
  List<DropValue> branchFilterServicesSearchHistory = [];

  void removeBranchFilterServicesSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      branchFilterServicesSearchHistory.clear();
      notifyListeners();
      return;
    }
    branchFilterServicesSearchHistory.removeAt(index);
    if (branchFilterServicesSearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter - 1);
      // branchFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addBranchFilterServicesSearchHistory({required DropValue value}) {
    if (branchFilterServicesSearchHistory.isEmpty) {
      updateBranchFiltersCounter(branchFiltersCounter + 1);
    }
    branchFilterServicesSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel branchFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateBranchFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue serviceDrop = servicesDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: branchFilterServicesSearchHistory, value: serviceDrop)) {
        addBranchFilterServicesSearchHistory(value: serviceDrop);
      }
      notifyListeners();
      return;
    }

    branchFilterSelectedServiceId = id;
    notifyListeners();
  }

  ifAlreadyExist({required List<DropValue> list, required DropValue value}) {
    int index = list.indexWhere((element) {
      return element.id == value.id;
    });

    if (index != -1) {
      return true;
    }

    return false;
  }

// Rating filter

  double branchFilterByRating = 4.0;

  updateBranchFilterByRating(double value) {
    branchFilterByRating = value;
    notifyListeners();
  }

  List<DropValue> categoryDropList = [];
  List<DropValue> providersDropList = [];
  List<DropValue> servicesDropList = [];
  List<DropValue> itemsDropList = [];

  Future<void> fetchAllProviders() async {
    if (providersDropList.isNotEmpty) return;

    providersDropList.clear();
    setOnlyState(ViewState.busy);
    List<ProviderBasicDataModel> providers = await branchRepo.getAllProvidersWitheBasicData();
    for (var element in providers) {
      providersDropList.add(
        DropValue(element.id ?? 0, element.providerName ?? "N/A", ""),
      );
    }
    setState(ViewState.idle);
  }

  Future<void> fetchAllCategories() async {
    if (categoryDropList.isNotEmpty) return;
    categoryDropList.clear();
    setOnlyState(ViewState.busy);
    Category category = await branchRepo.fetchBranchCategory();
    category.data?.forEach((element) {
      categoryDropList.add(
        DropValue(element.id ?? 0, ((element.categoryName!.isEmpty ? "N/A" : element.categoryName) ?? "N/A"), ""),
      );
    });
    setState(ViewState.idle);
  }

  Future<void> fetchAllServices() async {
    if (servicesDropList.isNotEmpty) return;
    servicesDropList.clear();
    setState(ViewState.busy);
    Services services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: -1); // to get all services

    for (var element in services.data!) {
      servicesDropList.add(
        DropValue(
          element.id ?? 0,
          element.description ?? "N/A",
          "",
        ),
      );
    }
    setState(ViewState.idle);
  }

  Future<void> fetchAllItems() async {
    if (itemsDropList.isNotEmpty) return;
    itemsDropList.clear();
    setState(ViewState.busy);
    List<ItemData> itemsList = await branchRepo.getServiceItems(serviceId: -1); // to get all services

    for (ItemData element in itemsList) {
      if (element.name != null && element.name!.isNotEmpty) {
        itemsDropList.add(
          DropValue(
            element.id ?? 0,
            element.name ?? "",
            "",
          ),
        );
      }
    }
    setState(ViewState.idle);
  }

  Future<void> populateDataForBranchesFilter() async {
    await fetchAllProviders();
    await fetchAllCategories();
    await fetchAllServices();
    updateBranchFilterCurrentDistance(25.0);
  }

  void clearBranchFilters() {
    branchFilterServicesSearchHistory.clear();
    branchFilterCategorySearchHistory.clear();
    branchFilterProviderSearchHistory.clear();
    branchFilterByRating = 4.0;
    branchFilterCurrentDistance = 25.0;
    branchFiltersCounter = 0;
    clearBranchFilterSelections();
    notifyListeners();
  }

  void clearBranchFilterSelections() {
    branchFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    branchFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    branchFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
  }

  Future<void> getBranchesBasedOnFilters() async {
    nearbyBranches.clear();
    setState(ViewState.busy);
    List<int> providersIdsList = [];
    if (branchFilterProviderSearchHistory.isNotEmpty) {
      for (var element in branchFilterProviderSearchHistory) {
        providersIdsList.add(element.id);
      }
    }
    List<int> categoryIdsList = [];
    if (branchFilterCategorySearchHistory.isNotEmpty) {
      for (var element in branchFilterCategorySearchHistory) {
        categoryIdsList.add(element.id);
      }
    }
    List<int> servicesIdsList = [];
    if (branchFilterServicesSearchHistory.isNotEmpty) {
      for (var element in branchFilterServicesSearchHistory) {
        servicesIdsList.add(element.id);
      }
    }

    nearbyBranches = await branchRepo.getBranchesByFilters(
      providerIdsList: providersIdsList.isNotEmpty ? providersIdsList : null,
      categoryIdsList: categoryIdsList.isNotEmpty ? categoryIdsList : null,
      serviceIdsList: servicesIdsList.isNotEmpty ? servicesIdsList : null,
      distanceKm: branchFilterCurrentDistance.toInt(),
      rating: branchFilterByRating.toInt(),
      latitude: AppState().currentLocation.latitude,
      longitude: AppState().currentLocation.longitude,
    );
    setState(ViewState.idle);
  }

  Future<void> getBranchesBasedOnCategoryFilters({required int categoryId}) async {
    setState(ViewState.busy);
    if (categoryId == 0) {
      await getAllNearBranches();
      return;
    }
    nearbyBranches.clear();
    nearbyBranches = await branchRepo.getBranchesByFilters(
      categoryIdsList: [categoryId],
      latitude: AppState().currentLocation.latitude,
      longitude: AppState().currentLocation.longitude,
    );
    setState(ViewState.idle);
  }

// Appointments Filter Screen

  List<DropValue> branchesDropList = [];

  Future<void> fetchAllBranchesBySelectedProviderId({required List<int> providersIdsList}) async {
    branchesDropList.clear();
    setOnlyState(ViewState.busy);
    List<BranchDetailModel> providers = await branchRepo.getBranchesByFilters(
      providerIdsList: providersIdsList,
      latitude: AppState().currentLocation.latitude,
      longitude: AppState().currentLocation.longitude,
    );
    for (var element in providers) {
      branchesDropList.add(DropValue(element.id ?? 0, element.branchName ?? "N/A", ""));
    }
    setState(ViewState.idle);

    log("branchesDropList: ${branchesDropList.length}");
  }

  Future<void> populateDataForAppointmentsFilter() async {
    await fetchAllProviders();
    await fetchAllCategories();
    await fetchAllServices();
    await fetchAllItems();
  }

  int appointmentFiltersCounter = 0;

  updateAppointmentFiltersCounter(int value) {
    appointmentFiltersCounter = value;
    notifyListeners();
  }

// Provider Filter For Appointments
  List<DropValue> appointmentFilterProviderSearchHistory = [];

  void removeAppointmentFilterProviderSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      appointmentFilterProviderSearchHistory.clear();
      notifyListeners();
      return;
    }
    appointmentFilterProviderSearchHistory.removeAt(index);
    if (appointmentFilterProviderSearchHistory.isNotEmpty) {
      List<int> providerIdsSelected = [];
      for (var element in appointmentFilterProviderSearchHistory) {
        providerIdsSelected.add(element.id);
      }
      fetchAllBranchesBySelectedProviderId(providersIdsList: providerIdsSelected);
    }

    if (appointmentFilterProviderSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
      // appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addAppointmentFilterProviderSearchHistory({required DropValue value}) {
    if (appointmentFilterProviderSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
    }
    List<int> providerIdsSelected = [];
    for (var element in appointmentFilterProviderSearchHistory) {
      providerIdsSelected.add(element.id);
    }
    fetchAllBranchesBySelectedProviderId(providersIdsList: providerIdsSelected);
    appointmentFilterProviderSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateAppointmentFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue providerDrop = providersDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: appointmentFilterProviderSearchHistory, value: providerDrop)) {
        addAppointmentFilterProviderSearchHistory(value: providerDrop);
      }
    }
    // appointmentFilterSelectedProviderId = id;
    notifyListeners();
  }

// Branches Filter For Appointments

  List<DropValue> appointmentFilterBranchSearchHistory = [];

  void removeAppointmentFilterBranchSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      appointmentFilterBranchSearchHistory.clear();
      notifyListeners();
      return;
    }
    appointmentFilterBranchSearchHistory.removeAt(index);
    if (appointmentFilterBranchSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
      // appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addAppointmentFilterBranchSearchHistory({required DropValue value}) {
    if (appointmentFilterBranchSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
    }
    appointmentFilterBranchSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateAppointmentFilterSelectedBranchId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue branchesDrop = branchesDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: appointmentFilterBranchSearchHistory, value: branchesDrop)) {
        addAppointmentFilterBranchSearchHistory(value: branchesDrop);
      }
    }
    // appointmentFilterSelectedBranchId = id;
    notifyListeners();
  }

// Category Filter For Appointments

  List<DropValue> appointmentFilterCategorySearchHistory = [];

  void removeAppointmentFilterCategorySearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      appointmentFilterCategorySearchHistory.clear();
      notifyListeners();
      return;
    }
    appointmentFilterCategorySearchHistory.removeAt(index);
    if (appointmentFilterCategorySearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
      // appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addAppointmentFilterCategorySearchHistory({required DropValue value}) {
    if (appointmentFilterCategorySearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
    }
    appointmentFilterCategorySearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateAppointmentFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue categoryDrop = categoryDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: appointmentFilterCategorySearchHistory, value: categoryDrop)) {
        addAppointmentFilterCategorySearchHistory(value: categoryDrop);
      }
    }
    // appointmentFilterSelectedCategoryId = id;
    notifyListeners();
  }

  List<DropValue> appointmentFilterServicesSearchHistory = [];

  void removeAppointmentFilterServicesSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      appointmentFilterServicesSearchHistory.clear();
      notifyListeners();
      return;
    }
    appointmentFilterServicesSearchHistory.removeAt(index);
    if (appointmentFilterServicesSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
      // appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addAppointmentFilterServicesSearchHistory({required DropValue value}) {
    if (appointmentFilterServicesSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
    }
    appointmentFilterServicesSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateAppointmentFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue servicesDrop = servicesDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: appointmentFilterServicesSearchHistory, value: servicesDrop)) {
        addAppointmentFilterServicesSearchHistory(value: servicesDrop);
      }
    }
    // appointmentFilterSelectedServiceId = id;
    notifyListeners();
  }

  List<DropValue> appointmentFilterItemsSearchHistory = [];

  void removeAppointmentFilterItemsSearchHistory({bool isClear = false, required int index}) {
    if (isClear) {
      appointmentFilterItemsSearchHistory.clear();
      notifyListeners();
      return;
    }
    appointmentFilterItemsSearchHistory.removeAt(index);
    if (appointmentFilterItemsSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
      // appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    }
    notifyListeners();
  }

  void addAppointmentFilterItemsSearchHistory({required DropValue value}) {
    if (appointmentFilterItemsSearchHistory.isEmpty) {
      updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
    }
    appointmentFilterItemsSearchHistory.add(value);
    notifyListeners();
  }

  SelectionModel appointmentFilterSelectedItemId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");

  void updateAppointmentFilterSelectedItemId(SelectionModel id, {bool isForSearch = false}) async {
    if (isForSearch) {
      DropValue itemsDrop = itemsDropList.firstWhere((element) => element.id == id.selectedId);
      if (!ifAlreadyExist(list: appointmentFilterItemsSearchHistory, value: itemsDrop)) {
        addAppointmentFilterItemsSearchHistory(value: itemsDrop);
      }
    }
    // appointmentFilterSelectedServiceId = id;
    notifyListeners();
  }

  void clearAppointmentFilterSelections() {
    appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    appointmentFilterSelectedItemId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
    appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
  }

  void clearAppointmentFilters() {
    appointmentFilterItemsSearchHistory.clear();
    appointmentFilterServicesSearchHistory.clear();
    appointmentFilterCategorySearchHistory.clear();
    appointmentFilterProviderSearchHistory.clear();
    appointmentFilterBranchSearchHistory.clear();
    appointmentFiltersCounter = 0;
    clearAppointmentFilterSelections();
    getMyAppointmentsForCustomer();
    notifyListeners();
  }

  Future<void> getAppointmentsBasedOnFilters() async {
    setState(ViewState.busy);
    List<String> providersIdsList = [];
    if (appointmentFilterProviderSearchHistory.isNotEmpty) {
      for (var element in appointmentFilterProviderSearchHistory) {
        providersIdsList.add(element.id.toString());
      }
    }
    List<String> categoryIdsList = [];
    if (appointmentFilterCategorySearchHistory.isNotEmpty) {
      for (var element in appointmentFilterCategorySearchHistory) {
        categoryIdsList.add(element.id.toString());
      }
    }
    List<String> servicesIdsList = [];
    if (appointmentFilterServicesSearchHistory.isNotEmpty) {
      for (var element in appointmentFilterServicesSearchHistory) {
        servicesIdsList.add(element.id.toString());
      }
    }
    List<String> itemIdsList = [];
    if (appointmentFilterItemsSearchHistory.isNotEmpty) {
      for (var element in appointmentFilterItemsSearchHistory) {
        itemIdsList.add(element.id.toString());
      }
    }
    List<String> branchesIdsList = [];
    if (appointmentFilterBranchSearchHistory.isNotEmpty) {
      for (var element in appointmentFilterBranchSearchHistory) {
        branchesIdsList.add(element.id.toString());
      }
    }

    myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(
      providerIdsList: providersIdsList,
      categoryIdsList: categoryIdsList,
      serviceIdsList: servicesIdsList,
      itemIdsList: itemIdsList,
      branchIdsList: branchesIdsList,
    );
    applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
    setState(ViewState.idle);
  }

  Future<bool> addProviderToFavorite({required int serviceProviderID, required BuildContext context}) async {
    Utils.showLoading(context);
    try {
      GenericRespModel genericRespModel = await branchRepo.addProviderToFavourite(providerID: serviceProviderID);
      Utils.showToast(genericRespModel.message.toString());
      Utils.hideLoading(context);
      return genericRespModel.messageStatus == 1;
    } catch (e, s) {
      logger.e(s);
      Utils.hideLoading(context);
      Utils.showToast(e.toString());
      return false;
    }
  }

  Future<bool> removeProviderFromFavorite({required int serviceProviderID, required BuildContext context}) async {
    Utils.showLoading(context);

    try {
      GenericRespModel genericRespModel = await branchRepo.removeProviderFromFavourite(providerID: serviceProviderID);
      Utils.hideLoading(context);
      Utils.showToast(genericRespModel.message.toString());
      return genericRespModel.messageStatus == 1;
    } catch (e, s) {
      logger.e(s);
      Utils.hideLoading(context);
      Utils.showToast(e.toString());
      return false;
    }
  }

  List<ProviderProfileModel> myFavProvidersList = [];

  Future<void> getMyFavoriteProviders() async {
    myFavProvidersList.clear();
    setState(ViewState.busy);
    try {
      myFavProvidersList = await branchRepo.getMyFavoriteProviders();
      setState(ViewState.idle);
    } catch (e, s) {
      logger.e(s);
      setState(ViewState.idle);
      Utils.showToast(e.toString());
    }
  }

  List<BranchRatingModel> currentBranchReviews = [];
  bool isReadMoreEnabled = false;

  void resetCurrentBranchRatings() {
    currentBranchReviews.clear();
    isReadMoreEnabled = false;
  }

  updateIsReadMoreEnabled(var value) {
    isReadMoreEnabled = value;
    notifyListeners();
  }

  Future<void> getReviewsByBranchId({required int serviceProviderBranchID}) async {
    setState(ViewState.busy);
    try {
      currentBranchReviews = await branchRepo.getBranchRatings(serviceProviderBranchID: serviceProviderBranchID);
    } catch (e, s) {
      logger.e(s);
      setState(ViewState.idle);
      Utils.showToast(e.toString());
    }
    setState(ViewState.idle);
  }

  // Chat Related

  Future<void> getGeneralChatsListForProvider({required BuildContext context}) async {
    try {
      // Utils.showLoading(context);
      // List<BuyersChatForAdzsModel> buyersChatList = await appointmentRepo.getChatBuyersForAds(adsID: adsID);
      // Utils.hideLoading(context);
      // buyersChatListForAds.clear();
      // buyersChatListForAds = buyersChatList;
      notifyListeners();
    } catch (e) {
      logger.i(e.toString());
      Utils.showToast(e.toString());
      Utils.hideLoading(context);
    }
  }

  void onGeneralChatMessagesButtonPressed({required BuildContext context}) async {
    final myUserID = AppState().getUser.data!.userInfo!.userId;
    // if (adDetailsModel.isMyAd != null && adDetailsModel.isMyAd!) {
    //   await getGeneralChatsListForProvider(context: context);
    //   // navigateWithName(context, AppRoutes.generalChatsListForProvider, arguments: buyersChatListForAds);
    // } else {
    //   ChatViewArgumentsForAd chatViewArgumentsForAd = ChatViewArgumentsForAd(receiverUserID: adDetailsModel.userID, adsID: adDetailsModel.id);
    //   ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.ads, chatViewArgumentsForAd: chatViewArgumentsForAd);
    //   final chatVM = context.read<ChatVM>();
    //   await chatVM
    //       .getUsersChatMessagesForAd(
    //         context: context,
    //         isForBuyer: true,
    //         adsChatBuyerId: 1,
    //         adID: adDetailsModel.id,
    //         userID: myUserID,
    //         senderName: adDetailsModel.adOwnerName,
    //       )
    //       .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
    // }
  }
}
