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.recyclerview.widget.LinearLayoutManager
import com.github.mikephil.charting.data.LineDataSet
import io.realm.RealmObject
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_bankroll.*
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.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.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
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.GraphActivity
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.DeletableItemFragment
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 net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList

class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {

	companion object {

		const val REQUEST_CODE_DETAILS = 100
		const val REQUEST_CODE_CREATE = 101

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

	private var rows: ArrayList<RowRepresentable> = ArrayList()
	private var bankrollReportForRow: HashMap<RowRepresentable, BankrollReport> = HashMap()
	private var lastItemClickedPosition: Int = 0
	private var lastItemClickedId: String = ""
	private var deletedRow: RowRepresentable? = null

	private lateinit var bankrolls: RealmResults<Bankroll>

	override fun deletableItems() : List<Deletable> {
		return this.bankrolls
	}

	// Life Cycle

	override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
		super.onCreateView(inflater, container, savedInstanceState)
		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 itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
			itemToDeleteId?.let { id ->
				GlobalScope.launch(Dispatchers.Main) {
					delay(300)
					deleteItem(dataListAdapter, bankrolls, id)
				}
			}

		} else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) {
			//TODO: Refresh bankrolls
			initData()
		}

	}

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

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		when (row) {
			is GraphRow -> {
				val lineDataSet = row.dataSet as LineDataSet
				GraphActivity.newInstance(requireContext(), listOf(lineDataSet), title = getString(R.string.bankroll))
			}
			else -> {
				if (bankrollReportForRow.containsKey(row)) {
					bankrollReportForRow[row]?.let { bankrollReport ->
						lastItemClickedPosition = position
						lastItemClickedId = (row as? Identifiable)?.id ?: ""
						BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, REQUEST_CODE_DETAILS)
					}
				}
			}
		}
	}

	// Business

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

		val realm = getRealm()
		this.bankrolls = realm.sorted()

		rows.clear()
		bankrollReportForRow.clear()

		GlobalScope.launch {

			launch(Dispatchers.Main) {

				// TODO: Improve that
				// We are in the main thread...

				val startDate = Date()

				// Graph
				val globalBankrollReportSetup = BankrollReportSetup()
				val globalBankrollReport = BankrollCalculator.computeReport(getRealm(), globalBankrollReportSetup)
				rows.add(0, GraphRow(dataSet = globalBankrollReport.lineDataSet(requireContext())))
				rows.add(globalBankrollReport)
				bankrollReportForRow[globalBankrollReport] = globalBankrollReport

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

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

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

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

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

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

	}

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

		setDisplayHomeAsUpEnabled(true)

		dataListAdapter = RowRepresentableAdapter(this, this)

		val viewManager = LinearLayoutManager(requireContext())

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

		addButton.setOnClickListener {
			EditableDataActivity.newInstanceForResult(this@BankrollFragment, dataType = LiveData.BANKROLL, primaryKey = null, requestCode = REQUEST_CODE_CREATE)
		}
	}

	override fun updateUIAfterDeletion(itemPosition: Int) {
		lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false }
		deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false }
		rows.removeAt(lastItemClickedPosition)
		dataListAdapter.notifyItemRemoved(lastItemClickedPosition)
	}

	override fun updateUIAfterUndoDeletion(newItem: RealmObject) {

		// TODO: Improve that
		// We are recreating a Bankroll report because the last one is invalid => the bankroll of the setup has been deleted

		deletedRow?.let { row ->
			val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll)
			val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
			bankrollReportForRow[row] = bankrollReport

			rows.add(lastItemClickedPosition, row)
			dataListAdapter.notifyItemInserted(lastItemClickedPosition)
		}
	}

}