package net.pokeranalytics.android.model.realm

import io.realm.*
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
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 org.jetbrains.annotations.TestOnly
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() {

	private var entityType: Int? = Entity.SESSION.ordinal

	private enum class Entity {
		SESSION,
		;
	}

	companion object {

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

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

	    @TestOnly
	    inline fun <reified T : Filterable> queryOn(realm: Realm, queries: List<QueryCondition>): RealmResults<T> {
		    var realmQuery = realm.where<T>()
		    queries.forEach {
			    realmQuery = it.filter(realmQuery)
		    }
		    Timber.d(">>> Filter query: ${realmQuery.description}")
		    return realmQuery.findAll()
	    }
    }

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

	// the filter name
	var name: String = ""

	// the number of use of the filter,
	// for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters
	val usageCount: MutableRealmInteger = MutableRealmInteger.valueOf(0)

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

	fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<FilterElementRow>) {
        val casted = arrayListOf<FilterElementRow>()
        filterConditionRows
			.map {
				it.id
			}
			.distinct()
			.forEach { filterName->
				filterConditionRows
					.filter {
						it.id == filterName
					}
					.apply {
                        casted.addAll(this)
                        val newFilterCondition = FilterCondition(casted)
                        val previousCondition = filterConditions.filter {
                            it.sectionName == newFilterCondition.filterName
                        }
                        filterConditions.removeAll(previousCondition)
                        filterConditions.add(newFilterCondition)
                    }
			}
	}

	fun countBy(filterCategoryRow: FilterCategoryRow): Int {
		val sections = filterCategoryRow.filterSectionRows
		return filterConditions.count {
			sections.contains(FilterSectionRow.valueOf(it.sectionName ?: throw PokerAnalyticsException.FilterElementUnknownSectionName))
		}
	}

	fun contains(filterElementRow: FilterElementRow): Boolean {
		val filtered = filterConditions.filter {
			it.filterName == filterElementRow.id
		}
		if (filtered.isEmpty()) {
			return false
		}
		return filterElementRow.contains(filtered)
	}

	/**
	 * Set the saved value in the filter for the given [filterElementRow]
	 */
	fun setSavedValueForElement(filterElementRow: FilterElementRow) {
		when (filterElementRow) {
			is FilterElementRow.PastDays -> {
				val values = getSavedValueForElement(filterElementRow) as Array<*>
				if (values.isNotEmpty() && values.first() is Int) {
					filterElementRow.lastDays = values.first() as Int
				}
			}
			is FilterElementRow.DateFilterElementRow -> filterElementRow.dateValue = getSavedValueForElement(filterElementRow) as Date? ?: Date()
		}
	}

	/**
	 * Get the saved value for the given [filterElementRow]
	 */
	private fun getSavedValueForElement(filterElementRow: FilterElementRow): Any? {
		val filtered = filterConditions.filter {
			it.filterName == filterElementRow.id
		}

		if (filtered.isNotEmpty()) {
			return filtered.first().getFilterConditionValue(filterElementRow)
		}

		return null
	}

	inline fun <reified T : Filterable> results(): RealmResults<T> {
		var realmQuery = realm.where<T>()
		this.filterConditions.map {
			it.queryCondition
		}.forEach {
			realmQuery = it.filter(realmQuery)
		}

		return realmQuery.findAll()
	}

}
