package net.pokeranalytics.android.model.realm

import android.content.Context
import android.text.InputType
import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import java.util.*
import kotlin.collections.ArrayList


open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {

	/**
	 * The custom field type: a list of items, a number or an amont
	 */
	enum class Type(override var uniqueIdentifier: Int, var resId: Int, var isEnabled: Boolean = true) :
		IntIdentifiable {
		LIST(0, R.string.enum_custom_field_type),
		NUMBER(1, R.string.number),
		AMOUNT(2, R.string.amount)
	}

	/**
	 * The sorting used for the list, either custom, or alphabetically asc/desc
	 */
	enum class Sort(override var uniqueIdentifier: Int) : IntIdentifiable {
		DEFAULT(0),
		ASCENDING(1),
		DESCENDING(2)
	}

	@PrimaryKey
	override var id = UUID.randomUUID().toString()

	/**
	 * The name of the custom field
	 */
	override var name: String = ""

	// The type of the custom fields, mapped with the CustomField.Type enum
	var type: Int = Type.LIST.uniqueIdentifier
		set(value) {
			if (field == Type.LIST.uniqueIdentifier && value != Type.LIST.uniqueIdentifier) {
				this.entriesToDelete.addAll(this.entries)
				this.entries.clear()
			}
			field = value

			this.updateRowRepresentation()
		}

	/**
	 * Indicates whether the custom field value should be copied when a session is duplicated
	 */
	var duplicateValue: Boolean = false

	/**
	 * The list of entries for the LIST type
	 */
	var entries: RealmList<CustomFieldEntry> = RealmList()

	/**
	 * The sorting of the entries, mapped with the CustomField.Sort enum
	 */
	var sortCondition: Int = Sort.DEFAULT.uniqueIdentifier
		set(value) {
			field = value
			sortEntries()
			updateRowRepresentation()
		}

	@Ignore
	private var entriesToDelete: ArrayList<CustomFieldEntry> = ArrayList()

	@Ignore
	override var viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal

	@Ignore
	private var rowRepresentation: List<RowRepresentable> = mutableListOf()


	//helper

	val isListType: Boolean
		get() {
			return this.type == Type.LIST.uniqueIdentifier
		}

	val isAmountType: Boolean
		get() {
			return this.type == Type.AMOUNT.uniqueIdentifier
		}

	override fun localizedTitle(context: Context): String {
		return this.name
	}

	override fun getDisplayName(context: Context): String {
		return this.name
	}

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

	override fun updateValue(value: Any?, row: RowRepresentable) {
		when (row) {
			SimpleRow.NAME -> this.name = value as String? ?: ""
			CustomFieldRow.TYPE -> this.type = (value as Type?)?.uniqueIdentifier ?: Type.LIST.uniqueIdentifier
			CustomFieldRow.COPY_ON_DUPLICATE -> this.duplicateValue = value as Boolean? ?: false
		}
	}

	override fun isValidForSave(): Boolean {
		return super.isValidForSave()
	}

	override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
		return when (status) {
			SaveValidityStatus.DATA_INVALID -> R.string.cf_empty_field_error
			SaveValidityStatus.ALREADY_EXISTS -> R.string.duplicate_cf_error
			else -> super.getFailedSaveMessage(status)
		}
	}

	override fun alreadyExists(realm: Realm): Boolean {
		return realm.where(this::class.java).equalTo("name", this.name).and().notEqualTo("id", this.id).findAll()
			.isNotEmpty()
	}

	override fun isValidForDelete(realm: Realm): Boolean {
		return true
//		val sessions = realm.where<Session>().contains("customFieldEntries.customFields.id", id).findAll()
//		return sessions.isEmpty()
	}

	override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
		//TODO:
		return R.string.cf_entry_delete_popup_message
	}

	override val bottomSheetType: BottomSheetType
		get() {
			return when (type) {
				Type.LIST.uniqueIdentifier -> BottomSheetType.LIST_STATIC
				else -> BottomSheetType.NUMERIC_TEXT
			}
		}

	override fun deleteDependencies(realm: Realm) {
		if (isValid) {
			val entries = realm.where<CustomFieldEntry>().equalTo("customFields.id", id).findAll()
			entries.deleteAllFromRealm()
		}
	}

	override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
		return when (row) {
			is CustomFieldEntry -> row.editingDescriptors(
				mapOf(
					"defaultValue" to row.value
				)
			)
			else -> null
		}
	}

	override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowRepresentableEditDescriptor>? {
		return when (type) {
			Type.LIST.uniqueIdentifier -> {
				val defaultValue: Any? by map
				val data: RealmList<CustomFieldEntry>? by map
				arrayListOf(
					RowRepresentableEditDescriptor(defaultValue, staticData = data)
				)
			}
			else -> {
				val defaultValue: Double? by map
				arrayListOf(
					RowRepresentableEditDescriptor(
						defaultValue, inputType = InputType.TYPE_CLASS_NUMBER
								or InputType.TYPE_NUMBER_FLAG_DECIMAL
								or InputType.TYPE_NUMBER_FLAG_SIGNED
					)
				)
			}
		}
	}

	/**
	 * Update the row representation
	 */
	private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> {
		val rows = ArrayList<RowRepresentable>()
		rows.add(SimpleRow.NAME)
		rows.add(CustomFieldRow.TYPE)

		if (type == Type.LIST.uniqueIdentifier && entries.size >= 0) {
			if (entries.isNotEmpty()) {
				rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.items_list))
				sortEntries()
				entries.forEach { customFieldEntry ->
					customFieldEntry.isMovable = sortCondition == Sort.DEFAULT.uniqueIdentifier
				}
				rows.addAll(entries)
			}
		}

		return rows
	}

	/**
	 * Sort the entries element
	 */
	private fun sortEntries() {
		when (sortCondition) {
			Sort.ASCENDING.uniqueIdentifier -> entries.sortBy { it.value }
			Sort.DESCENDING.uniqueIdentifier -> entries.sortByDescending { it.value }
		}
		entries.forEachIndexed { index, customFieldEntry ->
			customFieldEntry.order = index
		}
	}

	fun updateRowRepresentation() {
		this.rowRepresentation = this.updatedRowRepresentationForCurrentState()
	}

	/**
	 * Add an entry
	 */
	fun addEntry(): CustomFieldEntry {
		val entry = CustomFieldEntry()
		this.entries.add(entry)
		sortEntries()
		updateRowRepresentation()
		return entry
	}

	/**
	 * Delete an entry
	 */
	fun deleteEntry(entry: CustomFieldEntry) {
		entriesToDelete.add(entry)
		entries.remove(entry)

		sortEntries()
		updateRowRepresentation()
	}

	fun cleanupEntries() { // called when saving the custom field

		val realm = Realm.getDefaultInstance()

		realm.executeTransaction {
			this.entriesToDelete.forEach { // entries are out of realm
				realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
			}
		}
		realm.close()
		this.entriesToDelete.clear()
	}

	/**
	 * Clean the entries if the type is not a list & remove the deleted entries from realm
	 */
//	fun cleanEntries(realm: Realm) {
//		realm.executeTransaction {
//
//			if (!isListType) {
//				entriesToDelete.addAll(entries)
//				entries.clear()
//			}
//
//			// @TODO
//			entriesToDelete.forEach {
//				Timber.d("Delete entry: V=${it.value} N=${it.numericValue} / ID=${it.id}")
//				realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
//			}
//			entriesToDelete.clear()
//		}
//	}

	/**
	 * Returns a comparison criteria based on this custom field
	 */
	val criteria: Criteria
		get() {
			return when (this.type) {
				CustomField.Type.LIST.uniqueIdentifier -> Criteria.ListCustomFields(this.id)
				else -> Criteria.ValueCustomFields(this.id)
			}
		}

}