package net.pokeranalytics.android.ui.fragment

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_bankroll.*
import kotlinx.android.synthetic.main.fragment_bankroll.addButton
import kotlinx.android.synthetic.main.fragment_bankroll.toolbar
import kotlinx.android.synthetic.main.fragment_data_list.*
import kotlinx.android.synthetic.main.fragment_stats.recyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollCalculator
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportSetup
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.StatisticDetailsActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList

class BankrollFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {

	companion object {

		const val REQUEST_CODE_DETAILS = 1000

		/**
		 * Create new instance
		 */
		fun newInstance(): BankrollFragment {
			val fragment = BankrollFragment()
			val bundle = Bundle()
			fragment.arguments = bundle
			return fragment
		}
	}

	private lateinit var parentActivity: PokerAnalyticsActivity
	private lateinit var bankrollAdapter: RowRepresentableAdapter

	private var rows: ArrayList<RowRepresentable> = ArrayList()
	private var bankrollReportForRow: HashMap<RowRepresentable, BankrollReport> = HashMap()
	private var deletedItem: RealmObject? = null
	private var lastDeletedItemPosition: Int = 0
	private var lastItemClickedPosition: Int = 0

	// Life Cycle

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

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


	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
		super.onActivityResult(requestCode, resultCode, data)
		if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) {
			val needToDeleteItem = data?.getBooleanExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, false) ?: false
			if (needToDeleteItem) {
				GlobalScope.launch(Dispatchers.Main) {
					delay(300)
					deleteItem(lastItemClickedPosition)
				}
			}
		}
	}

	override fun adapterRows(): List<RowRepresentable>? {
		return rows
	}

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		Timber.d("onRowSelected: $row")
		when (row) {
			is GraphRow -> {
				row.report.results.firstOrNull()?.group?.let { computableGroup ->
					StatisticDetailsActivity.newInstance(requireContext(), row.stat, computableGroup, row.report, false, row.title)
				}
			}
			else -> {
				if (bankrollReportForRow.containsKey(row)) {
					bankrollReportForRow[row]?.let { bankrollReport ->
						BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, REQUEST_CODE_DETAILS)
					}
				}
			}
		}
	}

	// Business

	/**
	 * Init data
	 */
	private fun initData() {

		rows.clear()
		bankrollReportForRow.clear()

		GlobalScope.launch {

			val startDate = Date()
			val realm = Realm.getDefaultInstance()

			// Graph

			val requiredStats: List<Stat> = listOf(Stat.NET_RESULT)
			val options = Calculator.Options(
				evolutionValues = Calculator.Options.EvolutionValues.STANDARD,
				stats = requiredStats,
				criterias = listOf(Criteria.Bankrolls)
			)

			val report = Calculator.computeStats(realm, options = options)
			rows.add(0, GraphRow(report, Stat.NET_RESULT))

			val globalBankrollReportSetup = BankrollReportSetup()
			val globalBankrollReport = BankrollCalculator.computeReport(globalBankrollReportSetup)
			rows.add(globalBankrollReport)
			bankrollReportForRow[globalBankrollReport] = globalBankrollReport

			// Bankrolls
			rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.bankrolls))

			realm.close()
			Timber.d("initData: ${System.currentTimeMillis() - startDate.time}ms")

			launch(Dispatchers.Main) {

				val bankrolls = LiveData.BANKROLL.items(getRealm()) as RealmResults<Bankroll>

				bankrolls.forEach {
					val bankrollReportSetup = BankrollReportSetup(it)
					val bankrollReport = BankrollCalculator.computeReport(bankrollReportSetup)
					val computedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total)
					val row = CustomizableRowRepresentable(RowViewType.TITLE_VALUE_ARROW, title = it.name, computedStat = computedStat, isSelectable = true)

					rows.add(row)
					bankrollReportForRow[row] = bankrollReport
				}


				if (!isDetached) {
					bankrollAdapter.notifyDataSetChanged()
				}
			}
		}

	}

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

		parentActivity = activity as PokerAnalyticsActivity

		parentActivity.setSupportActionBar(toolbar)
		parentActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
		setHasOptionsMenu(true)

		bankrollAdapter = RowRepresentableAdapter(this, this)

		val viewManager = LinearLayoutManager(requireContext())

		recyclerView.apply {
			setHasFixedSize(true)
			layoutManager = viewManager
			adapter = bankrollAdapter
		}

		addButton.setOnClickListener {
			LiveData.BANKROLL?.let {
				EditableDataActivity.newInstance(
					requireContext(),
					dataType = it.ordinal,
					primaryKey = null
				)
			}
		}
	}


	/**
	 * Delete item
	 */
	private fun deleteItem(position: Int) {

		if (isDetached || activity == null) {
			return
		}

		// Save the delete position & create a copy of the object
		val mRecentlyDeletedItem = rowRepresentableForPosition(position)
		lastDeletedItemPosition = position

		if (mRecentlyDeletedItem is RealmObject) {

			// Check if the object is valid for the deletion
			if ((mRecentlyDeletedItem as Deletable).isValidForDelete(this.getRealm())) {
				deletedItem = getRealm().copyFromRealm(mRecentlyDeletedItem)
				getRealm().executeTransaction {
					mRecentlyDeletedItem.deleteFromRealm()
				}
				bankrollAdapter.notifyItemRemoved(position)
				showUndoSnackBar()
			} else {
				bankrollAdapter.notifyItemChanged(position)
				val builder = AlertDialog.Builder(requireContext())
					.setMessage((mRecentlyDeletedItem as Deletable).getFailedDeleteMessage())
					.setNegativeButton(R.string.ok, null)
				builder.show()
			}
		}
	}

	/**
	 * Show undo snack bar
	 */
	private fun showUndoSnackBar() {
		val message = String.format(getString(R.string.data_deleted), getString(R.string.bankroll))
		val snackBar = Snackbar.make(constraintLayout, message, Snackbar.LENGTH_INDEFINITE)
		snackBar.setAction(R.string.cancel) {
			getRealm().executeTransaction { realm ->
				deletedItem?.let {
					realm.copyToRealmOrUpdate(it)
					bankrollAdapter.notifyItemInserted(lastDeletedItemPosition)
				}
			}
		}
		snackBar.show()
	}

}