package com.app.simplitend.patient_dashboard.foodreminders;

import static android.content.Context.NOTIFICATION_SERVICE;
import static com.app.simplitend.apputils.Constants.MEAL_REMINDER;
import static com.app.simplitend.apputils.NotificationService.CONTENT_TYPE_KEY;
import static com.app.simplitend.apputils.NotificationService.NOTIFICATION_TITLE_KEY;
import static com.app.simplitend.locationupdates.LocationService.FOOD_REMINDER_NOTIFICATION_CHANNEL_ID;
import static com.app.simplitend.patient_dashboard.foodreminders.FoodReminderYesReceiver.WHICH_FOOD_REMINDER;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import com.app.simplitend.R;
import com.app.simplitend.apputils.BottomNotificationActivity;
import com.app.simplitend.patient_dashboard.PatientMainViewModel;

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

public abstract class FoodScheduler {

    private static final String TAG = "FoodScheduler";

    public static final int FOOD_REMINDER_NOTIFICATION_ID = 512;

    enum FOOD_TIME {
        BREAK_FAST, LUNCH, DINNER
    }

    private final static Meal breakFastMeal, lunchMeal, dinnerMeal;

    static {
        breakFastMeal = new Meal(FOOD_TIME.BREAK_FAST, 9, 0);
        lunchMeal = new Meal(FOOD_TIME.LUNCH, 13, 0);
        dinnerMeal = new Meal(FOOD_TIME.DINNER, 20, 0);
    }

    public static void scheduleFoodReminders(Context context) {
        //breakfast
        scheduleAt(context, breakFastMeal.hours, breakFastMeal.minutes, FOOD_TIME.BREAK_FAST);
        //lunch
        scheduleAt(context, lunchMeal.hours, lunchMeal.minutes, FOOD_TIME.LUNCH);
        //dinner
        scheduleAt(context, dinnerMeal.hours, dinnerMeal.minutes, FOOD_TIME.DINNER);
    }

    public static void removeFoodReminders(Context context) {
        WorkManager.getInstance(context).cancelAllWorkByTag(FOOD_TIME.BREAK_FAST.name());
        WorkManager.getInstance(context).cancelAllWorkByTag(FOOD_TIME.LUNCH.name());
        WorkManager.getInstance(context).cancelAllWorkByTag(FOOD_TIME.DINNER.name());
    }

    private static void scheduleAt(Context context, @IntRange(from = 0, to = 23) int hours, @IntRange(from = 0, to = 59) int minutes, FOOD_TIME foodTime) {

        Class<? extends Worker> workClass = null;
        switch (foodTime) {
            case BREAK_FAST:
                workClass = BreakFastWorker.class;
                break;
            case LUNCH:
                workClass = LunchWorker.class;
                break;
            case DINNER:
                workClass = DinnerWorker.class;
                break;
        }

        if (workClass == null) return;

        long nextReminderAt = nextReminderInMilliSeconds(hours, minutes);

        // one shot work and then invoking another work for next day
        OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(workClass)
                .setInitialDelay(nextReminderAt, TimeUnit.MILLISECONDS)
                .addTag(foodTime.name())
                .build();

                WorkManager.getInstance(context).enqueueUniqueWork(
                foodTime.name(),
                ExistingWorkPolicy.REPLACE,
                workRequest
        );

        Log.d(TAG, "Next reminder in MILLISECONDS = " + nextReminderAt);
        Log.d(TAG, "SCHEDULED " + foodTime.name() + " at " + hours + " hrs and " + minutes + " mins");
    }

    private static long nextReminderInMilliSeconds(int hour, int minutes) {
        Calendar currCal = Calendar.getInstance();

        Calendar reminderCal = Calendar.getInstance();
        reminderCal.set(Calendar.HOUR_OF_DAY, hour);
        reminderCal.set(Calendar.MINUTE, minutes);
        reminderCal.set(Calendar.SECOND, 0);
        reminderCal.set(Calendar.MILLISECOND, 0);

        if (reminderCal.before(currCal)) {
            // reminder time has already passed
            // thus, reminder for next day
            reminderCal.add(Calendar.DATE, 1);
        }

        return (reminderCal.getTimeInMillis() - currCal.getTimeInMillis());
    }

