package me.anon.grow.fragment;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.esotericsoftware.kryo.Kryo;
import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.DayViewDecorator;
import com.prolificinteractive.materialcalendarview.DayViewFacade;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import com.prolificinteractive.materialcalendarview.OnDateSelectedListener;
import com.prolificinteractive.materialcalendarview.spans.DotSpan;

import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneId;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Random;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.anon.controller.adapter.ActionAdapter;
import me.anon.controller.adapter.SimpleItemTouchHelperCallback;
import me.anon.controller.provider.PlantWidgetProvider;
import me.anon.grow.EditWateringActivity;
import me.anon.grow.R;
import me.anon.lib.SnackBar;
import me.anon.lib.SnackBarListener;
import me.anon.lib.Views;
import me.anon.lib.ext.IntUtilsKt;
import me.anon.lib.manager.PlantManager;
import me.anon.model.Action;
import me.anon.model.EmptyAction;
import me.anon.model.NoteAction;
import me.anon.model.Plant;
import me.anon.model.PlantStage;
import me.anon.model.StageChange;
import me.anon.model.Water;
import me.anon.view.ActionHolder;

@Views.Injectable
public class ActionsListFragment extends Fragment implements ActionAdapter.OnActionSelectListener
{
	private ActionAdapter adapter;

	@Views.InjectView(R.id.fab_add) private View fabAdd;
	@Views.InjectView(R.id.recycler_view) private RecyclerView recycler;
	@Views.InjectView(R.id.empty) private View empty;

	private Plant plant;

	private boolean filtered = false;
	private boolean watering = true;
	private boolean notes = true, stages = true;
	private ArrayList<Action.ActionName> selected = new ArrayList<>();
	private CalendarDay selectedFilterDate = null;

	public static final int REQUEST_WATERING = 3;

	public static ActionsListFragment newInstance(Bundle args)
	{
		ActionsListFragment fragment = new ActionsListFragment();
		fragment.setArguments(args);

		return fragment;
	}

	@Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{
		View view = inflater.inflate(R.layout.actions_list_view, container, false);
		Views.inject(this, view);

		return view;
	}

