package net.pokeranalytics.android.model.filter

import io.realm.RealmObject
import io.realm.RealmQuery
import net.pokeranalytics.android.exceptions.FilterValueMapException
import net.pokeranalytics.android.model.filter.interfaces.Filterable
import net.pokeranalytics.android.model.realm.Session

enum class SessionFilterable(private var fieldName:String? = null, private var subType:SubType? = null): Filterable {
    LIVE,
    CASH,
    ONLINE,
    TOURNAMENT,
    BANKROLL("bankroll.id"),
    GAME("game.id"),
    TOURNAMENT_NAME("tournamentName.id"),
    ANY_TOURNAMENT_FEATURES,
    ALL_TOURNAMENT_FEATURES,
    LOCATION("location.id"),
    LIMIT("limit"),
    TABLE_SIZE("tableSize"),
    TOURNAMENT_TYPE("tournamentType"),
    BLINDS,
    MORE_NUMBER_OF_TABLE(Field.NUMBER_OF_TABLE.fieldName, SubType.MORE),
    LESS_NUMBER_OF_TABLE(Field.NUMBER_OF_TABLE.fieldName, SubType.LESS),
    BETWEEN_NUMBER_OF_TABLE(Field.NUMBER_OF_TABLE.fieldName, SubType.BETWEEN),
    MORE_THAN_NET_RESULT(Field.NET_RESULT.fieldName, SubType.MORE),
    LESS_THAN_NET_RESULT(Field.NET_RESULT.fieldName, SubType.LESS),
    MORE_THAN_BUY_IN(Field.BUY_IN.fieldName, SubType.MORE),
    LESS_THAN_BUY_IN(Field.BUY_IN.fieldName, SubType.LESS),
    MORE_THAN_CASH_OUT(Field.CASH_OUT.fieldName, SubType.MORE),
    LESS_THAN_CASH_OUT(Field.CASH_OUT.fieldName, SubType.LESS),
    MORE_THAN_TIPS(Field.TIPS.fieldName, SubType.MORE),
    LESS_THAN_TIPS(Field.TIPS.fieldName, SubType.LESS),
    MORE_THAN_NUMBER_OF_PLAYER(Field.NUMBER_OF_PLAYER.fieldName, SubType.MORE),
    LESS_THAN_NUMBER_OF_PLAYER(Field.NUMBER_OF_PLAYER.fieldName, SubType.LESS),
    BETWEEN_NUMBER_OF_PLAYER(Field.NUMBER_OF_PLAYER.fieldName, SubType.BETWEEN),
    MORE_THAN_TOURNAMENT_FEE(Field.NET_RESULT.fieldName, SubType.MORE),
    LESS_THAN_TOURNAMENT_FEE(Field.NET_RESULT.fieldName, SubType.LESS),
    BETWEEN_TOURNAMENT_FEE(Field.TOURNAMENT_FEE.fieldName, SubType.BETWEEN),
    ;

    enum class SubType {
        BETWEEN,
        MORE,
        LESS;
    }

    private enum class Field(var fieldName:String) {
        LIVE("bankroll.live"),
        CASH("type"),
        CURRENCY("bankroll.currency"),
        CURRENCY_CODE("bankroll.currency.code"),
        BIG_BLIND("cgBigBlind"),
        SMALL_BLIND("cgSmallBlind"),
        COMMENT("comment"),
        TOURNAMENT_FEATURES("tournamentFeatures.id"),
        NET_RESULT("computableResult.ratedNet"),
        BUY_IN("result.buyin"),
        CASH_OUT("result.cashout"),
        TIPS("result.tips"),
        NUMBER_OF_TABLE("numberOfTable"),
        NUMBER_OF_PLAYER("tournamentNumberOfPlayers"),
        TOURNAMENT_FEE("tournamentEntryFee"),
        ;
    }

    override var valueMap : Map<String, Any?>? = null

