package com.app.simplitend.caregiverdashboard.fragments;

import static com.app.simplitend.apputils.NotificationService.CONTENT_TYPE_KEY;
import static com.app.simplitend.articles.ArticleShowerActivity.ARTICLE_TITLE;
import static com.app.simplitend.articles.ArticleShowerActivity.ARTICLE_URL_KEY;
import static com.app.simplitend.caregiverdashboard.activities.EditProfileInfoActivity.IS_CAREGIVER;
import static com.app.simplitend.cg_geofencing.CgGeoFencingActivity.GEOFENCE_DETAILS_KEY;
import static com.app.simplitend.patient_dashboard.NotificationsActivity.USER_ID;
import static com.app.simplitend.patient_dashboard.NotificationsActivity.USER_TOKEN;

import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
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 com.app.simplitend.R;
import com.app.simplitend.apputils.AppUtil;
import com.app.simplitend.apputils.CaregiverDataCache;
import com.app.simplitend.apputils.Constants;
import com.app.simplitend.apputils.RetrofitHelper;
import com.app.simplitend.articles.ArticleContracts;
import com.app.simplitend.articles.ArticleResult;
import com.app.simplitend.articles.ArticleShowerActivity;
import com.app.simplitend.articles.ArticlesActivity;
import com.app.simplitend.caregiverdashboard.mvvm.CaregiverMainViewModel;
import com.app.simplitend.caregiverdashboard.mvvm.CgHomeContracts;
import com.app.simplitend.caregiverdashboard.mvvm.models.GeoFenceDetails;
import com.app.simplitend.cg_geofencing.CgGeoFencingActivity;
import com.app.simplitend.databinding.CaregiverDashFragmentBinding;
import com.app.simplitend.patient_dashboard.NotificationsActivity;
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.welcomecg.mvvm.CareGiverData;
import com.app.simplitend.welcome.welcomepatient.mvvm.models.PatientData;
import com.bumptech.glide.Glide;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

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.TimeZone;

