package net.pokeranalytics.android.unitTests.filter

import androidx.test.ext.junit.runners.AndroidJUnit4
import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.FilterCondition
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow
import net.pokeranalytics.android.util.extensions.hourMinute
import net.pokeranalytics.android.util.extensions.startOfDay
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*

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

    @Test
    fun testDayOfWeekFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        val s1 = Session.testInstance(100.0, false, cal.time)
        cal.add(Calendar.DAY_OF_MONTH, 1) // adds one hour
        Session.testInstance(100.0, true, cal.time)
        realm.commitTransaction()

        val filter = QueryCondition.AnyDayOfWeek()
        cal.time = s1.startDate

        val filterElementRow = QueryCondition.AnyDayOfWeek().apply { listOfValues = arrayListOf(cal.get(Calendar.DAY_OF_WEEK)) }
        filterElementRow.filterSectionRow = FilterSectionRow.DynamicDate
        val filterElement = FilterCondition(arrayListOf(filterElementRow))
        filter.updateValueBy(filterElement)

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testMonthFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        val s1 = Session.testInstance(100.0, false, cal.time)
        cal.add(Calendar.MONTH, 1)
        
        Session.testInstance(100.0, true, cal.time)
        realm.commitTransaction()

        val filter = QueryCondition.AnyMonthOfYear()
        cal.time = s1.startDate
        val filterElementRow = QueryCondition.AnyMonthOfYear().apply { listOfValues = arrayListOf(cal.get(Calendar.MONTH)) }

        filterElementRow.filterSectionRow = FilterSectionRow.DynamicDate
        val filterElement = FilterCondition(arrayListOf(filterElementRow))
        filter.updateValueBy(filterElement)

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testYearFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        val s1 = Session.testInstance(100.0, false, cal.time)
        cal.add(Calendar.YEAR, 1)
        
        Session.testInstance(100.0, true, cal.time)
        realm.commitTransaction()

        val filter = QueryCondition.AnyYear()
        cal.time = s1.startDate
        val filterElementRow = QueryCondition.AnyYear().apply { listOfValues = arrayListOf(cal.get(Calendar.YEAR)) }
        filterElementRow.filterSectionRow = FilterSectionRow.DynamicDate
        val filterElement = FilterCondition(arrayListOf(filterElementRow))
        filter.updateValueBy(filterElement)

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }


    @Test
    fun testWeekEndFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY)
        val s1 = Session.testInstance(100.0, false, cal.time)
        cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY)
        
        Session.testInstance(100.0, true, cal.time)
        realm.commitTransaction()

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsWeekEnd))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testWeekDayFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance()
        cal.time = Date()

        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
        val s1 = Session.testInstance(100.0, false, cal.time)

        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY)
        Session.testInstance(100.0, true, cal.time)

        realm.commitTransaction()

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsWeekDay))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testTodayFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val s1 = Session.testInstance(100.0, false)

        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.add(Calendar.HOUR_OF_DAY, -72)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsToday))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, this.id)
        }
    }

    @Test
    fun testTodayNoonFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance()
        cal.time = Date().startOfDay()
        val s1 = Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.HOUR_OF_DAY, -72)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsToday))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, this.id)
        }
    }


    @Test
    fun testYesterdayFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.add(Calendar.HOUR_OF_DAY, -24)
        val s1 = Session.testInstance(100.0, false, cal.time)

        Session.testInstance(100.0, false)

        cal.add(Calendar.HOUR_OF_DAY, -72)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.WasYesterday))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, this.id)
        }
    }

    @Test
    fun testYesterdayNoonFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.add(Calendar.HOUR_OF_DAY, -24)
        val s1 = Session.testInstance(100.0, false, cal.time.startOfDay())

        Session.testInstance(100.0, false)

        cal.add(Calendar.HOUR_OF_DAY, -72)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.WasYesterday))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, this.id)
        }
    }

    @Test
    fun testTodayAndYesterdayFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.add(Calendar.HOUR_OF_DAY, -24)
        val s1 = Session.testInstance(100.0, false, cal.time)

        val s2 = Session.testInstance(100.0, false)

        cal.add(Calendar.HOUR_OF_DAY, -72)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.WasTodayAndYesterday))

        Assert.assertEquals(2, sessions.size)
        Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2)))
    }

    @Test
    fun testThisYear() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(100.0, false)

        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.add(Calendar.HOUR_OF_DAY, -24)
        val s2 = Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.HOUR_OF_DAY, -72)
        val s3 = Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.YEAR, -4)
        Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.YEAR, -1)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 5)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.DuringThisYear))

        Assert.assertEquals(3, sessions.size)
        Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2, s3)))
    }

    @Test
    fun testThisMonth() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(100.0, false)

        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.set(Calendar.DAY_OF_MONTH, 1)
        cal.time = cal.time.startOfDay()
        val s2 = Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.HOUR_OF_DAY, -1)
        Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.MONTH, -1)
        Session.testInstance(100.0, false, cal.time)

        cal.add(Calendar.YEAR, -1)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 5)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.DuringThisMonth))

        Assert.assertEquals(2, sessions.size)
        Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2)))
    }

    @Test
    fun testThisWeek() {

        val realm = this.mockRealm
        realm.beginTransaction()
        val s1 = Session.testInstance(100.0, false)

        val cal = Calendar.getInstance()
        cal.time = Date()
        cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1)
        cal.time = cal.time.startOfDay()
        cal.add(Calendar.HOUR_OF_DAY, -1)
        Session.testInstance(100.0, false, cal.time)

        realm.commitTransaction()

        Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2)

        val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.DuringThisWeek))

        Assert.assertEquals(1, sessions.size)
        Assert.assertTrue(sessions.containsAll(arrayListOf(s1)))
    }

    @Test
    fun testStartedFomDateFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        
        Session.testInstance(100.0, false, cal.time, 1)

        cal.add(Calendar.DAY_OF_YEAR, 2) // adds one hour
        val s2 = Session.testInstance(100.0, true, cal.time, 1)
        realm.commitTransaction()

        val filter = QueryCondition.StartedFromDate()
        val filterElementRow = QueryCondition.StartedFromDate().apply { singleValue = s2.startDate!!}
        filterElementRow.filterSectionRow = FilterSectionRow.FixedDate
        filter.updateValueBy(FilterCondition(arrayListOf(filterElementRow)))

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s2.id, (this).id)
        }
    }

    @Test
    fun testStartedToDateFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        val s1 = Session.testInstance(100.0, false, cal.time, 1)

        cal.add(Calendar.DAY_OF_YEAR, 2) // adds one hour
        Session.testInstance(100.0, true, cal.time, 1)

        realm.commitTransaction()

        val filter = QueryCondition.StartedToDate()
        val filterElementRow = QueryCondition.StartedToDate().apply { singleValue = s1.startDate!! }
        filterElementRow.filterSectionRow = FilterSectionRow.FixedDate
        filter.updateValueBy(FilterCondition(arrayListOf(filterElementRow)))

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testEndedFomDateFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        
        Session.testInstance(100.0, false, cal.time, 1)

        cal.add(Calendar.DAY_OF_YEAR, 2) // adds one hour
        val s2 = Session.testInstance(100.0, true, cal.time, 1)

        realm.commitTransaction()

        val filter = QueryCondition.EndedFromDate()
        val filterElementRow = QueryCondition.EndedFromDate().apply { singleValue = s2.endDate() }
        filterElementRow.filterSectionRow = FilterSectionRow.FixedDate
        filter.updateValueBy(FilterCondition(arrayListOf(filterElementRow)))

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s2.id, (this).id)
        }
    }

    @Test
    fun testEndedToDateFilter() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        val s1 = Session.testInstance(100.0, false, cal.time, 1)

        cal.add(Calendar.DAY_OF_YEAR, 2) // adds one hour
        
        Session.testInstance(100.0, true, cal.time, 1)
        realm.commitTransaction()


        val filter = QueryCondition.EndedToDate()
        val filterElementRow = QueryCondition.EndedToDate().apply { singleValue = s1.endDate() }
        filterElementRow.filterSectionRow = FilterSectionRow.FixedDate
        filter.updateValueBy(FilterCondition(arrayListOf(filterElementRow)))

        val sessions = Filter.queryOn<Session>(realm, Query(filter))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s1.id, (this).id)
        }
    }

    @Test
    fun testFomTimeToTime() {

        val realm = this.mockRealm
        realm.beginTransaction()

        val cal = Calendar.getInstance() // creates calendar
        cal.time = Date() // sets calendar time/date
        cal.set(Calendar.HOUR_OF_DAY, 14) // adds one hour

        println("<<<<< s1 ${cal.hourMinute()}")
        val s1 = Session.testInstance(100.0, false, cal.time, 1)
        println("<<<<< s1 ${cal.hourMinute()}")
        cal.add(Calendar.HOUR_OF_DAY, 2) // adds one hour
        println("<<<<< s2 ${cal.hourMinute()}")
        val s2 = Session.testInstance(100.0, true, cal.time, 1)
        println("<<<<< s2 ${cal.hourMinute()}")

        cal.set(Calendar.HOUR_OF_DAY, 23) // adds one hour
        println("<<<<< s3 ${cal.hourMinute()}")
        val s3 = Session.testInstance(100.0, true, cal.time, 2)
        println("<<<<< s3 ${cal.hourMinute()}")

        realm.commitTransaction()

        val filter = QueryCondition.StartedFromTime(s2.startDate!!)
        val filter2 = QueryCondition.EndedToTime(s2.endDate!!)
        val sessions = Filter.queryOn<Session>(realm, Query(filter, filter2))

        Assert.assertEquals(1, sessions.size)
        sessions[0]?.run {
            Assert.assertEquals(s2.id, (this).id)
        }
    }
}