package net.pokeranalytics.android.model.realm

import android.content.Context
import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Editable
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.ImageDecorator
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.FilterCategoryRow
import timber.log.Timber
import java.util.*

/**
 * A [Filter] is the top level representation of the filtering system
 * It contains a list of [FilterCondition] describing the complete query to launch
 * The [Filter] is working closely with a [Filterable] interface providing the entity we want the query being launched on
 */
open class Filter : RealmObject(), RowRepresentable, Editable, Deletable, CountableUsage, ImageDecorator {

	companion object {

		// Create a new instance
		fun newInstance(filterableTypeUniqueIdentifier: Int): Filter {
			val filter = Filter()
			filter.filterableTypeUniqueIdentifier = filterableTypeUniqueIdentifier
			return filter
			//return realm.copyToRealm(filter)
		}

		// Get a queryWith by its id
		fun getFilterBydId(realm: Realm, filterId: String): Filter? {
			return realm.where<Filter>().equalTo("id", filterId).findFirst()
		}

	    inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
            val rootQuery = realm.where<T>()
		    var realmQuery = query.queryWith(rootQuery)
            sortField?.let {
	            realmQuery = realmQuery.sort(it)
            }
		    val desc = realmQuery.description
		    return realmQuery.findAll()
	    }
    }

	override val viewType: Int
		get() = RowViewType.TITLE_VALUE_ACTION.ordinal
	override val imageRes: Int?
		get() = R.drawable.ic_outline_settings
	override val imageTint: Int?
		get() = R.color.green
	override val imageClickable: Boolean?
		get() = true


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

	// the queryWith name
	var name: String = ""
		get() {
			if (field.isEmpty()) {
				return this.query.defaultName
			}
			return field
		}

	override var useCount: Int = 0

	var filterConditions: RealmList<FilterCondition> = RealmList()
		private set

	private var filterableTypeUniqueIdentifier: Int? = null

	val filterableType: FilterableType
		get() {
			this.filterableTypeUniqueIdentifier?.let {
				return FilterableType.valueByIdentifier(it)
			}
			return FilterableType.ALL
		}

	fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) {
		Timber.d("list of querys saving: ${filterConditionRows.map { it.id }}")
		Timber.d("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
		filterConditionRows
			.map {
				it.groupId
			}
			.distinct()
			.forEach { groupId ->
				filterConditionRows
					.filter {
						it.groupId == groupId
					}
					.apply {

						Timber.d("list of querys: ${this.map { it.id }}")
						val casted = arrayListOf<QueryCondition>()
						casted.addAll(this)
						val newFilterCondition = FilterCondition(casted)
						val previousCondition = filterConditions.filter {
							it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
						}
						filterConditions.removeAll(previousCondition)
						filterConditions.add(newFilterCondition)
					}
			}
	}

	fun remove(filterCategoryRow: FilterCategoryRow) {
		val sections = filterCategoryRow.filterSectionRows.map { it.name }
		val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
		this.filterConditions.removeAll(savedSections)
	}

	fun countBy(filterCategoryRow: FilterCategoryRow): Int {
		val sections = filterCategoryRow.filterSectionRows.map { it.name }
		Timber.d("list of sections $sections")
		val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
		Timber.d("list of savedSections $savedSections")
		return savedSections.size
	}

	fun contains(filterElementRow: QueryCondition): Boolean {
		Timber.d("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
		Timber.d("list of contains ${filterElementRow.id}")
		val contained = filterConditions.flatMap { it.queryCondition.id }.contains(filterElementRow.id.first())
		Timber.d("list of : $contained")
		return contained
	}

	/**
	 * Get the saved value for the given [filterElementRow]
	 */
	fun loadValueForElement(filterElementRow: QueryCondition) {
		val filtered = filterConditions.filter {
			it.queryCondition.id == filterElementRow.id
		}
		if (filtered.isNotEmpty()) {
			return filterElementRow.updateValueBy(filtered.first())
		}
	}

	inline fun <reified T : Filterable> results(firstField: String? = null, secondField: String? = null): RealmResults<T> {

		val realmQuery = realm.where<T>()

		if (firstField != null && secondField != null) {
			return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll()
		}

		if (firstField != null) {
			return this.query.queryWith(realmQuery).distinct(firstField).findAll()
		}

		return this.query.queryWith(realmQuery).findAll()
	}

	val query: Query
		get() {
			val query = Query()
			this.filterConditions.forEach {
				query.add(it.queryCondition)
			}
			return query
		}

	override fun getDisplayName(context: Context): String {
		if (name.isNotEmpty()) return name
		return this.query.getName(context)
	}

	override fun isValidForDelete(realm: Realm): Boolean {
		return true
	}

	override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
		return R.string.relationship_error
	}

	override val bottomSheetType: BottomSheetType
		get() {
			return BottomSheetType.EDIT_TEXT
		}

	override fun localizedTitle(context: Context): String {
		return context.getString(R.string.name)
	}

	override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowRepresentableEditDescriptor>? {
		val defaultValue: String? by map
		return arrayListOf(RowRepresentableEditDescriptor(defaultValue, R.string.name))
	}

	override fun updateValue(value: Any?, row: RowRepresentable) {
		realm.executeTransaction {
			val newName = value as String? ?: ""
			if (newName.isNotEmpty()) {
				name = newName
			}
		}
	}
}
