package net.pokeranalytics.android.model.realm

import android.content.Context
import com.github.mikephil.charting.data.Entry
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.GraphFragment
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.view.DefaultLegendValues
import net.pokeranalytics.android.ui.view.LegendContent
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionRow
import net.pokeranalytics.android.util.TextFormat
import net.pokeranalytics.android.util.extensions.findById
import java.text.DateFormat
import java.util.*
import kotlin.collections.ArrayList


open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, TimeFilterable, Filterable, DatedValue,
	GraphUnderlyingEntry {

	companion object {

		val rowRepresentation: List<RowRepresentable> by lazy {
			val rows = ArrayList<RowRepresentable>()
			rows.addAll(TransactionRow.values())
			rows
		}

		fun fieldNameForQueryType(queryCondition: Class<out QueryCondition>): String? {
			return when (queryCondition) {

                QueryCondition.AnyBankroll::class.java -> "bankroll.id"
				QueryCondition.AnyTransactionType::class.java -> "type.id"
                QueryCondition.StartedFromDate::class.java, QueryCondition.StartedToDate::class.java, QueryCondition.EndedFromDate::class.java, QueryCondition.EndedToDate::class.java -> "date"
                QueryCondition.AnyDayOfWeek::class.java, QueryCondition.IsWeekEnd::class.java, QueryCondition.IsWeekDay::class.java -> "dayOfWeek"
                QueryCondition.AnyMonthOfYear::class.java -> "month"
                QueryCondition.AnyYear::class.java -> "year"
                QueryCondition.PastDay::class.java, QueryCondition.IsToday::class.java, QueryCondition.WasYesterday::class.java, QueryCondition.WasTodayAndYesterday::class.java, QueryCondition.DuringThisYear::class.java, QueryCondition.DuringThisMonth::class.java, QueryCondition.DuringThisWeek::class.java -> "date"
				else -> null
			}
		}
	}

	init {
		this.updateTimeParameter(Date())
	}

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

	// The bankroll of the transaction
	var bankroll: Bankroll? = null

	// The amount of the transaction
	override var amount: Double = 0.0

	// The date of the transaction
	override var date: Date = Date()
		set(value) {
			field = value
			this.updateTimeParameter(field)
		}

	// The type of the transaction
	var type: TransactionType? = null

	// A user comment
	var comment: String = ""

	// Timed interface
	override var dayOfWeek: Int? = null
	override var month: Int? = null
	override var year: Int? = null
	override var dayOfMonth: Int? = null

	@Ignore
	override val viewType: Int = RowViewType.ROW_TRANSACTION.ordinal

	override fun updateValue(value: Any?, row: RowRepresentable) {
		when (row) {
			TransactionRow.BANKROLL -> bankroll = value as Bankroll?
			TransactionRow.TYPE -> type = value as TransactionType?
			TransactionRow.AMOUNT -> amount = if (value == null) 0.0 else (value as String).toDouble()
			TransactionRow.COMMENT -> comment = value as String? ?: ""
			TransactionRow.DATE -> date = value as Date? ?: Date()
		}
	}

	override fun adapterRows(): List<RowRepresentable>? {
		return rowRepresentation
	}

	override fun isValidForSave(): Boolean {
		return bankroll != null && type != null && amount != 0.0
	}

	override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
		return if (bankroll == null) {
			R.string.no_br_popup_message
		} else if (type == null || amount == 0.0) {
			R.string.operation_empty_field_error
		} else {
			TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
		}
	}

	override fun alreadyExists(realm: Realm): Boolean {
		return realm.findById<Transaction>(id) != null
	}

	override fun isValidForDelete(realm: Realm): Boolean {
		return true
	}

	override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
		return R.string.relationship_error
	}

	override fun getSaveValidityStatus(realm: Realm): SaveValidityStatus {
		if (bankroll == null || type == null || amount == 0.0) {
			return SaveValidityStatus.DATA_INVALID
		}
		return SaveValidityStatus.VALID
	}

	// GraphUnderlyingEntry

	override fun entryTitle(context: Context): String {
		return DateFormat.getDateInstance(DateFormat.SHORT).format(this.date)
	}

	override fun formattedValue(stat: Stat): TextFormat {
		return stat.format(this.amount)
	}

	override fun legendValues(
		stat: Stat,
		entry: Entry,
		style: GraphFragment.Style,
		groupName: String,
		context: Context
	): LegendContent {

		val entryValue = this.formattedValue(stat)
		val totalStatValue = stat.format(entry.y.toDouble(), currency = null)
		val leftName = context.getString(R.string.amount)
		return DefaultLegendValues(this.entryTitle(context), entryValue, totalStatValue, leftName = leftName)
	}

}
