package net.pokeranalytics.android.model.realm

import io.realm.*
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
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.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() {

	companion object {

		// Create a new instance
		fun newInstance(realm: Realm): Filter {
			val filter = 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> {
		    var realmQuery = realm.where<T>()
		    query.conditions.forEach {
			    realmQuery = it.queryWith(realmQuery)
		    }
		    sortField?.let {
			    realmQuery.sort(it)
		    }
		    Timber.d(">>> Filter query: ${realmQuery.description}")
		    return realmQuery.findAll()
	    }
    }

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

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

	// the number of use of the queryWith,
	// 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<QueryCondition>) {
        println("list of querys saving: ${filterConditionRows.map { it.id }}")
        println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
        filterConditionRows
			.map {
				it.filterSectionRow
			}
			.distinct()
			.forEach { filterName->
				filterConditionRows
					.filter {
						it.filterSectionRow == filterName
					}
					.apply {

                        println("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
                        }
                        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 }
        println("list of sections $sections")
        val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
        println("list of savedSections $savedSections")
        return savedSections.size
	}

	fun contains(filterElementRow: QueryCondition): Boolean {
        println("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
        println("list of contains ${filterElementRow.id}")
		val contained = filterConditions.flatMap{ it.queryCondition.id }.contains(filterElementRow.id.first())
        println("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(): RealmResults<T> {
		var realmQuery = realm.where<T>()
		this.filterConditions.map {
			it.queryCondition
		}.forEach {
			realmQuery = it.queryWith(realmQuery)
		}

		return realmQuery.findAll()
	}

}
