package net.pokeranalytics.android.ui.fragment

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.*
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_report_creation.*
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.CustomFieldCriteria
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode
import net.pokeranalytics.android.ui.interfaces.FilterableType
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.SeparatorRow
import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber

class ReportCreationFragment : RealmFragment(), RowRepresentableDataSource, RowRepresentableDelegate {

	private lateinit var optionsAdapter: RowRepresentableAdapter

	private var assistant = Assistant()
	private var currentRows: List<RowRepresentable> = listOf()
	private var reportCreationMenu: Menu? = null

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

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

	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
		super.onActivityResult(requestCode, resultCode, data)
		if (requestCode == FilterActivityRequestCode.CREATE_FILTER.ordinal && resultCode == Activity.RESULT_OK) {
			this.updateUIWithCurrentStep()
			val selectedFilterId = data?.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName)
			val filterPosition = currentRows.indexOfFirst { it is Filter && it.id == selectedFilterId }
			if (filterPosition != -1) {
				onRowSelected(filterPosition, currentRows[filterPosition])
			}
		}
	}

	override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
		super.onCreateOptionsMenu(menu, inflater)
		menu?.clear()
		inflater?.inflate(R.menu.toolbar_report_creation, menu)
		menu?.findItem(R.id.add)?.isVisible = false
		reportCreationMenu = menu
	}

	override fun onOptionsItemSelected(item: MenuItem?): Boolean {
		when (item?.itemId) {
			R.id.add -> {
				if (this.assistant.step == Assistant.Step.FILTER) {
					FiltersActivity.newInstanceForResult(
						this,
						currentFilterable = FilterableType.SESSION,
						hideMostUsedFilters = true
					)
				}
			}
		}
		return super.onOptionsItemSelected(item)
	}

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

		setDisplayHomeAsUpEnabled(true)

		this.optionsAdapter = RowRepresentableAdapter(this, this)
		//this.optionsAdapter.setHasStableIds(true)

		val viewManager = LinearLayoutManager(requireContext())
		recyclerView.apply {
			setHasFixedSize(true)
			layoutManager = viewManager
			adapter = optionsAdapter
		}

		this.next.setOnClickListener {
			if (assistant.nextEnabled) {
				this.assistant.nextStep()

				if (this.assistant.step == Assistant.Step.FINALIZE) {

//					getRealm().executeTransaction {
//						val rs = this.assistant.options.reportSetup("test")
//						it.insert(rs)
//					}

					// launch report
					this.finishActivityWithOptions(this.assistant.options)

				} else {
					this.updateUIWithCurrentStep()
				}
			}
		}
	}

	/**
	 * Update UI when changing the current step
	 */
	private fun updateUIWithCurrentStep() {

		this.next.visibility = if (this.assistant.nextButtonShouldAppear) View.VISIBLE else View.GONE
		this.next.text = requireContext().getString(this.assistant.nextButtonTitleResId)
		this.next.isEnabled = this.assistant.nextEnabled
		this.reportCreationMenu?.findItem(R.id.add)?.isVisible = this.assistant.addButtonShouldAppear

		val rows = mutableListOf<RowRepresentable>()

		this.assistant.titleResId?.let { titleResId ->
			rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = titleResId))
		}

		rows.addAll(this.assistant.dataSource)

		this.currentRows = rows
		this.optionsAdapter.notifyDataSetChanged()

	}

	/**
	 * Finish activity
	 */
	private fun finishActivityWithOptions(options: Calculator.Options) {
		ReportCreationActivity.options = options // temp object
		val intent = Intent()
		activity?.setResult(Activity.RESULT_OK, intent)
		activity?.finish()
	}


	// RowRepresentableDataSource

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

	override fun rowRepresentableForPosition(position: Int): RowRepresentable? {
		return this.currentRows[position]
	}

	override fun numberOfRows(): Int {
		return this.currentRows.size
	}

	override fun viewTypeForPosition(position: Int): Int {
		return when (position) {
			0 -> RowViewType.HEADER_TITLE.ordinal
			else -> {
				val row = this.currentRows[position]
				when (row) {
					is SeparatorRow -> row.viewType
					else -> RowViewType.TITLE_CHECK.ordinal
				}
			}
		}
	}

	override fun isSelected(row: RowRepresentable): Boolean {
		return this.assistant.isSelected(row)
	}

	// RowRepresentableDelegate

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

		val newStep = this.assistant.onRowSelected(position - 1)
		if (newStep) {
			this.updateUIWithCurrentStep()
		} else {
			this.next.isEnabled = this.assistant.nextEnabled
			this.optionsAdapter.notifyDataSetChanged()
		}

	}

}

class Assistant {

	var step: Step = Step.TYPE
	private var display: Calculator.Options.Display = Calculator.Options.Display.TABLE
	private var stats = mutableListOf<Stat>()
	private var comparators = mutableListOf<Criteria>()
	private var useFilter: Boolean? = null
	private var filter: Filter? = null

