package net.pokeranalytics.android.ui.view.rowrepresentable

import io.realm.Realm
import io.realm.Sort
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow.*
import net.pokeranalytics.android.util.extensions.endOfDay
import net.pokeranalytics.android.util.extensions.startOfDay
import java.text.DateFormatSymbols
import java.util.*

enum class FilterSectionRow(override val resId: Int?) : RowRepresentable {
	CASH_TOURNAMENT(net.pokeranalytics.android.R.string.cash_or_tournament),
	LIVE_ONLINE(net.pokeranalytics.android.R.string.live_or_online),
	GAME(net.pokeranalytics.android.R.string.games),
	LIMIT_TYPE(net.pokeranalytics.android.R.string.limits),
	TABLE_SIZE(net.pokeranalytics.android.R.string.table_sizes),
	DYNAMIC_DATE(net.pokeranalytics.android.R.string.dynamic_date),
	FIXED_DATE(net.pokeranalytics.android.R.string.fixed_date),
	DURATION(net.pokeranalytics.android.R.string.duration),
	YEAR(net.pokeranalytics.android.R.string.year),
	WEEKDAYS_OR_WEEKEND(net.pokeranalytics.android.R.string.weekdays_or_weekend),
	DAY_OF_WEEK(net.pokeranalytics.android.R.string.day_of_the_week),
	MONTH_OF_YEAR(net.pokeranalytics.android.R.string.month_of_the_year),
	SESSION_DURATION(net.pokeranalytics.android.R.string.session_duration),
	RANGE(net.pokeranalytics.android.R.string.hour_slot),
	SESSIONS(R.string.sessions),
	BLIND(net.pokeranalytics.android.R.string.blinds),
	CASH_RE_BUY_COUNT(net.pokeranalytics.android.R.string.rebuy_count),
	TOURNAMENT_TYPE(net.pokeranalytics.android.R.string.tournament_types),
	TOURNAMENT_NAME(net.pokeranalytics.android.R.string.tournament_name),
	TOURNAMENT_FEATURE(net.pokeranalytics.android.R.string.tournament_feature),
	COMPLETION_PERCENTAGE(net.pokeranalytics.android.R.string.tournament_completion_percentage_interval),
	PLACE(net.pokeranalytics.android.R.string.final_position),
	PLAYERS_COUNT(net.pokeranalytics.android.R.string.players_count),
	TOURNAMENT_RE_BUY_COUNT(net.pokeranalytics.android.R.string.rebuy_count),
	BUY_IN(net.pokeranalytics.android.R.string.buyin),
	MULTI_TABLING(net.pokeranalytics.android.R.string.multi_tabling),
	VALUE(net.pokeranalytics.android.R.string.value),
	LOCATION(net.pokeranalytics.android.R.string.locations),
	BANKROLL(net.pokeranalytics.android.R.string.bankrolls),
	NUMBER_OF_PLAYERS(net.pokeranalytics.android.R.string.number_of_players),
	MULTI_PLAYER(net.pokeranalytics.android.R.string.multiplayer),
	;

	private enum class SelectionType {
		SINGLE,
		MULTIPLE,
	}

	override val viewType: Int = RowViewType.HEADER_TITLE.ordinal

	val allowMultiSelection: Boolean
		get() = (this.selectionType == SelectionType.MULTIPLE)