    override val filterValuesExceptedKeys : Array<String>?
        get() {
            this.subType?.let {
                return when (it) {
                    SubType.BETWEEN -> arrayOf("leftValue", "rightValue")
                    else -> arrayOf("value")
                }
            }
            return when (this) {
                BANKROLL, GAME, LOCATION, ANY_TOURNAMENT_FEATURES, ALL_TOURNAMENT_FEATURES -> arrayOf("ids")
                LIMIT, TOURNAMENT_TYPE, TABLE_SIZE -> arrayOf("values")
                BLINDS -> arrayOf("map")
                else -> null
            }
        }

    override fun filter(realmQuery: RealmQuery<out RealmObject>): RealmQuery<out RealmObject> {

        this.subType?.let {subType ->
            this.fieldName?.let {
                return when (subType) {
                    SubType.LESS -> {
                        val value: Double by filterValues
                        println("filter test less")
                        realmQuery.lessThanOrEqualTo(it, value)
                    }
                    SubType.MORE -> {
                        println("filter test more")
                        val value: Double by filterValues
                        realmQuery.greaterThanOrEqualTo(it, value)
                    }
                    SubType.BETWEEN -> {
                        val leftValue: Double by filterValues
                        val rightValue: Double by filterValues
                        realmQuery.between(it, leftValue, rightValue)
                    }
                }
            } ?: run {
                throw FilterValueMapException("fieldName is missing")
            }
        }

        return when (this) {
            LIVE -> realmQuery.equalTo(Field.LIVE.fieldName, true)
            CASH -> realmQuery.equalTo(Field.CASH.fieldName, Session.Type.CASH_GAME.ordinal)
            ONLINE -> LIVE.filter(realmQuery.not())
            TOURNAMENT -> CASH.filter(realmQuery.not())
            ALL_TOURNAMENT_FEATURES -> {
                val ids : Array<String> by filterValues
                ids.forEach {
                    realmQuery.equalTo(Field.TOURNAMENT_FEATURES.fieldName, it)
                }
                realmQuery
            }
            ANY_TOURNAMENT_FEATURES -> {
                val ids : Array<String> by filterValues
                realmQuery.`in`(Field.TOURNAMENT_FEATURES.fieldName, ids)
            }
            BANKROLL, GAME, LOCATION, TOURNAMENT_NAME -> {
                val ids : Array<String> by filterValues
                this.fieldName?.let {
                    realmQuery.`in`(it, ids)
                } ?: run {
                    throw FilterValueMapException("fieldName is missing")
                }
            }
            LIMIT, TOURNAMENT_TYPE, TABLE_SIZE -> {
                val values : Array<Int?>? by filterValues
                this.fieldName?.let {
                    realmQuery.`in`(it, values)
                } ?: run {
                    throw FilterValueMapException("fieldName is missing")
                }
            }
            BLINDS -> {
                val map : Array<Map<String,Any?>> by filterValues
                val expectedSubKeys = arrayOf("sb", "bb", "code")
                map.forEachIndexed { index, subMap ->
                    val missingKeys = subMap.keys.filter { !expectedSubKeys.contains(it) }
                    if (subMap.keys.size == expectedSubKeys.size && missingKeys.isNotEmpty()) {
                        throw FilterValueMapException("subValueMap does not contain $missingKeys")
                    }

                    val sb : Double? by subMap
                    val bb : Double? by subMap
                    val code : String? by subMap

                    realmQuery
                        .beginGroup()

                    sb?.let {
                        realmQuery
                            .equalTo(Field.SMALL_BLIND.fieldName, sb)
                            .and()
                    }

                    realmQuery
                        .equalTo(Field.BIG_BLIND.fieldName, bb)
                        .and()

                    code?.let {
                        realmQuery.equalTo(Field.CURRENCY_CODE.fieldName, code)
                    } ?: run {
                        realmQuery.isNull(Field.CURRENCY_CODE.fieldName)
                    }

                    realmQuery.endGroup()

                    if (index < map.size - 1) {
                        realmQuery.or()
                    }
                }
                realmQuery
            }
            else -> {
                realmQuery
            }
        }
    }
}