public class CgDashBoardFragment extends Fragment implements
        OnMapReadyCallback, ArticleContracts.GetArticleCallback, CgHomeContracts.GetGeoFenceCallback, ProfileContracts.GetRemindersListCallback, CaregiverMainViewModel.GetNearestResultCallback, ProfileContracts.GetRoutinesCallback, CaregiverMainViewModel.GetNearestActivityCallback {

    // view binding
    protected CaregiverDashFragmentBinding binding;

    private CaregiverMainViewModel viewModel;
    private ReminderViewModel reminderViewModel;
    private RoutineViewModel routineViewModel;
    private CareGiverData careGiverData;
    private PatientData patientData;
    private ProgressDialog progressDialog;
    private GoogleMap mGoogleMap;
    
    private BroadcastReceiver notification_receiver;

    private boolean gotoGeofenceActivity;

    private static CgDashBoardFragment fragment;

    public CgDashBoardFragment(){
        // required empty
    }

    public synchronized static CgDashBoardFragment getInstance(){
        if (fragment == null){
            fragment = new CgDashBoardFragment();
        }

        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = CaregiverDashFragmentBinding.inflate(inflater, container, false);
        viewModel = new ViewModelProvider(requireActivity()).get(CaregiverMainViewModel.class);
        reminderViewModel = new ViewModelProvider(requireActivity()).get(ReminderViewModel.class);
        routineViewModel = new ViewModelProvider(requireActivity()).get(RoutineViewModel.class);

        progressDialog = new ProgressDialog(requireContext());

        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();
                }
            }
        };

        CaregiverDataCache.getCaregiverData(requireActivity(), (careGiverData1 -> {
            this.careGiverData = careGiverData1;

            loadReminders();
            loadActivities();
            requireContext().registerReceiver(notification_receiver, new IntentFilter(AppUtil.NOTIFICATION_ACTION));
        }), true);

        initViews();

        clickEvents();

        setUpMap();

        loadArticles();

        return binding.getRoot();
    }

    @Override
    public void onResume() {
        super.onResume();
        CaregiverDataCache.getCaregiverData(requireActivity(), (careGiverData1 -> {
            this.careGiverData = careGiverData1;

            setDetails();
        }), true);
    }

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

    private void loadActivities() {
        if (careGiverData == null) return;

        binding.onGoingActivity.setText(viewModel.ongoingActivityText);
        binding.upcomingActivity.setText(viewModel.upcomingActivityText);

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

    private void loadReminders(){
        if (careGiverData == null) return;

        binding.nearestReminder.setText(viewModel.upcomingReminderText);

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

    private void loadArticles() {
        viewModel.getArticles(requireContext(), this);
    }

    private void setDetails() {
        if (careGiverData == null || careGiverData.patientDetails == null){
            Toast.makeText(requireActivity(), "Couldn't load data", Toast.LENGTH_SHORT).show();
            return;
        }

        patientData = careGiverData.patientDetails;

        binding.patientName.setText(patientData.first_name);
    }

    private void setUpMap() {
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager()
                .findFragmentById(R.id.home_map);
        if (mapFragment != null) {
            mapFragment.getMapAsync(this);
        }else{
            Toast.makeText(requireActivity(), "Couldn't load map. Please try again.", Toast.LENGTH_SHORT).show();
        }
    }

    private void initViews() {
        Glide.with(binding.activityGif)
                .asGif()
                .load(R.raw.anim_pulse)
                .into(binding.activityGif);

        Glide.with(binding.upComingGif)
                .asGif()
                .load(R.raw.anim_pulse)
                .into(binding.upComingGif);
    }

    private void clickEvents() {
        binding.viewMoreArticle.setOnClickListener(v -> {
            Intent intent = new Intent(requireActivity(), ArticlesActivity.class);
            startActivity(intent);
        });

        binding.refreshBtn.setOnClickListener(v -> {
            binding.refreshBtn.setVisibility(View.GONE);
            binding.refreshProgress.setVisibility(View.VISIBLE);

            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.upcomingActivity.setText(viewModel.upcomingActivityText);
            binding.onGoingActivity.setText(viewModel.ongoingActivityText);
            binding.nearestReminder.setText(viewModel.upcomingReminderText);

            this.gotoGeofenceActivity = false;
            loadGeofenceDetails();

            loadReminders();
            loadActivities();

            viewModel.setDefault_article(null); // to load new articles
            loadArticles();

            CaregiverDataCache.setCareGiverData(null); // to load new details
            CaregiverDataCache.getCaregiverData(requireActivity(), (careGiverData1 -> {
                if (careGiverData1 != null && careGiverData1.patientDetails != null){
                    this.careGiverData = careGiverData1;

                    setDetails();
                }else {
                    CaregiverDataCache.setCareGiverData(this.careGiverData);
                }
            }), false);
        });

        binding.notifications.setOnClickListener(view -> {
            CaregiverDataCache.getCaregiverData(requireActivity(), (careGiverData1 -> {
                this.careGiverData = careGiverData1;

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

                Intent intent = new Intent(requireActivity(), NotificationsActivity.class);
                intent.putExtra(USER_ID, careGiverData.caregiver_xid + "");
                intent.putExtra(USER_TOKEN, AppUtil.getCgToken(requireContext()));
                intent.putExtra(IS_CAREGIVER, true);
                startActivity(intent);
            }), true);
        });
    }

    public void setArticleDetails(ArticleResult articleResult){
        if (articleResult == null) return;

        Glide.with(binding.articleImg)
                .load(RetrofitHelper.IMAGE_BASE_URL + articleResult.large_image)
                .centerCrop()
                .placeholder(android.R.color.darker_gray)
                .error(android.R.color.darker_gray)
                .into(binding.articleImg);

        if (articleResult.article_title == null){
            articleResult.article_title = "No title";
        }

        binding.articleTitle.setText(articleResult.article_title);

        if (articleResult.article_author == null){
            articleResult.article_author = "No author";
        }

        if (articleResult.article_author_img != null){
            Glide.with(binding.authorImg)
                    .load(RetrofitHelper.IMAGE_BASE_URL + articleResult.article_author_img)
                    .centerCrop()
                    .placeholder(android.R.color.darker_gray)
                    .error(R.drawable.static_3)
                    .into(binding.authorImg);
        }

        binding.articleAuthorName.setText(articleResult.article_author);

        try {
            if (articleResult.created_at == null) throw new Exception();
            // parsing time of created at
            SimpleDateFormat format = new SimpleDateFormat(
                    "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
            format.setTimeZone(TimeZone.getTimeZone("UTC"));

            Date date = format.parse(articleResult.created_at);
            if (date == null) throw new Exception();

            SimpleDateFormat pretty_format = new SimpleDateFormat("dd MMM yy, hh:mm a", Locale.getDefault());
            String time = pretty_format.format(date);

            binding.articleCreatedAt.setText(time);

        }catch (Exception e){
            // do nothing
            String none = "None";
            binding.articleCreatedAt.setText(none);
        }

        binding.articleCard.setOnClickListener(v -> {
            Intent intent = new Intent(requireActivity(), ArticleShowerActivity.class);
            intent.putExtra(ARTICLE_URL_KEY, articleResult.article_link);
            intent.putExtra(ARTICLE_TITLE, articleResult.article_title);
            startActivity(intent);
        });
    }

    @Override
    public void onArticlesFetched(ArrayList<ArticleResult> articleResults) {
        if (articleResults == null) return;

        if (articleResults.size() > 0){
            // saving to view model
            viewModel.setDefault_article(articleResults.get(0));
            binding.articleView.setVisibility(View.VISIBLE);

            setArticleDetails(articleResults.get(0));
        }else{
            // no articles
            binding.articleView.setVisibility(View.GONE);
        }
    }

    @Override
    public void onArticleFetchFailed(Throwable t, String message) {
        // do nothing
        Log.d("fk", "onArticleFetchFailed: ");
    }

    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        this.mGoogleMap = googleMap;

        loadPatientData();

        loadGeofenceDetails();

        googleMap.setOnMapClickListener(latLng1 -> {
            if (careGiverData != null) {
                progressDialog.setTitle("please wait...");
                progressDialog.setMessage("while we load GeoFence.");
                progressDialog.setCancelable(false);
                progressDialog.show();

                this.gotoGeofenceActivity = true;
                loadGeofenceDetails();

            } else {
                Toast.makeText(requireContext(), "Something went wrong", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void loadGeofenceDetails(){
        if (careGiverData == null) return;

        viewModel.getGeoFenceDetails(careGiverData.patientId + "",
                "",
                "Bearer " + AppUtil.getCgToken(requireContext()),
                this);
    }

    private void loadPatientData() {
        LatLng latLng;
        String patient_name;

        // loading patient's location
        try {
            if (patientData == null) throw new Exception();

            double lat = Double.parseDouble(patientData.lat);
            double lng = Double.parseDouble(patientData.lng);

            latLng = new LatLng(lat, lng);
            patient_name = patientData.first_name + "";
        }catch (Exception e){
            // near marine drive
            latLng = new LatLng(18.93294274664527, 72.82806102186441);
            patient_name = "Default location";
        }

        if (mGoogleMap == null) return;

        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));

        MarkerOptions markerOptions = new MarkerOptions()
                .position(latLng)
                .icon(BitmapDescriptorFactory.fromResource(R.drawable.img_home_marker))
                .title(patient_name);

        mGoogleMap.addMarker(markerOptions);
        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));;
    }

    @Override
    public void onGeofenceDetailsFetched(GeoFenceDetails geoFenceDetails) {
        progressDialog.dismiss();
        try {
            if (gotoGeofenceActivity){
                gotoGeofenceActivity = false;
                Intent intent = new Intent(requireActivity(), CgGeoFencingActivity.class);
                intent.putExtra(GEOFENCE_DETAILS_KEY, geoFenceDetails);
                startActivity(intent);
            }else{
                // load into home screen map view
                LatLng pat_cur_latLng;
                try {
                    pat_cur_latLng = new LatLng(Double.parseDouble(geoFenceDetails.patient_radius_location.lat),
                            Double.parseDouble(geoFenceDetails.patient_radius_location.lng));
                } catch (Exception e) {
                    pat_cur_latLng = null;
                }

                if (pat_cur_latLng != null && mGoogleMap != null){
                    mGoogleMap.clear();

                    loadPatientData();

                    MarkerOptions markerOptions = new MarkerOptions()
                            .position(pat_cur_latLng)
                            .icon(BitmapDescriptorFactory.fromResource(R.drawable.img_pat_curr_location));

                    mGoogleMap.addMarker(markerOptions);
                    mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(pat_cur_latLng, 16));;
                }

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

    @Override
    public void onGeofenceDetailsFetchFailed(Throwable throwable, String message) {
        progressDialog.dismiss();

        try {
            if (gotoGeofenceActivity) {
                Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
            }else {
                loadPatientData();
            }
        } catch (Exception e) {
            // do nothing
        }
    }

    @Override
    public void onRemindersListFetched(List<ReminderResult> reminderResult) {
        CaregiverMainViewModel.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.nearestReminder.setText(R.string.couldnt_load_data);
        
        viewModel.upcomingReminderText = binding.nearestReminder.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.nearestReminder.setText(R.string.all_reminder_done);
                viewModel.upcomingReminderText = binding.nearestReminder.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.nearestReminder.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.nearestReminder.setText(reminder_txt);
            }

        }catch (Exception e){
            binding.nearestReminder.setText(R.string.couldnt_load_data);
        }
        
        viewModel.upcomingReminderText = binding.nearestReminder.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.upcomingActivity.setText(R.string.couldnt_load_data);
        binding.onGoingActivity.setText(R.string.couldnt_load_data);
        
        viewModel.ongoingActivityText = binding.onGoingActivity.getText().toString();
        viewModel.upcomingActivityText = binding.upcomingActivity.getText().toString();
    }

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

        if (nearestActivity.ongoing_activity_name != null){
            binding.onGoingActivity.setText(nearestActivity.ongoing_activity_name);
        }else{
            binding.onGoingActivity.setText(R.string.no_ongoing_activity);
        }

        // next routine setting
        try {

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

                viewModel.upcomingActivityText = binding.upcomingActivity.getText().toString();
                viewModel.ongoingActivityText = binding.onGoingActivity.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.upcomingActivity.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.upcomingActivity.setText(up_activity_txt);
            }

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

        viewModel.ongoingActivityText = binding.onGoingActivity.getText().toString();
        viewModel.upcomingActivityText = binding.upcomingActivity.getText().toString();
    }
}
