import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:location/location.dart' show Location;

class BookAppointmentsViewModel extends ChangeNotifier {
  int selectedTabIndex = 0;

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

  int initialSlotDuration = 0;

  LocationUtils locationUtils;

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

  List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList;

  List<DoctorsListResponseModel> doctorsList = [];

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

  late DoctorsProfileResponseModel doctorsProfileResponseModel;

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

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

  dynamic freeSlotsResponse;

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

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

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

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

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

  void filterClinics(String? query) {
    if (query!.isEmpty) {
      _filteredClinicsList = List.from(clinicsList);
    } else {
      _filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList();
    }
    notifyListeners();
  }

  initBookAppointmentViewModel() {
    _appState = getIt<AppState>();
    isClinicsListLoading = true;
    isDoctorsListLoading = true;
    isDoctorProfileLoading = true;
    clinicsList.clear();
    doctorsList.clear();
    // getLocation();
    notifyListeners();
  }

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

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

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

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

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

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

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

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

  void onTabChanged(int index) {
    selectedTabIndex = index;
    notifyListeners();
  }

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

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

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

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

    result.fold(
      (failure) async {
        onError!("No doctors found for the search criteria".needTranslation);
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          doctorsList = apiResponse.data!;
          isDoctorsListLoading = false;
          initializeFilteredList();
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

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

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

  //TODO: Handle the cases for LiveCare Schedule
  Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    docFreeSlots.clear();
    DateTime date;
    final DateFormat formatter = DateFormat('HH:mm');
    final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
    Map<DateTime, List> _eventsParsed;

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

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

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

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

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

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

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

                  showCommonBottomSheet(navigationService.navigatorKey.currentContext!,
                      child: Utils.getLoadingWidget(loadingText: "Cancelling your previous appointment....".needTranslation),
                      callBackFunc: (str) {},
                      title: "",
                      height: ResponsiveExtension.screenHeight * 0.3,
                      isCloseButtonVisible: false,
                      isDismissible: false,
                      isFullScreen: false);
                  await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
                    navigationService.pop();
                    Future.delayed(Duration(milliseconds: 50)).then((value) async {});
                    LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
                    await insertSpecificAppointment(
                        onError: (err) {},
                        onSuccess: (apiResp) async {
                          LoadingUtils.hideFullScreenLoader();
                          await Future.delayed(Duration(milliseconds: 50)).then((value) async {
                            LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
                            await Future.delayed(Duration(milliseconds: 4000)).then((value) {
                              LoadingUtils.hideFullScreenLoader();
                              Navigator.pushAndRemoveUntil(
                                  navigationService.navigatorKey.currentContext!,
                                  CustomPageRoute(
                                    page: LandingNavigation(),
                                  ),
                                  (r) => false);
                            });
                          });
                        });
                  });
                }),
            callBackFunc: () {},
            isFullScreen: false,
            isCloseButtonVisible: true,
          );
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data == null || apiResponse.data!.isEmpty) {
            onError!("No free slots available".tr());
            return;
          }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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