import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';

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

  MyAppointmentsRepo myAppointmentsRepo;
  ErrorHandlerService errorHandlerService;
  AppState appState;

  int activeAppointmentsCount = 0;

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

  bool isAppointmentDataToBeLoaded = true;

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

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

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

  List<PatientAppointmentHistoryResponseModel> patientTimelineAppointmentsList = [];

  List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];

  PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;

  GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
  bool isTamaraDetailsLoading = false;

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

  void onTabChange(int index) {
    previouslySelectedTab = selectedTabIndex;
    selectedTabIndex = index;
    start = null;
    end = null;
    notifyListeners();
  }

  initAppointmentsViewModel() {
    if (isAppointmentDataToBeLoaded) {
      patientAppointmentsHistoryList.clear();
      patientUpcomingAppointmentsHistoryList.clear();
      patientArrivedAppointmentsHistoryList.clear();
      patientTimelineAppointmentsList.clear();
      isMyAppointmentsLoading = true;
      isTimeLineAppointmentsLoading = true;
      patientMyDoctorsList.clear();
    }
    isTamaraDetailsLoading = true;
    isAppointmentPatientShareLoading = true;
    notifyListeners();
  }

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

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

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

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

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

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

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

  Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
    if (!isAppointmentDataToBeLoaded) return;

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

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

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

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

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

    print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}');
    print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}');
    print('All Appointments: ${patientAppointmentsHistoryList.length}');
    getFiltersForSelectedAppointmentList(filteredAppointmentList);
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    if (!isAppointmentDataToBeLoaded) return;
    isPatientMyDoctorsLoading = true;
    notifyListeners();

    final result = await myAppointmentsRepo.getPatientDoctorsList();

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

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

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

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

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

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

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

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

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

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

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

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

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

        case AppointmentListingFilters.DATESELECTION:

      }
    }
    return false;
  }

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

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

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

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