package com.app.simplitend.caregiverdashboard.mvvm;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;

import com.app.simplitend.articles.ArticleContracts;
import com.app.simplitend.articles.ArticleResult;
import com.app.simplitend.caregiverdashboard.mvvm.models.GeoFenceDetails;
import com.app.simplitend.patientprofile.medreminder.mvvm.models.NearestActivity;
import com.app.simplitend.patientprofile.medreminder.mvvm.models.NearestReminder;
import com.app.simplitend.patientprofile.medreminder.mvvm.models.ReminderResult;
import com.app.simplitend.patientprofile.setuproutine.mvvm.RoutineDetails;
import com.yarolegovich.slidingrootnav.SlidingRootNav;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.MultipartBody;
import okhttp3.RequestBody;

public class CaregiverMainViewModel extends ViewModel {

    public SlidingRootNav slidingRootNav;

    private final CgHomeRepository cgHomeRepository;

    private ArticleResult default_article;

    public String ongoingActivityText, upcomingActivityText;
    public String upcomingReminderText, dailyReminderText;

    public static List<ReminderResult> remindersList;
    public static List<RoutineDetails> activityList;

    public CaregiverMainViewModel(){
        this.cgHomeRepository = CgHomeRepository.getHomeRepository();
        this.ongoingActivityText = this.upcomingActivityText =
                this.upcomingReminderText = this.dailyReminderText = "Loading...";
    }

    public void setDefault_article(ArticleResult default_article) {
        this.default_article = default_article;
    }

    public void getArticles(Context context, @NonNull ArticleContracts.GetArticleCallback articleCallback){
        if (default_article == null){
            cgHomeRepository.getDefaultArticle(context, articleCallback);
        }else{
            ArrayList<ArticleResult> articleResults = new ArrayList<>();
            articleResults.add(default_article);
            articleCallback.onArticlesFetched(articleResults);
        }

    }

    public void saveGeoFenceDetails(@NonNull GeoFenceDetails geoFenceDetails,
                                    @NonNull String token,
                                    @NonNull CgHomeContracts.SaveGeoFenceCallback geoFenceCallback){
        cgHomeRepository.saveGeoFenceDetails(geoFenceDetails, token, geoFenceCallback);
    }

    public void getGeoFenceDetails(String p_id,
                                   String cg_xid,
                                   @NonNull String token,
                                   @NonNull CgHomeContracts.GetGeoFenceCallback getGeoFenceCallback){
        cgHomeRepository.getGeoFenceDetails(p_id, cg_xid, token, getGeoFenceCallback);
    }

    public void updateCaregiverData(int cg_xid,
                                    Map<String, RequestBody> body,
                                    MultipartBody.Part photo,
                                    @NonNull String token,
                                    @NonNull CgHomeContracts.UpdateCaregiverDataCallback updateCaregiverDataCallback){
        cgHomeRepository.updateCaregiverData(cg_xid, body, photo, token, updateCaregiverDataCallback);
    }

    public void updatePatientData(int patient_xid,
                                  Map<String, RequestBody> body,
                                  MultipartBody.Part photo,
                                  String token,
                                  @NonNull CgHomeContracts.UpdatePatientDataCallback patientDataCallback){
        cgHomeRepository.updatePatientData(patient_xid, body, photo, token, patientDataCallback);
    }

    public void updatePatientAddress(int pat_id,
                                     Map<String, String> body,
                                     @NonNull String token,
                                     @NonNull CgHomeContracts.UpdatePatientAddressCallback patientAddressCallback){
        cgHomeRepository.updatePatientAddress(pat_id, body, token, patientAddressCallback);
    }

    public void updateCgPassword(int cg_xid,
                                 Map<String, RequestBody> body,
                                 @NonNull String token,
                                 @NonNull CgHomeContracts.UpdateCgPasswordCallback passwordCallback){
        cgHomeRepository.updateCgPassword(cg_xid, body, token, passwordCallback);
    }

    public void signOut(String token, @NonNull CgHomeContracts.SignOutCallback signOutCallback){
        cgHomeRepository.signOut(token, signOutCallback);
    }

