package net.pokeranalytics.android.ui.fragment

import android.os.Bundle
import android.os.Handler
import android.view.*
import android.view.animation.OvershootInterpolator
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DiffUtil
import kotlinx.android.synthetic.main.fragment_session.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.extensions.SessionState
import net.pokeranalytics.android.model.extensions.getState
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.realm.Location
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.utils.FavoriteSessionFinder
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.helpers.DateTimePickerManager
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.getNextMinuteInMilliseconds
import java.util.*


class SessionFragment : RealmFragment(), RowRepresentableDelegate {

	companion object {
		const val TIMER_DELAY = 60000L

		const val REQUEST_CODE_NEW_CUSTOM_FIELD = 1000
	}

	private lateinit var currentSession: Session
	private lateinit var sessionAdapter: RowRepresentableAdapter

	private var sessionMenu: Menu? = null
	private val oldRows: ArrayList<RowRepresentable> = ArrayList()
	private var sessionHasBeenCustomized = false
	private val handler: Handler = Handler()

	private val refreshTimer: Runnable = object : Runnable {
		override fun run() {
			refreshTimer()
			handler.postDelayed(this, TIMER_DELAY)
		}
	}


	/**
	 * Set fragment data
	 */
	fun setData(isTournament: Boolean, sessionId: String) {

		val realm = getRealm()
		val sessionRealm = realm.findById<Session>(sessionId)
		if (sessionRealm != null) {
			currentSession = sessionRealm
			sessionHasBeenCustomized = true
		} else {
			realm.beginTransaction()
			currentSession = Session.newInstance(realm, isTournament)
			FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext())
			realm.commitTransaction()

			// Find the nearest location around the user
			parentActivity?.findNearestLocation {
				it?.let { location ->
					realm.beginTransaction()
					val realmLocation = realm.findById<Location>(location.id)
					FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext())

					currentSession.location = realmLocation
					realm.commitTransaction()
					updateSessionUI(true)
				}
			}
			sessionHasBeenCustomized = false
		}

		toolbar.title = if (currentSession.isTournament()) getString(R.string.tournament) else getString(R.string.cash_game)
		collapsingToolbar.title = toolbar.title

		sessionAdapter = RowRepresentableAdapter(currentSession, this)
		recyclerView.adapter = sessionAdapter

		updateSessionUI(true)
	}

	override fun onResume() {
		super.onResume()
		this.refreshTimer()
	}

	private fun refreshTimer() {
		this.updateSessionUI(false)
	}

	override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
		super.onCreateView(inflater, container, savedInstanceState)
		return inflater.inflate(R.layout.fragment_session, container, false)
	}

	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		super.onViewCreated(view, savedInstanceState)
		initUI()
	}

	override fun onPause() {
		super.onPause()
		handler.removeCallbacksAndMessages(null)
	}

	override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
		menu?.clear()
		inflater?.inflate(R.menu.toolbar_session, menu)
		this.sessionMenu = menu
		updateMenuUI()
		super.onCreateOptionsMenu(menu, inflater)
	}

	override fun onOptionsItemSelected(item: MenuItem?): Boolean {
		when (item!!.itemId) {
			R.id.stop -> stopSession()
			R.id.newCustomField -> addNewCustomField()
			R.id.restart -> restartTimer()
			R.id.delete -> deleteSession()
		}
		return true
	}

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		if (fromAction) {
			Toast.makeText(requireContext(), "Action for row: $row", Toast.LENGTH_SHORT).show()
			return
		}

		val data = currentSession.editDescriptors(row)
		when (row) {
			SessionRow.START_DATE -> DateTimePickerManager.create(requireContext(), row, this, currentSession.startDate)
			SessionRow.END_DATE -> {

				if (this.currentSession.startDate == null) {
					Toast.makeText(context, R.string.session_missing_start_date, Toast.LENGTH_SHORT).show()
				} else {
					DateTimePickerManager.create(
						requireContext(),
						row,
						this,
						currentSession.endDate ?: currentSession.startDate ?: Date(),
						currentSession.startDate
					)
				}

			}
			SessionRow.BANKROLL -> {
				BottomSheetFragment.create(fragmentManager, row, this, data, false, currentSession.currency)
			}
			else -> BottomSheetFragment.create(fragmentManager, row, this, data, currentCurrency = currentSession.currency)
		}
	}

	override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
		sessionHasBeenCustomized = true
		try {
			currentSession.updateValue(value, row)
		} catch (e: IllegalStateException) {
			Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
			return
		}
		sessionAdapter.refreshRow(row)
		when (row) {
			SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT, SessionRow.BUY_IN, SessionRow.TIPS,
			SessionRow.START_DATE, SessionRow.END_DATE, SessionRow.BANKROLL, SessionRow.BREAK_TIME -> updateSessionUI()
		}
	}

	/**
	 * Init UI
	 */
	private fun initUI() {

		setDisplayHomeAsUpEnabled(true)

		val viewManager = SmoothScrollLinearLayoutManager(requireContext())
		recyclerView.apply {
			setHasFixedSize(true)
			layoutManager = viewManager
		}

		floatingActionButton.setOnClickListener {
			if (this.currentSession.isValidForSave()) {
				sessionHasBeenCustomized = true
				manageSessionState()
			} else {
				val builder = AlertDialog.Builder(requireContext())
					.setMessage(this.currentSession.getFailedSaveMessage(SaveValidityStatus.DATA_INVALID))
					.setNegativeButton(R.string.ok, null)
				builder.show()
			}
		}
	}

	/**
	 * Update the UI with the session data
	 * Should be called after the initialization of the session
	 */
	private fun updateSessionUI(firstDisplay: Boolean = false) {

		this.currentSession.updateRowRepresentation()

		handler.removeCallbacksAndMessages(null)

		val animationDuration = if (firstDisplay) 0L else 300L

		val state = currentSession.getState()
		when (state) {
			SessionState.PENDING, SessionState.PLANNED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = false
				floatingActionButton.setImageResource(R.drawable.ic_outline_play)
				sessionMenu?.findItem(R.id.stop)?.isVisible = false
				floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
					.setDuration(animationDuration)
					.setInterpolator(OvershootInterpolator()).start()
				handler.postDelayed(refreshTimer, Date().getNextMinuteInMilliseconds())
			}
			SessionState.STARTED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = true
				floatingActionButton.setImageResource(R.drawable.ic_outline_pause)
				sessionMenu?.findItem(R.id.stop)?.isVisible = true
				floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
					.setDuration(animationDuration)
					.setInterpolator(OvershootInterpolator()).start()
				handler.postDelayed(refreshTimer, Date().getNextMinuteInMilliseconds())
			}
			SessionState.PAUSED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = true
				floatingActionButton.setImageResource(R.drawable.ic_outline_play)
				sessionMenu?.findItem(R.id.stop)?.isVisible = true
				floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
					.setDuration(animationDuration)
					.setInterpolator(OvershootInterpolator()).start()
			}
			SessionState.FINISHED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = true
				sessionMenu?.findItem(R.id.stop)?.isVisible = false
				floatingActionButton.animate().scaleX(0f).scaleY(0f).alpha(0f)
					.setDuration(animationDuration)
					.setInterpolator(FastOutSlowInInterpolator()).start()
			}
		}

		updateMenuUI()
		updateAdapterUI(true)
	}

	/**
	 * Update Menu UI
	 */
	private fun updateMenuUI() {
		when (currentSession.getState()) {
			SessionState.PENDING, SessionState.PLANNED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = false
				sessionMenu?.findItem(R.id.stop)?.isVisible = false
			}
			SessionState.STARTED, SessionState.PAUSED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = true
				sessionMenu?.findItem(R.id.stop)?.isVisible = true
			}
			SessionState.FINISHED -> {
				sessionMenu?.findItem(R.id.restart)?.isVisible = true
				sessionMenu?.findItem(R.id.stop)?.isVisible = false
			}
		}
	}

	/**
	 * Update adapter UI
	 */
	private fun updateAdapterUI(scrollToTop: Boolean) {
		currentSession.adapterRows()?.let {
			val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(it, oldRows))
			sessionAdapter.updateRows(diffResult)

			oldRows.clear()
			oldRows.addAll(it)

//			if (scrollToTop) {
//				recyclerView.smoothScrollToPosition(0)
//			}
		}
	}

	/**
	 * Update the state of the session (start / pause)
	 */
	private fun manageSessionState() {
		when (currentSession.getState()) {
			SessionState.PENDING, SessionState.PLANNED, SessionState.PAUSED -> {
				currentSession.startOrContinue()
				this.recyclerView.smoothScrollToPosition(0)
			}
			SessionState.STARTED -> {
				currentSession.pause()
			}
			else -> {
			}
		}
		updateSessionUI()
	}

	/**
	 * Stop the current session
	 */
	private fun stopSession() {
		currentSession.stop()
		updateSessionUI()
	}

	/**
	 * Add new custom field
	 */
	private fun addNewCustomField() {
		EditableDataActivity.newInstanceForResult(this, LiveData.CUSTOM_FIELD, requestCode = REQUEST_CODE_NEW_CUSTOM_FIELD)
	}

	/**
	 * Restart timer
	 */
	private fun restartTimer() {
		currentSession.restart()
		updateSessionUI()
	}

	/**
	 * Delete a session
	 */
	private fun deleteSession() {
		currentSession.delete()
		activity?.finish()
	}

	/**
	 * Called when the user pressed back on the parent activity
	 */
	override fun onBackPressed() {
		super.onBackPressed()
		if (!sessionHasBeenCustomized) {
			currentSession.delete()
		}
	}

}