package net.pokeranalytics.android.filter

import androidx.test.ext.junit.runners.AndroidJUnit4
import io.realm.RealmList
import net.pokeranalytics.android.model.filter.FilterManager
import net.pokeranalytics.android.model.filter.SessionFilterable
import net.pokeranalytics.android.model.realm.*
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*

@RunWith(AndroidJUnit4::class)
class SessionFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {

    @Test
    fun testCashFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        Session.testInstance(100.0, false, Date(), 1)
        Session.testInstance(100.0, true, Date(), 1)
        realm.commitTransaction()

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(SessionFilterable.CASH)
        )

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(Session.Type.CASH_GAME.ordinal, (this as Session).type)
        }
    }

    @Test
    fun testTournamentFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        Session.testInstance(100.0, false, Date(), 1)
        Session.testInstance(100.0, true, Date(), 1)
        realm.commitTransaction()

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(SessionFilterable.TOURNAMENT)
        )

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(Session.Type.TOURNAMENT.ordinal, (this as Session).type)
        }
    }

    @Test
    fun testLiveFilter() {

        val realm = this.mockRealm

        realm.beginTransaction()
        val b1 = realm.createObject(Bankroll::class.java, "1")
        val b2 = realm.createObject(Bankroll::class.java, "2")
        b1.live = true
        b2.live = false

        Session.testInstance(100.0, false, Date(), 1, b1)
        Session.testInstance(100.0, true, Date(), 1, b2)
        realm.commitTransaction()

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(SessionFilterable.LIVE)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).bankroll?.run {
            Assert.assertEquals(true, this.live)
        }
    }

    @Test
    fun testOnlineFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val b1 = realm.createObject(Bankroll::class.java, "1")
        val b2 = realm.createObject(Bankroll::class.java, "2")
        b1.live = false
        b2.live = true

        Session.testInstance(100.0, false, Date(), 1, b1)
        Session.testInstance(100.0, true, Date(), 1, b2)
        realm.commitTransaction()

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(SessionFilterable.ONLINE)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).bankroll?.run {
            Assert.assertEquals(false, this.live)
        }
    }

    @Test
    fun testBankrollFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val b1 = realm.createObject(Bankroll::class.java, "1")
        val b2 = realm.createObject(Bankroll::class.java, "2")
        b1.live = false
        b2.live = true
        Session.testInstance(bankroll = b1)
        Session.testInstance(bankroll = b2)
        realm.commitTransaction()

        val filter = SessionFilterable.BANKROLL
        filter.valueMap = mapOf("ids" to arrayOf(b1.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).bankroll?.run {
            Assert.assertEquals(b1.id, this.id)
        }
    }

    @Test
    fun testMultipleBankrollFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val b1 = realm.createObject(Bankroll::class.java, "1")
        val b2 = realm.createObject(Bankroll::class.java, "2")
        val b3 = realm.createObject(Bankroll::class.java, "3")
        Session.testInstance(bankroll = b1)
        Session.testInstance(bankroll = b2)
        Session.testInstance(bankroll = b3)
        Session.testInstance(bankroll = b1)
        Session.testInstance(bankroll = b2)
        Session.testInstance(bankroll = b3)
        Session.testInstance(bankroll = b1)
        Session.testInstance(bankroll = b2)
        Session.testInstance(bankroll = b3)
        realm.commitTransaction()

        val filter = SessionFilterable.BANKROLL
        filter.valueMap = mapOf("ids" to arrayOf(b1.id, b2.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(6, sessions.size)

        val result = arrayListOf(b1.id, b2.id)

        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).bankroll?.id))
        }
    }

    @Test
    fun testGameFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val g1 = realm.createObject(Game::class.java, "1")
        val g2 = realm.createObject(Game::class.java, "2")
        Session.testInstance(game = g1)
        Session.testInstance(game = g2)
        realm.commitTransaction()

        val filter = SessionFilterable.GAME
        filter.valueMap = mapOf("ids" to arrayOf(g2.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).game?.run {
            Assert.assertEquals(g2.id, this.id)
        }
    }

    @Test
    fun testMultipleGameFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val g1 = realm.createObject(Game::class.java, "1")
        val g2 = realm.createObject(Game::class.java, "2")
        val g3 = realm.createObject(Game::class.java, "3")
        Session.testInstance(game = g1)
        Session.testInstance(game = g2)
        Session.testInstance(game = g3)
        Session.testInstance(game = g1)
        Session.testInstance(game = g2)
        Session.testInstance(game = g3)
        Session.testInstance(game = g1)
        Session.testInstance(game = g2)
        Session.testInstance(game = g3)
        realm.commitTransaction()

        val filter = SessionFilterable.GAME
        filter.valueMap = mapOf("ids" to arrayOf(g2.id, g3.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(6, sessions.size)

        val result = arrayListOf(g2.id, g3.id)

        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).game?.id))
        }
    }

    @Test
    fun testLocationFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val l1 = realm.createObject(Location::class.java, "1")
        val l2 = realm.createObject(Location::class.java, "2")
        Session.testInstance(location = l1)
        Session.testInstance(location = l2)
        realm.commitTransaction()

        val filter = SessionFilterable.LOCATION
        filter.valueMap = mapOf("ids" to arrayOf(l1.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).location?.run {
            Assert.assertEquals(l1.id, this.id)
        }
    }

    @Test
    fun testMultipleLocationFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val l1 = realm.createObject(Location::class.java, "1")
        val l2 = realm.createObject(Location::class.java, "2")
        val l3 = realm.createObject(Location::class.java, "3")
        Session.testInstance(location = l1)
        Session.testInstance(location = l2)
        Session.testInstance(location = l3)
        Session.testInstance(location = l1)
        Session.testInstance(location = l2)
        Session.testInstance(location = l3)
        Session.testInstance(location = l1)
        Session.testInstance(location = l2)
        Session.testInstance(location = l3)
        realm.commitTransaction()

        val filter = SessionFilterable.LOCATION
        filter.valueMap = mapOf("ids" to arrayOf(l1.id, l3.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(6, sessions.size)

        val result = arrayListOf(l1.id, l3.id)

        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).location?.id))
        }
    }

    @Test
    fun testTournamentNameFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val t1 = realm.createObject(TournamentName::class.java, "1")
        val t2 = realm.createObject(TournamentName::class.java, "2")
        Session.testInstance(tournamentName = t1)
        Session.testInstance(tournamentName = t2)
        realm.commitTransaction()

        val filter = SessionFilterable.TOURNAMENT_NAME
        filter.valueMap = mapOf("ids" to arrayOf(t1.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).tournamentName?.run {
            Assert.assertEquals(t1.id, this.id)
        }
    }

    @Test
    fun testMultipleTournamentNameFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val t1 = realm.createObject(TournamentName::class.java, "1")
        val t2 = realm.createObject(TournamentName::class.java, "2")
        val t3 = realm.createObject(TournamentName::class.java, "3")
        Session.testInstance(tournamentName = t1)
        Session.testInstance(tournamentName = t2)
        Session.testInstance(tournamentName = t3)
        Session.testInstance(tournamentName = t1)
        Session.testInstance(tournamentName = t2)
        Session.testInstance(tournamentName = t3)
        Session.testInstance(tournamentName = t1)
        Session.testInstance(tournamentName = t2)
        Session.testInstance(tournamentName = t3)
        realm.commitTransaction()

        val filter = SessionFilterable.TOURNAMENT_NAME
        filter.valueMap = mapOf("ids" to arrayOf(t1.id, t2.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(6, sessions.size)

        val result = arrayListOf(t1.id, t2.id)

        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).tournamentName?.id))
        }
    }

    @Test
    fun testAllTournamentFeatureFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val t1 = realm.createObject(TournamentFeature::class.java, "1")
        val t2 = realm.createObject(TournamentFeature::class.java, "2")
        val t3 = realm.createObject(TournamentFeature::class.java, "3")
        val t4 = realm.createObject(TournamentFeature::class.java, "4")
        Session.testInstance(tournamentFeatures = RealmList(t1,t2))
        Session.testInstance(tournamentFeatures = RealmList(t2,t3))
        Session.testInstance(tournamentFeatures = RealmList(t3,t4))
        val s = Session.testInstance(tournamentFeatures = RealmList(t1,t2,t3,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t3))
        Session.testInstance(tournamentFeatures = RealmList(t2,t4, t3))
        Session.testInstance(tournamentFeatures = RealmList(t1))
        realm.commitTransaction()

        val filter = SessionFilterable.ALL_TOURNAMENT_FEATURES
        filter.valueMap = mapOf("ids" to arrayOf(t1.id, t2.id, t3.id, t4.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(1, sessions.size)
        (sessions[0] as Session).run {
            Assert.assertEquals(s.id, this.id)
        }
    }

    @Test
    fun testAnyTournamentFeatureFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val t1 = realm.createObject(TournamentFeature::class.java, "1")
        val t2 = realm.createObject(TournamentFeature::class.java, "2")
        val t3 = realm.createObject(TournamentFeature::class.java, "3")
        val t4 = realm.createObject(TournamentFeature::class.java, "4")
        Session.testInstance(tournamentFeatures = RealmList(t1,t2))
        Session.testInstance(tournamentFeatures = RealmList(t2,t3))
        Session.testInstance(tournamentFeatures = RealmList(t3,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t2,t3,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t3))
        Session.testInstance(tournamentFeatures = RealmList(t2,t4, t3))
        Session.testInstance(tournamentFeatures = RealmList(t1))
        realm.commitTransaction()

        val filter = SessionFilterable.ANY_TOURNAMENT_FEATURES
        filter.valueMap = mapOf("ids" to arrayOf(t1.id, t2.id, t3.id, t4.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(8, sessions.size)
    }

    @Test
    fun testSingleAnyTournamentFeatureFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val t1 = realm.createObject(TournamentFeature::class.java, "1")
        val t2 = realm.createObject(TournamentFeature::class.java, "2")
        val t3 = realm.createObject(TournamentFeature::class.java, "3")
        val t4 = realm.createObject(TournamentFeature::class.java, "4")
        val s1 = Session.testInstance(tournamentFeatures = RealmList(t1,t2))
        val s2 = Session.testInstance(tournamentFeatures = RealmList(t2,t3))
        Session.testInstance(tournamentFeatures = RealmList(t3,t4))
        val s3 = Session.testInstance(tournamentFeatures = RealmList(t1,t2,t3,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t4))
        Session.testInstance(tournamentFeatures = RealmList(t1,t3))
        val s4 = Session.testInstance(tournamentFeatures = RealmList(t2,t4, t3))
        Session.testInstance(tournamentFeatures = RealmList(t1))
        realm.commitTransaction()

        val filter = SessionFilterable.ANY_TOURNAMENT_FEATURES
        filter.valueMap = mapOf("ids" to arrayOf(t2.id))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        val result = arrayListOf(s1.id, s2.id, s3.id, s4.id)

        Assert.assertEquals(4, sessions.size)
        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).id))
        }
    }

    @Test
    fun testTableSizeFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(tableSize = 2)
        val s2 = Session.testInstance(tableSize = 4)
        Session.testInstance(tableSize = 9)
        Session.testInstance(tableSize = 10)
        realm.commitTransaction()

        val filter = SessionFilterable.TABLE_SIZE
        filter.valueMap = mapOf("values" to arrayOf(2,4))

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(2, sessions.size)

        val result = arrayListOf(s1.id, s2.id)
        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).id))
        }
    }

    @Test
    fun testMoreThanNetResultFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        Session.testInstance(netResult = 200.0)
        val s1 = Session.testInstance(netResult = 500.0)
        Session.testInstance(netResult = 50.0)
        val s2 = Session.testInstance(netResult = 570.0)
        realm.commitTransaction()

        val filter = SessionFilterable.MORE_THAN_NET_RESULT
        filter.valueMap = mapOf("value" to 204.0)

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(2, sessions.size)

        val result = arrayListOf(s1.id, s2.id)
        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).id))
        }
    }

    @Test
    fun testLessThanNetResultFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(netResult = 200.0)
        val s2 = Session.testInstance(netResult = 500.0)
        val s3 = Session.testInstance(netResult = 50.0)
        Session.testInstance(netResult = 570.0)
        realm.commitTransaction()

        val filter = SessionFilterable.LESS_THAN_NET_RESULT
        filter.valueMap = mapOf("value" to 540.0)

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filter)
        )

        Assert.assertEquals(3, sessions.size)

        val result = arrayListOf(s1.id, s2.id, s3.id)
        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).id))
        }
    }

    @Test
    fun tesNetResultFilter() {
        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(netResult = 200.0)
        Session.testInstance(netResult = 500.0)
        Session.testInstance(netResult = 50.0)
        Session.testInstance(netResult = 570.0)
        realm.commitTransaction()

        val filterMore = SessionFilterable.MORE_THAN_NET_RESULT
        filterMore.valueMap = mapOf("value" to 200.0)

        val filterLess = SessionFilterable.LESS_THAN_NET_RESULT
        filterLess.valueMap = mapOf("value" to 400.0)

        val sessions = FilterManager().filter(
            realm,
            Session::class.java,
            arrayListOf(filterMore, filterLess)
        )

        Assert.assertEquals(1, sessions.size)

        val result = arrayListOf(s1.id)
        sessions.forEach {
            Assert.assertTrue(result.contains((it as Session).id))
        }
    }
}