package net.pokeranalytics.android.ui.fragment.data

import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.view.View
import net.pokeranalytics.android.R
import net.pokeranalytics.android.api.CurrencyConverterApi
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.retrofit.CurrencyConverterValue
import net.pokeranalytics.android.ui.activity.CurrenciesActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.CurrenciesFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.UserDefaults
import net.pokeranalytics.android.util.extensions.toCurrency
import net.pokeranalytics.android.util.extensions.toRate
import retrofit2.Call
import retrofit2.Response
import java.util.*



/**
 * Custom EditableDataFragment to manage the Bankroll data
 */
class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataSource {

	companion object {
		const val REQUEST_CODE_CURRENCY: Int = 100
	}

	// Return the item as a Bankroll object
	private val bankroll: Bankroll
		get() {
			return this.item as Bankroll
		}

	private lateinit var defaultCurrency: Currency
	private val rows = ArrayList<RowRepresentable>()
	private var isRefreshingRate = false
	private var lastRefreshRateCall = 0L

	private val shouldShowCurrencyRate : Boolean
		get() {
			return bankroll.currency?.let { bankrollCurrency ->
				bankrollCurrency.code != defaultCurrency.currencyCode
			} ?: false
		}

	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		super.onViewCreated(view, savedInstanceState)

		shouldOpenKeyboard = false

		initData()
		refreshRows()
		updateAdapterUI()
	}

	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
		super.onActivityResult(requestCode, resultCode, data)
		if (requestCode == REQUEST_CODE_CURRENCY && resultCode == RESULT_OK) {
			data?.let {
				val currencyCode = it.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE)
				onRowValueChanged(currencyCode, BankrollRow.CURRENCY)
				if (shouldShowCurrencyRate) {
					refreshRate()
				}
			}
		}
	}

	override fun getDataSource(): RowRepresentableDataSource {
		return this
	}

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

	override fun stringForRow(row: RowRepresentable): String {
		return when (row) {
			SimpleRow.NAME -> if (bankroll.name.isNotEmpty()) bankroll.name else NULL_TEXT
			BankrollRow.CURRENCY -> {
				bankroll.currency?.code?.let { code ->
					Currency.getInstance(code).currencyCode
				} ?: run {
					NULL_TEXT
				}
			}
			BankrollRow.INITIAL_VALUE -> {
				var c: Currency? = null
				this.bankroll.currency?.code?.let {
					c = Currency.getInstance(it)
				}
				this.bankroll.initialValue.toCurrency(c)
			}
			BankrollRow.RATE -> {
				val rate = this.bankroll.currency?.rate ?: 1.0
				rate.toRate()
			}
			else -> super.stringForRow(row)
		}
	}

	override fun boolForRow(row: RowRepresentable): Boolean {
		return when(row) {
			BankrollRow.LIVE -> !bankroll.live
			BankrollRow.REFRESH_RATE -> isRefreshingRate
			else -> super.boolForRow(row)
		}
	}

	override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
		return when (row) {
			SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.bankroll.name))
			BankrollRow.INITIAL_VALUE -> {
				row.editingDescriptors(mapOf("defaultValue" to this.bankroll.initialValue))
			}
			BankrollRow.RATE -> {
				val rate = this.bankroll.currency?.rate
				row.editingDescriptors(mapOf("defaultValue" to rate))
			}
			else -> null
		}
	}

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		when (row) {
			BankrollRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@BankrollDataFragment,
				REQUEST_CODE_CURRENCY
			)
			BankrollRow.REFRESH_RATE -> refreshRate()
			else -> super.onRowSelected(position, row, fromAction)
		}
	}

	override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
		super.onRowValueChanged(value, row)

		// Clear the value when the currency has been updated
		if (row == BankrollRow.CURRENCY) {
			this.lastRefreshRateCall = 0
		}

		updateAdapterUI()
	}

	/**
	 * Init data
	 */
	override fun initData() {
		super.initData()

		defaultCurrency = UserDefaults.currency

		if (!deleteButtonShouldAppear) {
			bankroll.currency = net.pokeranalytics.android.model.realm.Currency()
			bankroll.currency?.code = defaultCurrency.currencyCode
			bankroll.currency?.rate = 1.0
		}
	}

	/**
	 * Refresh rows
	 */
	private fun refreshRows() {

		rows.clear()
		rows.add(SimpleRow.NAME)
		rows.add(BankrollRow.LIVE)
		rows.add(BankrollRow.INITIAL_VALUE)
		rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.currency))
		rows.add(BankrollRow.CURRENCY)
		if (this.shouldShowCurrencyRate) {
			rows.add(BankrollRow.RATE)
			rows.add(BankrollRow.REFRESH_RATE)
		}
	}

	/**
	 * Update UI adapter
	 */
	private fun updateAdapterUI() {

		val currentRowsSize = rows.size
		refreshRows()
		val newRowsSize = rows.size

		if (currentRowsSize < newRowsSize) {
			rowRepresentableAdapter.notifyItemRangeInserted(currentRowsSize, newRowsSize - currentRowsSize)
		} else {
			rowRepresentableAdapter.notifyItemRangeRemoved(newRowsSize, currentRowsSize - newRowsSize)
		}
	}

	/**
	 * Refresh the rate with the CurrencyCode Converter API
	 */
	private fun refreshRate() {

		// Check if the last call was older than 10 seconds
		if (System.currentTimeMillis() - lastRefreshRateCall < 10 * 1000 || isRefreshingRate) {
			return
		}
		lastRefreshRateCall = System.currentTimeMillis()

		val currenciesConverterValue = "${defaultCurrency.currencyCode}_${bankroll.currency?.code}"
		val call = CurrencyConverterApi.getApi(requireContext())?.convert(currenciesConverterValue)
		call?.enqueue(object : retrofit2.Callback<Map<String, CurrencyConverterValue>> {

			override fun onResponse(call: Call<Map<String, CurrencyConverterValue>>, response: Response<Map<String, CurrencyConverterValue>>) {
				response.body()?.let {
					it[currenciesConverterValue]?.value?.let { rate ->
						onRowValueChanged(rate, BankrollRow.RATE)
					}
				}

				isRefreshingRate = false
				rowRepresentableAdapter.refreshRow(BankrollRow.REFRESH_RATE)
			}

			override fun onFailure(call: Call<Map<String, CurrencyConverterValue>>, t: Throwable) {
				isRefreshingRate = false
				rowRepresentableAdapter.refreshRow(BankrollRow.REFRESH_RATE)
			}
		})

		isRefreshingRate = true
		rowRepresentableAdapter.refreshRow(BankrollRow.REFRESH_RATE)
	}

}