    public synchronized void getNearestActivity(List<RoutineDetails> activityList,
                                                @NonNull GetNearestActivityCallback activityCallback){
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Handler handler = new Handler(Looper.getMainLooper());

        executor.execute(() -> {
            // Background work

            NearestActivity nearestActivity = new NearestActivity();

            try {

                for (RoutineDetails activity: activityList){
                    SimpleDateFormat input_sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

                    Date start_date = input_sdf.parse(activity.routine_start_time);
                    Date end_date = input_sdf.parse(activity.routine_end_time);

                    if (start_date == null || end_date == null) throw new Exception();

                    Calendar cur_cal = Calendar.getInstance();

                    // matching date and seconds of the current date and routine dates
                    Date cur_date = new Date(start_date.getYear(), start_date.getMonth(), start_date.getDate(), cur_cal.get(Calendar.HOUR_OF_DAY), cur_cal.get(Calendar.MINUTE), 0);
                    start_date.setSeconds(0);
                    end_date.setSeconds(0);

                    // checking on going activity
                    if (cur_date.getTime() >= start_date.getTime() &&
                        cur_date.getTime() <= end_date.getTime()){
                        // on going activity
                        nearestActivity.ongoing_activity_name = activity.routine_title;
                        nearestActivity.ongoing_start_time = activity.routine_start_time;
                        nearestActivity.ongoing_end_time = activity.routine_end_time;
                    }

                    // checking upcoming activity
                    if (start_date.getTime() > cur_date.getTime() && smallestTime(activity.routine_start_time, nearestActivity.upcoming_time)){
                        // upcoming_time for this reminder is less than nearestReminder.upcoming_time
                        // thus, updating nearest reminder

                        nearestActivity.upcoming_time = activity.routine_start_time;
                        nearestActivity.activity_id = activity.id;
                        nearestActivity.upcoming_activity_name = activity.routine_title;
                    }

                }

            }catch (Exception e){
                // do nothing
            }

            handler.post(() -> activityCallback.nearestActivity(nearestActivity));
        });
    }

    public synchronized void getNearestReminder(List<ReminderResult> reminderResultList,
                                   @NonNull GetNearestResultCallback nearestResultCallback){

        ExecutorService executor = Executors.newSingleThreadExecutor();
        Handler handler = new Handler(Looper.getMainLooper());

        executor.execute(() -> {
            // background thread work

            NearestReminder nearestReminder = new NearestReminder();

            for (ReminderResult reminder: reminderResultList){
                if (!alreadyPassed(reminder.medicine_time)){
                    nearestReminder.upcoming_time = reminder.medicine_time;
                    nearestReminder.reminder_id = reminder.id;
                    nearestReminder.medication_name = reminder.medicine_name;
                    break;
                }
            }

            // callback through main thread
            handler.post(() -> nearestResultCallback.nearestReminder(nearestReminder));
        });
    }

    // check if the time has passed current time or not
    private boolean alreadyPassed(String time1) {
        SimpleDateFormat input_sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

        try {
            Date date = input_sdf.parse(time1);
            if (date == null) throw new Exception();

            Calendar cur_cal = Calendar.getInstance();
            Date cur_date = new Date(date.getYear(), date.getMonth(), date.getDate(), cur_cal.get(Calendar.HOUR_OF_DAY), cur_cal.get(Calendar.MINUTE), date.getSeconds());

            return date.getTime() <= cur_date.getTime();
        } catch (Exception e) {
            return false;
        }
    }

    // returns true if time1 is less than time2;
    private boolean smallestTime(@NonNull String time1, String time2) {
        if (time2 == null) return true;

        // comparing this 2 times with current time
        // first converting time string to calendar
        SimpleDateFormat input_sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

        try {
            Date date1 = input_sdf.parse(time1);
            Date date2 = input_sdf.parse(time2);

            if (date2 == null || date1 == null) throw new Exception();

            // All times above dates are of same date 01 Jan 1970
            // but, of different times i.e. hh:mm:ss

            long t1 = date1.getTime();
            long t2 = date2.getTime();

            return t1 < t2;
        }catch (Exception e){
            // if something goes wrong, returning the time1
            return true;
        }
    }

    @FunctionalInterface
    public interface GetNearestResultCallback{
        void nearestReminder(NearestReminder nearestReminder);
    }

    @FunctionalInterface
    public interface GetNearestActivityCallback{
        void nearestActivity(NearestActivity nearestActivity);
    }

}
