package net.pokeranalytics.android.ui.fragment

import android.app.Activity.RESULT_OK
import android.content.Context
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 kotlinx.android.synthetic.main.fragment_filter_details.*
import kotlinx.android.synthetic.main.fragment_filter_details.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.FilterDetailsActivity
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.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.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow
import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList

open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {

	lateinit var rowRepresentableAdapter: RowRepresentableAdapter
	private lateinit var primaryKey: String
	private lateinit var filterCategoryRow: FilterCategoryRow

	private var currentFilter: Filter? = null
	private var rows: ArrayList<RowRepresentable> = ArrayList()
	private var rowsForFilterSubcategoryRow: HashMap<FilterSectionRow, ArrayList<RowRepresentable>> = HashMap()

	private val selectedRows = ArrayList<QueryCondition>()

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

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

	override fun onBackPressed() {
		super.onBackPressed()
		saveData()
	}

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

		if (row.viewType == RowViewType.TITLE_CHECK.ordinal) {
			updateRowsSelection(row)
			return
		}

		when (row) {
			is QueryCondition.DateQuery -> DateTimePickerManager.create(
				requireContext(),
				row,
				this,
				row.singleValue,
				onlyDate = !row.showTime,
				onlyTime = row.showTime
			)
			is QueryCondition.Duration -> {
				var hours: String? = null
				var minutes: String? = null
				row.minutes?.let {
					hours = if (it / 60 > 0) (it / 60).toString() else null
					minutes = if (it % 60 > 0) (it % 60).toString() else null
				}
				val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes))
				BottomSheetFragment.create(fragmentManager, row, this, data, true)
			}
			is QueryCondition.ListOfValues<*> -> {
				var valueAsString: String? = null
				row.listOfValues.firstOrNull()?.let {
					valueAsString = row.listOfValues.firstOrNull()?.toString()
				}
				val data = row.editingDescriptors(mapOf("valueAsString" to valueAsString))
				BottomSheetFragment.create(fragmentManager, row, this, data, true)
			}
		}
	}

	override fun stringForRow(row: RowRepresentable, context: Context): String {
		return when (row) {
			is QueryCondition.ListOfValues<*> -> row.firstValue(context)
			else -> super.stringForRow(row)
		} ?: NULL_TEXT
	}

	override fun isSelected(row: RowRepresentable): Boolean {
		return selectedRows.contains(row)
	}

	override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
		super.onRowValueChanged(value, row)
		Timber.d("onRowValueChanged: $row $value")

		when (row) {
			is QueryCondition.DateQuery -> row.singleValue = if (value != null && value is Date) value else null
			is QueryCondition.Duration -> {
				if (value is ArrayList<*>) {
					val hours: Int? = try {
						(value[0] as String?)?.toInt()
					} catch (e: Exception) {
						null
					}
					val minutes = try {
						(value[1] as String?)?.toInt()
					} catch (e: Exception) {
						null
					}
					if (hours != null && minutes != null) {
						row.minutes = hours * 60 + minutes
					} else if (hours != null) {
						row.minutes = hours * 60
					} else if (minutes != null) {
						row.minutes = minutes
					}
				} else {
					row.minutes = null
				}
			}
			is QueryCondition.SingleInt -> row.singleValue = if (value != null && value is String) value.toInt() else null
			is QueryCondition.ListOfDouble -> row.listOfValues = arrayListOf<Double>().apply {
				if (value != null && value is String) this.add(value.toDouble())
			}
			is QueryCondition.ListOfInt -> row.listOfValues = arrayListOf<Int>().apply {
				if (value != null && value is String) this.add(value.toInt())
			}
			is QueryCondition.ListOfString -> row.listOfValues = arrayListOf<String>().apply {
				if (value != null && value is String) this.add(value)
			}
		}

		// Remove the row before updating the selected rows list
		selectedRows.remove(row as FilterElementRow)
		updateRowsSelection(row, value == null)

	}

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

	override fun viewTypeForPosition(position: Int): Int {
		val rowViewType = rowRepresentableForPosition(position)?.viewType ?: -1
		return if (rowViewType != -1) rowViewType else RowViewType.TITLE_CHECK.ordinal
	}

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

		setDisplayHomeAsUpEnabled(true)

		this.appBar.toolbar.title = getString(R.string.filter)

		val viewManager = LinearLayoutManager(requireContext())

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

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

		//currentFilter = Filter.getFilterBydId(getRealm(), primaryKey)
		currentFilter = FiltersFragment.currentFilter

		this.appBar.toolbar.title = filterCategoryRow.localizedTitle(requireContext())

		this.rows.clear()
		this.rowsForFilterSubcategoryRow.clear()
		this.rows.addAll(filterCategoryRow.filterElements)

		this.rows.forEach { element ->
			if (element is QueryCondition && currentFilter?.contains(element) == true) {
				currentFilter?.loadValueForElement(element)
				this.selectedRows.add(element)
			}
		}

		this.rowRepresentableAdapter = RowRepresentableAdapter(this, this)
		this.recyclerView.adapter = rowRepresentableAdapter
	}

	/**
	 * Update rows selection
	 */
	private fun updateRowsSelection(row: RowRepresentable, forceDeselection: Boolean = false) {

		if (selectedRows.contains(row) || forceDeselection) {
			selectedRows.remove(row)
		} else {
			if (row is FilterElementRow) {
				row.sectionToExclude?.let { filterSectionToExclude ->
					val excludedFilters = selectedRows.filter {
						filterSectionToExclude.contains(it.filterSectionRow)
					}
					excludedFilters.forEach {
						selectedRows.remove(it)
						rowRepresentableAdapter.refreshRow(it)
					}
				}
				selectedRows.add(row as QueryCondition)
			}
		}

		println("list of selected rows : $selectedRows")

		// Update UI
		rowRepresentableAdapter.refreshRow(row)
	}

	/**
	 * Save data
	 */
	private fun saveData() {
		//TODO: Save currentFilter details data
		Timber.d("Save data for queryWith: ${currentFilter?.id}")
		selectedRows.forEach {
			Timber.d("Selected rows: $it")
		}

		val realm = getRealm()
		realm.beginTransaction()
		currentFilter?.remove(filterCategoryRow)
		currentFilter?.createOrUpdateFilterConditions(selectedRows)
		realm.commitTransaction()

		currentFilter?.filterConditions?.forEach {
			Timber.d("Condition: $it")
		}

		finishActivityWithResult(currentFilter?.id)
	}


	/**
	 * Finish the activity with a result
	 */
	private fun finishActivityWithResult(uniqueIdentifier: String?) {
		val intent = Intent()
		intent.putExtra(FilterDetailsActivity.IntentKey.FILTER_ID.keyName, uniqueIdentifier)
		activity?.setResult(RESULT_OK, intent)
		activity?.finish()
	}

	/**
	 * Set fragment data
	 */
	fun setData(primaryKey: String, filterCategory: Int) {
		this.primaryKey = primaryKey
		this.filterCategoryRow = FilterCategoryRow.values()[filterCategory]
	}

}