	@Override public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setHasOptionsMenu(true);
	}

	@Override public void onActivityCreated(Bundle savedInstanceState)
	{
		super.onActivityCreated(savedInstanceState);

		getActivity().setTitle(R.string.events_title);

		if (savedInstanceState != null)
		{
			selectedFilterDate = savedInstanceState.getParcelable("selected_filter_date");
			filtered = savedInstanceState.getBoolean("filtered", filtered);
			plant = savedInstanceState.getParcelable("plant");
		}
		else if (getArguments() != null)
		{
			plant = getArguments().getParcelable("plant");
			getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
		}

		if (plant == null)
		{
			getActivity().finish();
			return;
		}

		selected.addAll(new ArrayList<Action.ActionName>(Arrays.asList(Action.ActionName.values())));
		adapter = new ActionAdapter();
		adapter.setOnActionSelectListener(this);
		if (plant.getActions() != null && plant.getActions().size() > 0)
		{
//			calendar.
		}

		adapter.setOnDateChangedListener(new OnDateSelectedListener()
		{
			@Override public void onDateSelected(@NonNull MaterialCalendarView materialCalendarView, @NonNull CalendarDay calendarDay, boolean b)
			{
				selectedFilterDate = calendarDay;
				adapter.setFilterDate(selectedFilterDate);
				adapter.notifyDataSetChanged();
			}
		});

		adapter.showCalendar = filtered;
		adapter.setFilterDate(selectedFilterDate);

		setActions();

		recycler.setLayoutManager(new LinearLayoutManager(getActivity()));
		recycler.setAdapter(adapter);

		ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter)
		{
			@Override public boolean canDropOver(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target)
			{
				return current instanceof ActionHolder && target instanceof ActionHolder;
			}

			@Override public boolean isLongPressDragEnabled()
			{
				return selected.size() == Action.ActionName.values().length && watering && notes && stages && !filtered;
			}
		};
		ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
		touchHelper.attachToRecyclerView(recycler);

		adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
		{
			@Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount)
			{
				if (selected.size() == Action.ActionName.values().length && watering && notes && stages && !filtered)
				{
					ArrayList<Action> actions = new ArrayList<Action>();
					actions.addAll((ArrayList<Action>)adapter.getActions());
					Collections.reverse(actions);

					plant.setActions(actions);
				}
			}
		});
	}

	@Override public void onSaveInstanceState(@NonNull Bundle outState)
	{
		outState.putParcelable("selected_filter_date", selectedFilterDate);
		outState.putBoolean("filtered", filtered);
		outState.putParcelable("plant", plant);

		super.onSaveInstanceState(outState);
	}

	@Override public void onDestroy()
	{
		PlantManager.getInstance().upsert(plant);

		super.onDestroy();
	}

	public void setActions()
	{
		ArrayList<Action> actions = new ArrayList<>();
		actions.addAll(plant.getActions());
		Collections.reverse(actions);
		actions.removeAll(Collections.singleton(null));
		adapter.setActions(plant, actions);

		if (adapter.getItemCount() > 0)
		{
			recycler.setVisibility(View.VISIBLE);
			empty.setVisibility(View.GONE);
		}
		else
		{
			recycler.setVisibility(View.GONE);
			empty.setVisibility(View.VISIBLE);
		}
	}

	@Override public void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		if (resultCode == Activity.RESULT_OK && data.getExtras().containsKey("plant"))
		{
			plant = data.getExtras().getParcelable("plant");
			PlantManager.getInstance().upsert(plant);
			setResult();
		}

		if (requestCode == REQUEST_WATERING)
		{
			setActions();
			adapter.notifyDataSetChanged();
		}

		super.onActivityResult(requestCode, resultCode, data);
	}

	@Views.OnClick public void onFabAddClick(View view)
	{
		ActionDialogFragment dialogFragment = new ActionDialogFragment();
		dialogFragment.setOnActionSelected(new ActionDialogFragment.OnActionSelected()
		{
			@Override public void onActionSelected(final EmptyAction action)
			{
				plant.getActions().add(action);
				PlantManager.getInstance().upsert(plant);
				setResult();

				SnackBar.show(getActivity(), getString(action.getAction().getPrintString()) + " " + getString(R.string.added), getString(R.string.undo), new SnackBarListener()
				{
					@Override public void onSnackBarStarted(Object o){}

					@Override public void onSnackBarFinished(Object o)
					{
						if (getView() != null)
						{
							PlantWidgetProvider.triggerUpdateAll(getView().getContext());
						}
					}

					@Override public void onSnackBarAction(View v)
					{
						plant.getActions().remove(action);
						PlantManager.getInstance().upsert(plant);
						setResult();
					}
				});

				setActions();
				adapter.notifyDataSetChanged();
			}
		});
		dialogFragment.show(getFragmentManager(), null);
	}

	@Override public void onActionDuplicate(Action action)
	{
		action.setDate(action.getDate() + new Random().nextInt(1000));
		plant.getActions().add(action);
		PlantManager.getInstance().upsert(plant);
		setResult();

		setActions();
		adapter.notifyDataSetChanged();

		SnackBar.show(getActivity(), getString(R.string.action_duplicated), getString(R.string.undo), new SnackBarListener()
		{
			@Override public void onSnackBarStarted(Object o){}

			@Override public void onSnackBarFinished(Object o)
			{
				if (getView() != null)
				{
					PlantWidgetProvider.triggerUpdateAll(getView().getContext());
				}
			}

			@Override public void onSnackBarAction(View v)
			{
				plant.getActions().remove(plant.getActions().size() - 1);
				PlantManager.getInstance().upsert(plant);
				setResult();

				setActions();
				adapter.notifyDataSetChanged();
			}
		});
	}

	@Override public void onActionCopy(final Action action)
	{
		PlantSelectDialogFragment dialogFragment = new PlantSelectDialogFragment(true);
		dialogFragment.setDisabled(PlantManager.getInstance().indexOf(plant));
		dialogFragment.setOnDialogActionListener(new PlantSelectDialogFragment.OnDialogActionListener()
		{
			@Override public void onDialogAccept(final ArrayList<Integer> indexes, boolean showImage)
			{
				for (int plantIndex = 0; plantIndex < indexes.size(); plantIndex++)
				{
					PlantManager.getInstance().getPlants().get(indexes.get(plantIndex)).getActions().add(new Kryo().copy(action));
				}

				PlantManager.getInstance().save();
				setActions();
				adapter.notifyDataSetChanged();

				String plantName = getString(R.string.multiple_plants);
				plantName = indexes.size() == 0 ? PlantManager.getInstance().getPlants().get(indexes.get(0)).getName() : plantName;

				SnackBar.show(getActivity(), getString(R.string.added_to) + plantName, getString(R.string.undo), new SnackBarListener()
				{
					@Override public void onSnackBarStarted(Object o){}

					@Override public void onSnackBarFinished(Object o)
					{
						if (getView() != null)
						{
							PlantWidgetProvider.triggerUpdateAll(getView().getContext());
						}
					}

					@Override public void onSnackBarAction(View v)
					{
						for (int plantIndex = 0; plantIndex < indexes.size(); plantIndex++)
						{
							Plant plant = PlantManager.getInstance().getPlants().get(indexes.get(plantIndex));
							plant.getActions().remove(plant.getActions().size() - 1);
						}

						PlantManager.getInstance().save();
						setActions();
						adapter.notifyDataSetChanged();
					}
				});
			}
		});

		dialogFragment.show(getFragmentManager(), null);
	}

	@Override public void onActionEdit(final Action action)
	{
		final int originalIndex = plant.getActions().indexOf(action);

		if (action instanceof Water)
		{
			Intent edit = new Intent(getActivity(), EditWateringActivity.class);
			edit.putExtra("plant_index", PlantManager.getInstance().indexOf(plant));
			edit.putExtra("action_index", originalIndex);
			startActivityForResult(edit, REQUEST_WATERING);
		}
		else if (action instanceof NoteAction)
		{
			NoteDialogFragment dialogFragment = new NoteDialogFragment((NoteAction)action);
			dialogFragment.setOnDialogConfirmed(new NoteDialogFragment.OnDialogConfirmed()
			{
				@Override public void onDialogConfirmed(String notes, Date date)
				{
					final NoteAction noteAction = new NoteAction(date.getTime(), notes);

					plant.getActions().set(originalIndex, noteAction);
					PlantManager.getInstance().upsert(plant);
					setActions();
					adapter.notifyDataSetChanged();

					SnackBar.show(getActivity(), getString(R.string.note_updated), getString(R.string.undo), new SnackBarListener()
					{
						@Override public void onSnackBarStarted(Object o){}

						@Override public void onSnackBarFinished(Object o)
						{
							if (getView() != null)
							{
								PlantWidgetProvider.triggerUpdateAll(getView().getContext());
							}
						}

						@Override public void onSnackBarAction(View v)
						{
							plant.getActions().set(originalIndex, action);
							PlantManager.getInstance().upsert(plant);
							setActions();
							adapter.notifyDataSetChanged();
						}
					});
				}
			});
			dialogFragment.show(getFragmentManager(), null);
		}
		else if (action instanceof EmptyAction)
		{
			ActionDialogFragment dialogFragment = ActionDialogFragment.newInstance((EmptyAction)action);
			dialogFragment.setOnActionSelected(new ActionDialogFragment.OnActionSelected()
			{
				@Override public void onActionSelected(final EmptyAction action)
				{
					plant.getActions().set(originalIndex, action);
					PlantManager.getInstance().upsert(plant);
					setActions();
					adapter.notifyDataSetChanged();

					SnackBar.show(getActivity(), getString(action.getAction().getPrintString()) + " " + getString(R.string.updated), getString(R.string.undo), new SnackBarListener()
					{
						@Override public void onSnackBarStarted(Object o){}
						@Override public void onSnackBarFinished(Object o){}

						@Override public void onSnackBarAction(View v)
						{
							plant.getActions().set(originalIndex, action);
							PlantManager.getInstance().upsert(plant);
							setActions();
							adapter.notifyDataSetChanged();
						}
					});
				}
			});
			dialogFragment.show(getFragmentManager(), null);
		}
		else if (action instanceof StageChange)
		{
			StageDialogFragment dialogFragment = StageDialogFragment.newInstance((StageChange)action);
			dialogFragment.setOnStageUpdated(new StageDialogFragment.OnStageUpdated()
			{
				@Override public void onStageUpdated(final StageChange action)
				{
					if (action.getNewStage() == PlantStage.PLANTED)
					{
						plant.setPlantDate(action.getDate());
					}

					plant.getActions().set(originalIndex, action);
					PlantManager.getInstance().upsert(plant);
					setActions();
					adapter.notifyDataSetChanged();

					SnackBar.show(getActivity(), getString(R.string.stage_updated), getString(R.string.undo), new SnackBarListener()
					{
						@Override public void onSnackBarStarted(Object o){}

						@Override public void onSnackBarFinished(Object o)
						{
							if (getView() != null)
							{
								PlantWidgetProvider.triggerUpdateAll(getView().getContext());
							}
						}

						@Override public void onSnackBarAction(View v)
						{
							plant.getActions().set(originalIndex, action);
							PlantManager.getInstance().upsert(plant);
							setActions();
							adapter.notifyDataSetChanged();
						}
					});
				}
			});
			dialogFragment.show(getFragmentManager(), null);
		}
	}

	@Override public void onActionDeleted(final Action action)
	{
		final int originalIndex = plant.getActions().indexOf(action);
		plant.getActions().remove(action);
		setActions();
		adapter.notifyDataSetChanged();

		setResult();

		SnackBar.show(getActivity(), getString(R.string.event_deleted), getString(R.string.undo), new SnackBarListener()
		{
			@Override public void onSnackBarStarted(Object o){}

			@Override public void onSnackBarFinished(Object o)
			{
				if (getView() != null)
				{
					PlantWidgetProvider.triggerUpdateAll(getView().getContext());
				}
			}

			@Override public void onSnackBarAction(View v)
			{
				plant.getActions().add(originalIndex, action);
				setActions();
				adapter.notifyDataSetChanged();

				setResult();
			}
		});
	}

	private void setResult()
	{
		Intent intent = new Intent();
		intent.putExtra("plant", plant);
		getActivity().setIntent(intent);
		getActivity().setResult(Activity.RESULT_OK, intent);
	}

	@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
	{
		inflater.inflate(R.menu.event_filter_menu, menu);
		super.onCreateOptionsMenu(menu, inflater);
	}

	@Override public boolean onOptionsItemSelected(MenuItem item)
	{
		if (item.isCheckable())
		{
			item.setChecked(!item.isChecked());
		}

		if (item.getItemId() == R.id.menu_calendar)
		{
			adapter.showCalendar = !adapter.showCalendar;
			filtered = adapter.showCalendar;
			selectedFilterDate = null;
			fabAdd.setVisibility(View.VISIBLE);

			if (filtered)
			{
				fabAdd.setVisibility(View.GONE);
				Action lastAction = plant.getActions().get(plant.getActions().size() - 1);
				selectedFilterDate = CalendarDay.from(LocalDate.from(Instant.ofEpochMilli(lastAction.getDate()).atZone(ZoneId.systemDefault())));
			}

			adapter.setFilterDate(selectedFilterDate);
			adapter.notifyDataSetChanged();
		}
		else if (item.getItemId() == R.id.filter_actions)
		{
			CharSequence[] actionItems = new CharSequence[Action.ActionName.values().length];
			boolean[] selectedItems = new boolean[Action.ActionName.values().length];

			for (int index = 0; index < actionItems.length; index++)
			{
				actionItems[index] = getString(Action.ActionName.values()[index].getPrintString());
				selectedItems[index] = selected.contains(Action.ActionName.values()[index]);
			}

			new AlertDialog.Builder(getActivity())
				.setTitle(R.string.filter_actions)
				.setMultiChoiceItems(actionItems, selectedItems, new DialogInterface.OnMultiChoiceClickListener()
				{
					@Override public void onClick(DialogInterface dialog, int which, boolean isChecked)
					{
						if (isChecked)
						{
							selected.add(Action.ActionName.values()[which]);
						}
						else
						{
							selected.remove(Action.ActionName.values()[which]);
						}
					}
				})
				.setPositiveButton(R.string.done, new DialogInterface.OnClickListener()
				{
					@Override public void onClick(DialogInterface dialog, int which)
					{
						filter();
					}
				})
				.show();
		}
		else if (item.getItemId() == R.id.filter_waterings)
		{
			watering = item.isChecked();
		}
		else if (item.getItemId() == R.id.filter_notes)
		{
			notes = item.isChecked();
		}
		else if (item.getItemId() == R.id.filter_stages)
		{
			stages = item.isChecked();
		}

		filter();

		return true;
	}

	private void filter()
	{
		ArrayList<Action> items = new ArrayList<>();
		items.addAll(plant.getActions());
		Collections.reverse(items);

		for (int index = 0; index < items.size(); index++)
		{
			if (items.get(index) instanceof EmptyAction)
			{
				if (!selected.contains(((EmptyAction)items.get(index)).getAction()))
				{
					items.set(index, null);
				}
			}
			else if (!notes && items.get(index) instanceof NoteAction)
			{
				items.set(index, null);
			}
			else if (!stages && items.get(index) instanceof StageChange)
			{
				items.set(index, null);
			}
			else if (!watering && items.get(index) instanceof Water)
			{
				items.set(index, null);
			}
		}

		items.removeAll(Collections.singleton(null));
		adapter.setActions(plant, items);
		adapter.notifyDataSetChanged();
	}
}