	val filterElements: List<RowRepresentable>
		get() {

			val data = arrayListOf<RowRepresentable>().apply {
				this.addAll(
					when (this@FilterSectionRow) {

						// General
						CASH_TOURNAMENT -> arrayListOf(Cash, Tournament)
						LIVE_ONLINE -> arrayListOf(Live, Online)
						GAME -> {
							val games = arrayListOf<FilterElementRow.Game>()
							val realm = Realm.getDefaultInstance()
							LiveData.GAME.items(realm).forEach {
								val game = Game(it as net.pokeranalytics.android.model.realm.Game)
								games.add(game)
							}
							realm.close()
							games
						}
						LIMIT_TYPE -> {
							val limits = arrayListOf<FilterElementRow.Limit>()
							net.pokeranalytics.android.model.Limit.values().forEach {
								limits.add(Limit(it))
							}
							limits
						}
						TABLE_SIZE -> {
							val tableSizes = arrayListOf<FilterElementRow.TableSize>()
							val realm = Realm.getDefaultInstance()
							val distinctTableSizes =
								realm.where<Session>().distinct("tableSize").findAll().sort("tableSize", Sort.ASCENDING)
							distinctTableSizes.forEach { session ->
								session.tableSize?.let { tableSize ->
									tableSizes.add(TableSize(net.pokeranalytics.android.model.TableSize(tableSize)))
								}
							}
							realm.close()
							tableSizes
						}

						// Date
						DYNAMIC_DATE -> arrayListOf(
							Today,
							Yesterday,
							TodayAndYesterday,
							CurrentWeek,
							CurrentMonth,
							CurrentYear
						)
						FIXED_DATE -> arrayListOf(From, To)
						DURATION -> arrayListOf(PastDays(0))
						YEAR -> {
							val years = arrayListOf<FilterElementRow.Year>()
							val realm = Realm.getDefaultInstance()
							val distinctYears =
								realm.where<Session>().distinct("year").findAll().sort("year", Sort.DESCENDING)
							distinctYears.forEach { session ->
								session.year?.let { year ->
									years.add(Year(year))
								}
							}
							realm.close()
							years
						}
						WEEKDAYS_OR_WEEKEND -> arrayListOf(Weekday, Weekend)
						DAY_OF_WEEK -> {
							val daysOfWeek = arrayListOf<FilterElementRow.Day>()
							DateFormatSymbols.getInstance(Locale.getDefault()).weekdays.forEachIndexed { index, day ->
								if (day.isNotEmpty()) {
									daysOfWeek.add(Day(index))
								}
							}
							daysOfWeek
						}
						MONTH_OF_YEAR -> {
							val months = arrayListOf<FilterElementRow.Month>()
							DateFormatSymbols.getInstance(Locale.getDefault()).months.forEachIndexed { index, month ->
								if (month.isNotEmpty()) {
									months.add(Month(index))
								}
							}
							months
						}

						// Duration
						SESSION_DURATION -> arrayListOf(
							DurationMoreThan as FilterElementRow,
							DurationLessThan as FilterElementRow
						)
						RANGE -> {
							val fromTime = FromTime
							fromTime.dateValue = Date().startOfDay()
							val toTime = ToTime
							toTime.dateValue = Date().endOfDay()
							arrayListOf(fromTime, toTime)
						}

						// Sessions
						SESSIONS -> arrayListOf(LastGames(0), LastSessions(0))

						// Cash
						BLIND -> {
							val blinds = arrayListOf<Blind>()
							val realm = Realm.getDefaultInstance()
							realm.where<Session>().distinct("blind", "bankroll.currency.code").findAll().sort("cgSmallBlind", Sort.ASCENDING).map {
                                it.blinds?.let { stake ->
                                    blinds.add(Blind(stake, it.hasDefaultCurrency))
                                }
                            }
							realm.close()
                            blinds
                        }
						CASH_RE_BUY_COUNT -> arrayListOf(
							ReBuyMoreThan as FilterElementRow,
							ReBuyLessThan as FilterElementRow
						)

						// Tournament
						TOURNAMENT_TYPE -> arrayListOf()
						COMPLETION_PERCENTAGE -> arrayListOf()
						PLACE -> arrayListOf()
						PLAYERS_COUNT -> arrayListOf()
						TOURNAMENT_RE_BUY_COUNT -> arrayListOf()
						BUY_IN -> arrayListOf()


						TOURNAMENT_NAME -> arrayListOf()
						TOURNAMENT_FEATURE -> arrayListOf()
						LOCATION -> arrayListOf()
						BANKROLL -> arrayListOf()
						MULTI_TABLING -> arrayListOf()
						NUMBER_OF_PLAYERS -> arrayListOf()
						MULTI_PLAYER -> arrayListOf()

						VALUE -> arrayListOf()

					}.apply {
						this.forEach {
							it.filterSectionRow = this@FilterSectionRow
						}
					}
				)
			}

			// Add the section row only if we have data for this section
			if (data.isNotEmpty()) {
				data.add(0, this@FilterSectionRow)
			}

			return data
		}

	private val selectionType: SelectionType
		get() {
			return when (this) {
				CASH_TOURNAMENT, DYNAMIC_DATE, LIVE_ONLINE, SESSIONS -> SelectionType.SINGLE
				else -> SelectionType.MULTIPLE
			}
		}

	val exclusiveWith: List<FilterSectionRow>?
		get() {
			return when (this) {
				DYNAMIC_DATE -> arrayListOf(FIXED_DATE)
				FIXED_DATE -> arrayListOf(DYNAMIC_DATE)
				else -> null
			}
		}
}
