package com.app.simplitend.patient_dashboard.fragments;

import static com.app.simplitend.appblocking.FUAActivity.IS_FROM_DASHBOARD;
import static com.app.simplitend.apputils.NotificationService.CONTENT_TYPE_KEY;
import static com.app.simplitend.caregiverdashboard.activities.EditProfileInfoActivity.IS_CAREGIVER;
import static com.app.simplitend.patient_dashboard.DirectionToHomeActivity.LAT_KEY;
import static com.app.simplitend.patient_dashboard.DirectionToHomeActivity.LNG_KEY;
import static com.app.simplitend.patient_dashboard.NotificationsActivity.USER_ID;
import static com.app.simplitend.patient_dashboard.NotificationsActivity.USER_TOKEN;
import static com.app.simplitend.patient_dashboard.fragments.CallsActivity.CALL_CONTACT_LIST_KEY;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;

import com.app.simplitend.R;
import com.app.simplitend.appblocking.FUAActivity;
import com.app.simplitend.apputils.AppUtil;
import com.app.simplitend.apputils.Constants;
import com.app.simplitend.apputils.PatientDataCache;
import com.app.simplitend.apputils.RetrofitHelper;
import com.app.simplitend.caregiverdashboard.mvvm.CaregiverMainViewModel;
import com.app.simplitend.chats.ChatFragment;
import com.app.simplitend.databinding.PatientDashboardFragmentBinding;
import com.app.simplitend.patient_dashboard.DirectionToHomeActivity;
import com.app.simplitend.patient_dashboard.NotificationsActivity;
import com.app.simplitend.patient_dashboard.PatientMainViewModel;
import com.app.simplitend.patientprofile.ProfileContracts;
import com.app.simplitend.patientprofile.medreminder.mvvm.ReminderViewModel;
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.app.simplitend.patientprofile.setuproutine.mvvm.RoutineViewModel;
import com.app.simplitend.welcome.welcomepatient.fragments.contacts.mvvm.models.ContactData;
import com.app.simplitend.welcome.welcomepatient.mvvm.models.PatientData;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class PatientDashboardFragment extends Fragment implements ProfileContracts.GetRemindersListCallback, CaregiverMainViewModel.GetNearestResultCallback, ProfileContracts.GetRoutinesCallback, CaregiverMainViewModel.GetNearestActivityCallback {

    // view binding
    protected PatientDashboardFragmentBinding binding;

    private ReminderViewModel reminderViewModel;
    private RoutineViewModel routineViewModel;

    private PatientData patientData;

    private ArrayList<ContactData> contactList;

    private PatientMainViewModel viewModel;

    private BroadcastReceiver notification_receiver;

    // date suffixes
    String[] suffixes =
            //    0     1     2     3     4     5     6     7     8     9
            {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
                    //    10    11    12    13    14    15    16    17    18    19
                    "th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
                    //    20    21    22    23    24    25    26    27    28    29
                    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
                    //    30    31
                    "th", "st"};

    // ticker broadcast receiver that is called every one minute passes the system clock
    private final BroadcastReceiver timeTickReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) return;

            if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
                updateTime();
            }
        }
    };

    public PatientDashboardFragment() {
        // required
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = PatientDashboardFragmentBinding.inflate(inflater, container, false);

        viewModel = new ViewModelProvider(requireActivity()).get(PatientMainViewModel.class);
        reminderViewModel = new ViewModelProvider(requireActivity()).get(ReminderViewModel.class);
        routineViewModel = new ViewModelProvider(requireActivity()).get(RoutineViewModel.class);

        initViews();

        clickEvents();

        loadReminders();

        loadActivities();

        notification_receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String content_type = intent.getStringExtra(CONTENT_TYPE_KEY);

                if (Constants.ACTIVITY_TIME.equals(content_type)){
                    loadActivities();
                }else if (Constants.MEDICINE_TIME.equals(content_type)){
                    loadReminders();
                }
            }
        };

        requireContext().registerReceiver(notification_receiver, new IntentFilter(AppUtil.NOTIFICATION_ACTION));

        PatientDataCache.getPatientData(requireContext(), (patientData -> {
            this.patientData = patientData;

            setDetails();

            // updating chats credentials to send message when geofence is triggered when patient is out of geofence
            // to send message to patient on behalf of caregiver

            if (patientData != null && patientData.link_id == null){
                PatientDataCache.setPatientData(null); // to load new fresh data
            }

            try {
                PatientDataCache.getPatientData(requireContext(), (patientData1 -> {
                    try {
                        if (patientData != null){
                            int cg_id = Integer.parseInt(patientData.caregiverId);
                            int channel_id = Integer.parseInt(patientData.link_id);

                            AppUtil.updatePatientGeofenceChatsCred(
                                    requireContext(),
                                    cg_id,
                                    channel_id,
                                    patientData.id
                            );
                        }

                    }catch (Exception e){
                        // do nothing
                    }
                }), false);
            } catch (Exception e) {
                // do nothing
            }

        }), true);

        PatientDataCache.setContactList(null); // to load new contact list
        PatientDataCache.getContactList(requireContext(), "Bearer " + AppUtil.getPatientToken(requireContext()),
                (contactList1 -> {
                    this.contactList = contactList1;

                    if (contactList != null) {
                        AppUtil.setWhiteListedContacts(requireContext(), contactList);
                    }
                }), false);

        return binding.getRoot();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        requireContext().unregisterReceiver(notification_receiver);
    }

    private void setDetails() {
        if (patientData == null) return;

        if (patientData.profile_photo != null) {
            Glide.with(requireContext())
                    .load(RetrofitHelper.IMAGE_BASE_URL + patientData.profile_photo)
                    .diskCacheStrategy(DiskCacheStrategy.DATA)
                    .fitCenter()
                    .placeholder(android.R.color.darker_gray)
                    .error(R.drawable.senior_img)
                    .into(binding.profile);
        }

        if (patientData.caregiver_name != null){
            binding.chatWith.setText("Chat with " + patientData.caregiver_name);
        }
    }

    private void initViews() {

    }

    @Override
    public void onResume() {
        super.onResume();
        updateTime();
        Activity activity = getActivity();
        if (activity != null) {
            activity.registerReceiver(timeTickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        Activity activity = getActivity();
        if (activity != null) {
            activity.unregisterReceiver(timeTickReceiver);
        }
    }

    private void clickEvents() {
        binding.refreshBtn.setOnClickListener(v -> {
            showLoadingProgress(true);

            viewModel.ongoingActivityText = getString(R.string.loading);
            viewModel.upcomingActivityText = getString(R.string.loading);
            viewModel.upcomingReminderText = getString(R.string.loading);
            viewModel.dailyReminderText = getString(R.string.loading);

            binding.activityMessage.setText(viewModel.upcomingActivityText);
            binding.reminderMessage.setText(viewModel.upcomingReminderText);

            loadReminders();
            loadActivities();
        });

        binding.chats.setOnClickListener(v -> {
            if (patientData.link_id == null){
                PatientDataCache.setPatientData(null); // to fetch new data with link id
            }

            PatientDataCache.getPatientData(requireContext(), (patientData1 -> {
                if (patientData1 != null){
                    this.patientData = patientData1;

                    try {
                        String sender_image;
                        if (patientData1.profile_photo == null){
                            sender_image = String.valueOf(R.drawable.senior_img);
                        }else{
                            sender_image = RetrofitHelper.IMAGE_BASE_URL + patientData.profile_photo;
                        }

                        String receiver_image;
                        if (patientData.caregiver_profile_photo == null){
                            receiver_image = String.valueOf(R.drawable.caregiver_img);
                        }else{
                            receiver_image = RetrofitHelper.IMAGE_BASE_URL + patientData.caregiver_profile_photo;
                        }

                        getParentFragmentManager().beginTransaction()
                                .replace(R.id.fragmentContainerView, new ChatFragment(
                                        patientData1.patientId + "",
                                        patientData1.caregiverId,
                                        patientData1.link_id,
                                        patientData1.caregiver_iamprincipal_id,
                                        patientData1.first_name,
                                        patientData.caregiver_name,
                                        sender_image,
                                        receiver_image
                                ), "patient_chat")
                                .addToBackStack("patient_chat")
                                .commitAllowingStateLoss();
                    } catch (Exception e) {
                        // do nothing
                    }
                }
            }), true);
        });

        binding.sosBtn.setOnClickListener(v -> {
            PatientDataCache.getContactList(requireContext(),
                    "Bearer " + AppUtil.getPatientToken(requireContext()), (contactList1 -> {
                        this.contactList = contactList1;

                        viewModel.notifyRequestedSOS(AppUtil.getPatientUid(requireContext()) + "",
                                AppUtil.getPatientToken(requireContext()));

                        String phone_number = "911";

                        for (ContactData contactData : contactList1) {
                            if (contactData.is_sos.equals("1")) {
                                phone_number = contactData.phone_number;
                            }
                        }

                        AppUtil.callPhone(requireActivity(), phone_number);

                    }), true);
        });

        binding.closeReminder.setOnClickListener(v -> removeReminder());

        binding.calls.setOnClickListener(v -> {
            PatientDataCache.getContactList(requireContext(),
                    "Bearer " + AppUtil.getPatientToken(requireContext()), (contactList1 -> {
                        this.contactList = contactList1;

                        if (contactList != null) {
                            Intent intent = new Intent(requireActivity(), CallsActivity.class);
                            intent.putExtra(CALL_CONTACT_LIST_KEY, contactList);
                            startActivity(intent);

                        } else {
                            Toast.makeText(requireContext(), "Couldn't load contact list", Toast.LENGTH_SHORT).show();
                        }
                    }), true);
        });

        binding.profile.setOnClickListener(v -> {
            Navigation.findNavController(v).navigate(R.id.action_CPDashboardFragment_to_patientProfileInfoFragment);
        });

        binding.apps.setOnClickListener(v -> {

            if (!Settings.canDrawOverlays(requireContext()) && "xiaomi".equals(Build.MANUFACTURER.toLowerCase(Locale.ROOT))) {
                    final Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
                    intent.setClassName("com.miui.securitycenter",
                            "com.miui.permcenter.permissions.PermissionsEditorActivity");
                    intent.putExtra("extra_pkgname", requireActivity().getPackageName());

                    new AlertDialog.Builder(requireContext())
                            .setTitle("Please Enable the additional permissions")
                            .setMessage("You couldn't use App whitelisting feature unless you turn the following permissions on:\n\n1. Display pop-up windows\n2. Open new windows while running in the background")
                            .setPositiveButton("Go to Settings", (dialog, which) -> startActivity(intent))
                            .setNegativeButton("Cancel", ((dialogInterface, i) -> dialogInterface.dismiss()))
                            .setIcon(android.R.drawable.ic_dialog_info)
                            .show();
            }else{
                Intent intent = new Intent(requireActivity(), FUAActivity.class);
                intent.putExtra(IS_FROM_DASHBOARD, true);
                startActivity(intent);
            }

        });

        binding.directions.setOnClickListener(v -> {
            if (patientData == null) return;

            try {
                Intent intent = new Intent(requireActivity(), DirectionToHomeActivity.class);
                double lat = Double.parseDouble(patientData.lat);
                double lng = Double.parseDouble(patientData.lng);
                intent.putExtra(LAT_KEY, lat);
                intent.putExtra(LNG_KEY, lng);
                startActivity(intent);
            } catch (Exception e) {
                Toast.makeText(requireContext(), "Couldn't load your home location", Toast.LENGTH_SHORT).show();
            }
        });

        binding.notifications.setOnClickListener(v -> {
            PatientDataCache.getPatientData(requireActivity(), (patientData1 -> {
                this.patientData = patientData1;

                if (patientData == null) {
                    Toast.makeText(requireContext(), "Couldn't load notifications", Toast.LENGTH_SHORT).show();
                    return;
                }

                Intent intent = new Intent(requireActivity(), NotificationsActivity.class);
                intent.putExtra(USER_ID, patientData.patientId + "");
                intent.putExtra(USER_TOKEN, AppUtil.getPatientToken(requireContext()));
                intent.putExtra(IS_CAREGIVER, false);
                startActivity(intent);
            }), true);
        });

    }

    private void loadReminders() {
        binding.reminderMessage.setText(viewModel.upcomingReminderText);

        reminderViewModel.getRemindersList(AppUtil.getPatientUid(requireContext()),
                Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1,
                "Bearer " + AppUtil.getPatientToken(requireContext()),
                this);
    }

    private void loadActivities(){
        binding.activityMessage.setText(viewModel.ongoingActivityText);

        routineViewModel.getRoutines(AppUtil.getPatientUid(requireContext()),
                "Bearer " + AppUtil.getPatientToken(requireContext()),
                Calendar.getInstance().get(Calendar.DAY_OF_WEEK)-1,
                this);
    }

    private void showLoadingProgress(boolean isLoading){
        if (isLoading){
            binding.refreshBtn.setVisibility(View.GONE);
            binding.refreshProgress.setVisibility(View.VISIBLE);
        }else{
            binding.refreshBtn.setVisibility(View.VISIBLE);
            binding.refreshProgress.setVisibility(View.GONE);
        }
    }

    private void updateTime() {
        Calendar calendar = Calendar.getInstance();

        SimpleDateFormat sdf = new SimpleDateFormat("hh:mm a", Locale.getDefault());
        String currentTime = sdf.format(calendar.getTime());
        binding.currentTime.setText(currentTime.toUpperCase());

        binding.dayOfWeek.setText(getDayOfWeek(calendar.get(Calendar.DAY_OF_WEEK)));

        SimpleDateFormat date_sdf = new SimpleDateFormat("MMMM d, yyyy", Locale.getDefault());
        String date_str = date_sdf.format(calendar.getTime());

        binding.date.setText(date_str);

    }

    public String getDayOfWeek(int position) {
        switch (position) {
            case 1:
                return "Sunday";
            case 2:
                return "Monday";
            case 3:
                return "Tuesday";
            case 4:
                return "Wednesday";
            case 5:
                return "Thursday";
            case 6:
                return "Friday";
            case 7:
                return "Saturday";
        }

        return "";
    }

    private void removeReminder() {

        // removing card from screen code.
        binding.homeReminder.setVisibility(View.GONE);

        try {
            ViewGroup.LayoutParams layoutParams = binding.bgImg.getLayoutParams();
            layoutParams.height = (int) getResources().getDimension(com.intuit.sdp.R.dimen._140sdp);
            binding.bgImg.setLayoutParams(layoutParams);
        } catch (Exception e) {
            // do nothing
        }
    }

    @Override
    public void onRemindersListFetched(List<ReminderResult> reminderResult) {
        viewModel.remindersList = reminderResult;
        viewModel.getNearestReminder(reminderResult, this);
    }

    @Override
    public void onFetchRemindersListFailed(Throwable t, String message) {
        binding.refreshProgress.setVisibility(View.GONE);
        binding.refreshBtn.setVisibility(View.VISIBLE);

        binding.reminderMessage.setText(R.string.couldnt_load_data);

        viewModel.upcomingReminderText = binding.reminderMessage.getText().toString();
    }

    @Override
    public void nearestReminder(NearestReminder nearestReminder) {
        binding.refreshProgress.setVisibility(View.GONE);
        binding.refreshBtn.setVisibility(View.VISIBLE);

        // next reminder setting
        try {
            if (nearestReminder == null) throw new Exception();

            if (nearestReminder.upcoming_time == null){
                binding.reminderMessage.setText(R.string.all_reminder_done);
                viewModel.upcomingReminderText = binding.reminderMessage.getText().toString();
                return;
            }

            SimpleDateFormat input_sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

            Date upcoming_date = input_sdf.parse(nearestReminder.upcoming_time);
            if (upcoming_date == null) throw new Exception();

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

            long time_diff = upcoming_date.getTime() - cur_date.getTime() + 1000; // +1000 for extra one min to reach that time

            if (time_diff < 0){
                // the nearest time has already passed the current time
                // i.e. all the reminders has been done for current day

                binding.reminderMessage.setText(R.string.all_reminder_done);
            }else{
                time_diff = time_diff/1000; // milliseconds to seconds
                long s = time_diff % 60;
                long m = (time_diff / 60) % 60;
                long h = (time_diff / (60 * 60)) % 24;

                StringBuilder reminder_txt = new StringBuilder(nearestReminder.medication_name);

                SimpleDateFormat format_sdf = new SimpleDateFormat("hh:mm a", Locale.getDefault());

                try {
                    String time = format_sdf.format(upcoming_date);
                    reminder_txt.append(" at ").append(time);
                } catch (Exception e) {
                    reminder_txt.append(" at ").append(nearestReminder.upcoming_time);
                }

                binding.reminderMessage.setText(reminder_txt);
            }

        }catch (Exception e){
            binding.reminderMessage.setText(R.string.couldnt_load_data);
        }

        viewModel.upcomingReminderText = binding.reminderMessage.getText().toString();
    }

    @Override
    public void onRoutinesFetched(List<RoutineDetails> routineList) {
        viewModel.activityList = routineList;
        viewModel.getNearestActivity(routineList, this);
    }

    @Override
    public void onRoutinesFetchedFailed(Throwable t, String message) {
        binding.refreshProgress.setVisibility(View.GONE);
        binding.refreshBtn.setVisibility(View.VISIBLE);

        binding.activityMessage.setText(R.string.couldnt_load_data);

        viewModel.ongoingActivityText = binding.activityMessage.getText().toString();
    }

    @Override
    public void nearestActivity(NearestActivity nearestActivity) {
        binding.refreshProgress.setVisibility(View.GONE);
        binding.refreshBtn.setVisibility(View.VISIBLE);

        // next routine setting
        try {

            if (nearestActivity.upcoming_time == null){
                binding.activityMessage.setText(R.string.no_upcoming_activities);

                viewModel.upcomingActivityText = binding.activityMessage.getText().toString();
                viewModel.ongoingActivityText = binding.activityMessage.getText().toString();
                return;
            }

            SimpleDateFormat input_sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

            Date upcoming_date = input_sdf.parse(nearestActivity.upcoming_time);
            if (upcoming_date == null) throw new Exception();

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

            long time_diff = upcoming_date.getTime() - cur_date.getTime() + 1000; // +1000 for extra one min to reach that time

            if (time_diff < 0){
                // the nearest time has already passed the current time
                // i.e. all the reminders has been done for current day

                binding.activityMessage.setText(R.string.all_activity_done);
            }else{
                time_diff = time_diff/1000; // milliseconds to seconds
                long s = time_diff % 60;
                long m = (time_diff / 60) % 60;
                long h = (time_diff / (60 * 60)) % 24;

                StringBuilder up_activity_txt = new StringBuilder(nearestActivity.upcoming_activity_name);

                SimpleDateFormat format_sdf = new SimpleDateFormat("hh:mm a", Locale.getDefault());

                try {
                    String time = format_sdf.format(upcoming_date);
                    up_activity_txt.append(" at ").append(time);
                } catch (Exception e) {
                    up_activity_txt.append(" at ").append(nearestActivity.upcoming_time);
                }

                binding.activityMessage.setText(up_activity_txt);
            }

        }catch (Exception e){
            binding.activityMessage.setText(R.string.couldnt_load_data);
        }

        viewModel.ongoingActivityText = binding.activityMessage.getText().toString();
    }
}
