package com.tomhempel.rbarometer.questionnaire.handlers

import android.content.Context
import android.view.View
import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.MAX_VALUE_YEAR
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.tomhempel.rbarometer.ui.Month
import com.tomhempel.rbarometer.ui.Months
import com.tomhempel.rbarometer.utils.DateAnswerFormat
import com.tomhempel.rbarometer.utils.DatePrecision
import com.tomhempel.rbarometer.utils.ViewUtils
import java.util.Calendar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class HandlerDateSpinner(
    private val context: Context,
    private val answers: MutableMap<String, Any>,
    private val languageID: String,
    private val goToNextQuestion: () -> Unit,
    private val goToPreviousQuestion: () -> Unit,
    private val goToQuestionById: (String) -> Unit,
    private val showToast: (String) -> Unit,
    private val questionnaireMeta: String,
) : QuestionHandler {

    private companion object {
        private var cachedYearItems: List<Int>? = null

        private fun yearItems(maxYear: Int): List<Int> =
            cachedYearItems ?: (1900..maxYear + 1).reversed().toList().also { cachedYearItems = it }
    }

    private lateinit var question: QuestionItem.DateSpinnerQuestion
    private lateinit var layout: View
    private lateinit var spinnerDay: Spinner
    private lateinit var spinnerMonth: Spinner
    private lateinit var spinnerYear: Spinner
    private lateinit var labelDay: TextView
    private lateinit var labelMonth: TextView
    private lateinit var labelYear: TextView
    private lateinit var precision: DatePrecision

    override fun bind(layout: View, question: QuestionItem) {
        if (question !is QuestionItem.DateSpinnerQuestion) return

        this.layout = layout
        this.question = question
        precision = DatePrecision.fromWire(question.precision)

        initViews()

        val questionTextView = layout.findViewById<TextView>(R.id.question)
        val textView = layout.findViewById<TextView>(R.id.textView)

        questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
        textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
        QuestionNotesHelper.bind(layout, languageID, question.noteBeforeKey, question.noteAfterKey)

        ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f)
        ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
        ViewUtils.setTextSizePercentOfScreenHeight(labelDay, 0.025f)
        ViewUtils.setTextSizePercentOfScreenHeight(labelMonth, 0.025f)
        ViewUtils.setTextSizePercentOfScreenHeight(labelYear, 0.025f)

        applyPrecisionVisibility()

        val saved = question.question?.let { answers[it] as? String }
        val parsed = DateAnswerFormat.parseStored(saved)

        val prompt = LanguageManager.getText(languageID, "choose_answer")
        val days = listOf<Any>(prompt) + (1..31).toList()
        val months = listOf<Any>(prompt) + Months.getAllMonths(languageID)
        val years = listOf<Any>(prompt) + yearItems(MAX_VALUE_YEAR)

        val defaultDay = parsed?.day
        val defaultMonth = parsed?.month?.takeIf { it in 1..12 }?.let { months[it] }
        val defaultYear = parsed?.year

        if (precision != DatePrecision.YEAR) {
            ViewUtils.setupResponsiveSpinner(context, spinnerMonth, months, defaultMonth)
        }
        if (precision == DatePrecision.FULL) {
            ViewUtils.setupResponsiveSpinner(context, spinnerDay, days, defaultDay, compact = true)
        }
        ViewUtils.setupResponsiveSpinner(context, spinnerYear, years, defaultYear, compact = true)

        restoreFromDatabaseIfNeeded(months, years, days)

        layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
            if (validate()) {
                saveAnswer()
                QuestionFlowResolver.navigateAfterAnswer(
                    question.nextQuestionId,
                    goToQuestionById,
                    goToNextQuestion,
                )
            }
        }
        layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
            goToPreviousQuestion()
        }
    }

    private fun applyPrecisionVisibility() {
        val showDay = precision == DatePrecision.FULL
        val showMonth = precision != DatePrecision.YEAR

        labelDay.visibility = if (showDay) View.VISIBLE else View.GONE
        spinnerDay.visibility = if (showDay) View.VISIBLE else View.GONE
        labelMonth.visibility = if (showMonth) View.VISIBLE else View.GONE
        spinnerMonth.visibility = if (showMonth) View.VISIBLE else View.GONE
    }

    private fun restoreFromDatabaseIfNeeded(
        months: List<Any>,
        years: List<Any>,
        days: List<Any>,
    ) {
        val answerMapKey = question.question ?: (question.id ?: "")
        if (answerMapKey.isBlank() || answers.containsKey(answerMapKey)) return

        CoroutineScope(Dispatchers.IO).launch {
            try {
                val clientCode = GlobalValues.LAST_CLIENT_CODE ?: return@launch
                val myQuestionId = "$questionnaireMeta-$answerMapKey"
                val dbAnswer = AnswerRepository.getAnswersForClient(clientCode)
                    .find { it.questionId == myQuestionId }
                    ?.answerValue

                if (!dbAnswer.isNullOrBlank()) {
                    withContext(Dispatchers.Main) {
                        answers[answerMapKey] = dbAnswer
                        val parsed = DateAnswerFormat.parseStored(dbAnswer) ?: return@withContext

                        years.indexOf(parsed.year).takeIf { it >= 0 }?.let {
                            spinnerYear.setSelection(it)
                        }
                        if (precision != DatePrecision.YEAR) {
                            val monthIdx = parsed.month - 1
                            val monthItemIndex = monthIdx + 1
                            if (monthItemIndex in months.indices) {
                                spinnerMonth.setSelection(monthItemIndex)
                            }
                        }
                        if (precision == DatePrecision.FULL) {
                            days.indexOf(parsed.day).takeIf { it >= 0 }?.let {
                                spinnerDay.setSelection(it)
                            }
                        }
                    }
                }
            } catch (_: Exception) {
            }
        }
    }

    private fun initViews() {
        spinnerDay = layout.findViewById(R.id.spinner_value_day)
        spinnerMonth = layout.findViewById(R.id.spinner_value_month)
        spinnerYear = layout.findViewById(R.id.spinner_value_year)
        labelDay = layout.findViewById(R.id.date_spinner_day)
        labelMonth = layout.findViewById(R.id.date_spinner_month)
        labelYear = layout.findViewById(R.id.date_spinner_year)
    }

    override fun validate(): Boolean {
        buildSelectedValue() ?: return false
        if (precision == DatePrecision.FULL && !isValidSelectedDay()) {
            showToast(LanguageManager.getText(languageID, "error_invalid_data"))
            return false
        }

        return true
    }

    override fun saveAnswer() {
        question.question?.let { key ->
            buildSelectedValue()?.let { built ->
                val stored = answers[key] as? String
                answers[key] = if (storedSelectionMatchesUi(stored)) stored?.trim().orEmpty() else built
            }
        }
    }

    private fun buildSelectedValue(): String? {
        val year = spinnerYear.selectedItem?.toString()?.toIntOrNull() ?: return null
        return when (precision) {
            DatePrecision.YEAR -> DateAnswerFormat.format(precision, year, 1, 1)
            DatePrecision.YEAR_MONTH -> DateAnswerFormat.format(
                precision, year, selectedMonthNumber() ?: return null, 1,
            )
            DatePrecision.FULL -> DateAnswerFormat.format(
                precision,
                year,
                selectedMonthNumber() ?: return null,
                spinnerDay.selectedItem?.toString()?.toIntOrNull() ?: return null,
            )
        }
    }

    private fun selectedMonthNumber(): Int? {
        val month = spinnerMonth.selectedItem as? Month ?: return null
        val idx = Months.getAllMonths(languageID).indexOf(month)
        return if (idx >= 0) idx + 1 else null
    }

    private fun storedSelectionMatchesUi(stored: String?): Boolean {
        val parsed = DateAnswerFormat.parseStored(stored) ?: return false
        val year = spinnerYear.selectedItem?.toString()?.toIntOrNull() ?: return false
        val month = selectedMonthNumber() ?: 1
        val day = spinnerDay.selectedItem?.toString()?.toIntOrNull() ?: 1
        return parsed.year == year && parsed.month == month && parsed.day == day
    }

    private fun isValidSelectedDay(): Boolean {
        val year = spinnerYear.selectedItem?.toString()?.toIntOrNull() ?: return false
        val month = selectedMonthNumber() ?: return false
        val day = spinnerDay.selectedItem?.toString()?.toIntOrNull() ?: return false
        val cal = Calendar.getInstance()
        cal.set(year, month - 1, 1)
        return day <= cal.getActualMaximum(Calendar.DAY_OF_MONTH)
    }
}