    private static void showNotification(Context context, String title, int which) {
        Intent yesIntent = new Intent(context, FoodReminderYesReceiver.class);
        yesIntent.putExtra(WHICH_FOOD_REMINDER, which);

        Intent noIntent = new Intent(context, FoodReminderNotifyingService.class);
        noIntent.putExtra(WHICH_FOOD_REMINDER, which);

        Notification notification = new Notification.Builder(context, FOOD_REMINDER_NOTIFICATION_CHANNEL_ID)
                .setContentTitle(title)
                .setSmallIcon(R.mipmap.ic_launcher_round)
                .addAction(new Notification.Action.Builder(null, "Yes", PendingIntent.getBroadcast(context, which, yesIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT)).build())
                .addAction(new Notification.Action.Builder(null, "No", PendingIntent.getService(context, which, noIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT)).build())
                .setPriority(Notification.PRIORITY_HIGH)
                .build();

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(FOOD_REMINDER_NOTIFICATION_ID + which, notification);
    }

    public static class BreakFastWorker extends Worker {

        private final Context context;

        public BreakFastWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
            this.context = context;
        }

        @NonNull
        @Override
        public Result doWork() {
            scheduleAt(context, breakFastMeal.hours, breakFastMeal.minutes, FOOD_TIME.BREAK_FAST);
            Log.d(TAG, "doWork: BREAK_FAST");
            if (PatientMainViewModel.remindersList == null) {
                // app is not open
                showNotification(context, "Have you done your breakfast?", 0);
            } else {
                Intent intent = new Intent(context, BottomNotificationActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra(CONTENT_TYPE_KEY, MEAL_REMINDER);
                intent.putExtra(WHICH_FOOD_REMINDER, 0);
                intent.putExtra(NOTIFICATION_TITLE_KEY, "Did you have your breakfast?");
                context.startActivity(intent);
            }
            return Result.success();
        }
    }

    public static class LunchWorker extends Worker {

        private final Context context;

        public LunchWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
            this.context = context;
        }

        @NonNull
        @Override
        public Result doWork() {
            scheduleAt(context, lunchMeal.hours, lunchMeal.minutes, FOOD_TIME.LUNCH);
            if (PatientMainViewModel.remindersList == null) {
                // app is not open
                showNotification(context, "Have you done your lunch?", 1);
            } else {
                Intent intent = new Intent(context, BottomNotificationActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra(CONTENT_TYPE_KEY, MEAL_REMINDER);
                intent.putExtra(WHICH_FOOD_REMINDER, 1);
                intent.putExtra(NOTIFICATION_TITLE_KEY, "Did you have your lunch?");
                context.startActivity(intent);
            }
            return Result.success();
        }
    }

    public static class DinnerWorker extends Worker {

        private final Context context;

        public DinnerWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
            this.context = context;
        }

        @NonNull
        @Override
        public Result doWork() {
            scheduleAt(context, dinnerMeal.hours, dinnerMeal.minutes, FOOD_TIME.DINNER);
            if (PatientMainViewModel.remindersList == null) {
                // app is not open
                showNotification(context, "Have you done your dinner?", 2);
            } else {
                Intent intent = new Intent(context, BottomNotificationActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra(CONTENT_TYPE_KEY, MEAL_REMINDER);
                intent.putExtra(WHICH_FOOD_REMINDER, 2);
                intent.putExtra(NOTIFICATION_TITLE_KEY, "Did you have your dinner?");
                context.startActivity(intent);
            }
            return Result.success();
        }
    }

    static class Meal {
        public Meal(FOOD_TIME foodTime, int hours, int minutes) {
            this.foodTime = foodTime;
            this.hours = hours;
            this.minutes = minutes;
        }

        public FOOD_TIME foodTime;
        public int hours, minutes;
    }

}
