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 android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.*
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.Stat
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
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.ReportRow
import timber.log.Timber
import java.util.*

class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {

	private lateinit var reportSetups: RealmResults<ReportSetup>
	private var adapterRows = mutableListOf<RowRepresentable>()

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

	companion object {

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

	}

	// Life Cycle

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

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

	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
		super.onActivityResult(requestCode, resultCode, data)

		if (requestCode == RequestCode.NEW_REPORT.value && resultCode == Activity.RESULT_OK) {
			ReportCreationActivity.options?.let { options ->
				this.launchReportWithOptions(options, options.getName(requireContext()))
			}
			ReportCreationActivity.options = null
		} else if (requestCode == RequestCode.DEFAULT.value && resultCode == Activity.RESULT_OK) {
			val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
			itemToDeleteId?.let { id ->
				GlobalScope.launch(Dispatchers.Main) {
					delay(300)
					deleteItem(dataListAdapter, reportSetups, id)
				}
			}
		}

	}

	// Business

	/**
	 * Init data
	 */
	private fun initData() {
		this.reportSetups = getRealm().where(ReportSetup::class.java).findAll().sort("name")
		this.reportSetups.addChangeListener { _, _ ->
			this.updateRows()
		}
	}

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

		dataListAdapter = RowRepresentableAdapter(this, this)

		val viewManager = LinearLayoutManager(requireContext())

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

		this.addButton.setOnClickListener {
			ReportCreationActivity.newInstanceForResult(this, requireContext())
		}

	}

	// Rows

	private fun updateRows() {
		this.adapterRows.clear()
		if (this.reportSetups.size > 0) {
			adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.custom))
			adapterRows.addAll(this.reportSetups)
		}
		adapterRows.addAll(ReportRow.getRows())
		this.dataListAdapter.notifyDataSetChanged()
	}

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

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		super.onRowSelected(position, row, fromAction)

		when (row) {
			is ReportRow -> {
				val reportName = row.localizedTitle(requireContext())
				launchComputation(row.criteria, reportName)
			}
			is ReportSetup -> {
				launchReportWithOptions(row.options, row.name)
			}
		}
	}

	/**
	 * Launch computation
	 */
	private fun launchComputation(criteriaList: List<Criteria>, reportName: String) {

		if (criteriaList.combined().size < 2) {
			Toast.makeText(context, R.string.less_then_2_values_for_display, Toast.LENGTH_LONG).show()
			return
		}

		val requiredStats: List<Stat> = listOf(Stat.NET_RESULT)
		val options = Calculator.Options(
			display = Calculator.Options.Display.COMPARISON,
			progressValues = Calculator.Options.ProgressValues.STANDARD,
			stats = requiredStats,
			criterias = criteriaList
		)

		this.launchReportWithOptions(options, reportName)

	}

	/**
	 * Launch and display a report with some [options] and a [reportName]
	 */
	private fun launchReportWithOptions(options: Calculator.Options, reportName: String) {

		showLoader()

		GlobalScope.launch {

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

			val report = Calculator.computeStats(realm, options = options)

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

			launch(Dispatchers.Main) {
				if (!isDetached) {
					hideLoader()
					ReportActivity.newInstanceForResult(this@ReportsFragment, report, reportName)
				}
			}
			realm.close()
		}
	}

}