	val options: Calculator.Options
		get() {
			return Calculator.Options(
				this.display,
				stats = this.stats,
				criterias = this.comparators,
				filter = this.filter,
				userGenerated = true
			)
		}

	enum class Step {
		TYPE,
		STAT,
		COMPARATOR,
		FILTER,
		FINALIZE
	}

	/**
	 * Notifies the Assistant a row has been selected with a [position]
	 * Returns whether the step has changed
	 */
	fun onRowSelected(position: Int): Boolean {

		val stepChanges = !this.nextButtonShouldAppear

		when (this.step) {
			Step.TYPE -> {
				this.display = this.dataSource[position] as Calculator.Options.Display
				this.nextStep()
			}
			Step.STAT -> {
				val stat = this.dataSource[position] as Stat
				if (this.stats.contains(stat)) {
					this.stats.remove(stat)
				} else {
					this.stats.add(stat)
				}
			}
			Step.COMPARATOR -> {
				val item = this.dataSource[position]

				val comparator = when (item) {
					is CustomField -> {
						val criteria: Criteria = when (item.type) {
							CustomField.Type.LIST.ordinal -> Criteria.ListCustomFields(item.id)
							else -> Criteria.ValueCustomFields(item.id)
						}
						criteria
					}
					is Criteria -> item
					else -> {
						Timber.d("onRowSelected: unmanaged data type: $item")
						return false
					}
				}

				if (this.comparators.contains(comparator)) {
					this.comparators.remove(comparator)
				} else {
					this.comparators.add(comparator)
				}
			}
			Step.FILTER -> {
				this.filter = this.dataSource[position] as Filter
			}
			else -> {
			}
		}
		return stepChanges
	}

	fun isSelected(row: RowRepresentable): Boolean {
		return when (this.step) {
			Step.STAT -> this.stats.contains(row as Stat)
			Step.COMPARATOR -> {
				when (row) {
					is Criteria -> this.comparators.contains(row)
					is CustomField -> {
						val cfCriteria = this.comparators.filterIsInstance<CustomFieldCriteria>()
						val cfCriterion = cfCriteria.firstOrNull { it.customFieldId == row.id }
						return cfCriterion != null
					}
					else -> false
				}
			}
			Step.FILTER -> this.filter == row
			else -> false
		}
	}

	fun nextStep() {
		this.step = this.nextStep
	}

	private val nextStep: Step
		get() {
			return if (this.stats.isEmpty()) {
				Step.STAT
			} else if (this.display == Calculator.Options.Display.COMPARISON && this.comparators.isEmpty()) {
				Step.COMPARATOR
			} else if (this.step == Step.FILTER) {
				Step.FINALIZE
			} else if (this.useFilter == null) {
				Step.FILTER
			} else {
				Step.FINALIZE
			}

		}

	val titleResId: Int?
		get() {
			return when (step) {
				Step.TYPE -> R.string.new_report_step_type
				Step.STAT -> R.string.new_report_step_stat
				Step.COMPARATOR -> R.string.new_report_step_comparator
				Step.FILTER -> R.string.new_report_step_filter
				else -> null
			}
		}

	val dataSource: List<RowRepresentable>
		get() {
			return when (this.step) {
				Step.TYPE -> listOf(
					Calculator.Options.Display.TABLE,
					Calculator.Options.Display.PROGRESS,
					Calculator.Options.Display.COMPARISON
				)
				Step.STAT -> {
					when (this.display) {
						Calculator.Options.Display.PROGRESS -> Stat.evolutionValuesList
						else -> Stat.userSelectableList
					}
				}
				Step.COMPARATOR -> {
					val list = mutableListOf<RowRepresentable>()

					val realm = Realm.getDefaultInstance()
					val customFields = realm.sorted<CustomField>()
					list.addAll(customFields)
					list.add(SeparatorRow())
					list.addAll(Criteria.all)
					realm.close()
					return list
				}
				Step.FILTER -> {
					val realm = Realm.getDefaultInstance()
					val filters = realm.sorted(Filter::class.java)
					realm.close()
					filters
				}
				else -> listOf()
			}
		}

	val nextEnabled: Boolean
		get() {
			return when (this.step) {
				Step.STAT -> this.stats.isNotEmpty()
				Step.COMPARATOR -> this.comparators.isNotEmpty()
				Step.FILTER -> true
				else -> false
			}
		}

	val nextButtonShouldAppear: Boolean
		get() {
			return when (this.step) {
				Step.STAT, Step.COMPARATOR, Step.FILTER -> true
				else -> false
			}
		}

	val nextButtonTitleResId: Int
		get() {
			return when (this.step) {
				Step.FILTER -> R.string.launch_report
				else -> R.string.next
			}
		}

	val addButtonShouldAppear: Boolean
		get() {
			return when (this.step) {
				Step.FILTER -> true
				else -> false
			}
		}

}