package com.dano.test1.questionnaire

import android.graphics.Color
import android.text.Editable
import android.text.TextWatcher
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout
import com.dano.test1.LanguageManager
import com.dano.test1.LocalizationHelper
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.network.TokenStore
import com.dano.test1.utils.ViewUtils
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import kotlinx.coroutines.*

class QuestionnaireAllInOne(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() {

    private data class Section(
        val index: Int,
        val question: QuestionItem,
        val card: MaterialCardView,
        val sectionView: View,
        val handler: QuestionHandler?
    )

    private val sections = mutableListOf<Section>()
    private lateinit var container: LinearLayout
    private lateinit var btnSave: MaterialButton
    private var setupComplete = false

    override fun startQuestionnaire() {
        val (meta, questionsList) = loadQuestionnaireFromJson(questionnaireFileName)
        questionnaireMeta = meta
        questions = questionsList
        currentIndex = 0
        buildAllInOneUi()
    }

    override fun showCurrentQuestion() {
        // Not used in all-in-one mode
    }

    private fun buildAllInOneUi() {
        context.setContentView(R.layout.questionnaire_all_in_one)

        container = context.findViewById(R.id.questionContainer)
        btnSave = context.findViewById(R.id.btnSave)
        val btnBack = context.findViewById<MaterialButton>(R.id.btnBack)
        val progressBar = context.findViewById<ProgressBar>(R.id.progressBar)

        btnSave.text = LanguageManager.getText(languageID, "save")
        ViewUtils.setTextSizePercentOfScreenHeight(btnSave, 0.025f)

        btnSave.isEnabled = false
        btnSave.alpha = 0.5f

        btnBack.setOnClickListener {
            (context as? MainActivity)?.finishQuestionnaire()
        }

        val inflater = LayoutInflater.from(context)

        for ((idx, question) in questions.withIndex()) {
            if (question is QuestionItem.LastPage) continue

            val layoutResId = getLayoutResId(question.layout ?: "default_layout")
            if (layoutResId == 0) continue

            val sectionView = inflater.inflate(layoutResId, container, false)
            adaptLayoutForEmbedding(sectionView)
            LocalizationHelper.localizeViewTree(sectionView, languageID)

            val card = wrapInCard(sectionView)
            container.addView(card)

            val handler = createEmbeddedHandler(question)
            handler?.bind(sectionView, question)
            reduceTextSizes(sectionView)

            sections.add(Section(idx, question, card, sectionView, handler))
        }

        installChangeListeners()
        recalculateVisibility()

        // Allow DB restores to finish before enabling interaction tracking
        container.postDelayed({
            setupComplete = true
            recalculateVisibility()
            updateSaveButtonState()
        }, 600)

        btnSave.setOnClickListener {
            handleSave(progressBar)
        }
    }

    private fun createEmbeddedHandler(question: QuestionItem): QuestionHandler? {
        val noop = {}
        val noopId: (String) -> Unit = {}
        val toast: (String) -> Unit = { showToast(it) }
        val metaId = questionnaireMeta.id

        return when (question) {
            is QuestionItem.RadioQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerRadioQuestion(
                    context, answers, points, languageID, noop, noop, noopId, toast, metaId
                )
            is QuestionItem.ClientCoachCodeQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerClientCoachCode(
                    answers, languageID, noop, noop, toast
                )
            is QuestionItem.DateSpinnerQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerDateSpinner(
                    context, answers, languageID, noop, noop, toast, metaId
                )
            is QuestionItem.ValueSpinnerQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerValueSpinner(
                    context, answers, languageID, noop, noop, noopId, toast, metaId
                )
            is QuestionItem.GlassScaleQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerGlassScaleQuestion(
                    context, answers, points, languageID, noop, noop, toast, metaId
                )
            is QuestionItem.ClientNotSigned ->
                com.dano.test1.questionnaire.handlers.HandlerClientNotSigned(
                    answers, languageID, noop, noop, toast
                )
            is QuestionItem.StringSpinnerQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerStringSpinner(
                    context, answers, languageID, noop, noop, toast, metaId
                )
            is QuestionItem.MultiCheckboxQuestion ->
                com.dano.test1.questionnaire.handlers.HandlerMultiCheckboxQuestion(
                    context, answers, points, languageID, noop, noop, toast, metaId
                )
            else -> null
        }
    }

    private fun adaptLayoutForEmbedding(view: View) {
        if (view is ConstraintLayout) {
            view.layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )
            // Ensure the last field in the card is never clipped at the bottom
            view.setPadding(
                view.paddingLeft, view.paddingTop,
                view.paddingRight, ViewUtils.dp(context, 12)
            )
        }

        view.findViewById<View>(R.id.Qprev)?.visibility = View.GONE
        view.findViewById<View>(R.id.Qnext)?.visibility = View.GONE

        view.findViewById<View>(R.id.gTop)?.let { guideline ->
            if (guideline is androidx.constraintlayout.widget.Guideline) {
                val params = guideline.layoutParams as? ConstraintLayout.LayoutParams
                params?.guideBegin = ViewUtils.dp(context, 8)
                guideline.layoutParams = params
            }
        }

        adaptInnerScrollView(view, R.id.radioScroll)
        adaptInnerScrollView(view, R.id.scrollView)
        adaptInnerScrollView(view, R.id.glassScroll)

        view.findViewById<EditText>(R.id.client_code)?.let { et ->
            val params = et.layoutParams as? ConstraintLayout.LayoutParams ?: return@let
            if (params.matchConstraintPercentHeight > 0f) {
                params.matchConstraintPercentHeight = 0f
                params.height = ViewUtils.dp(context, 64)
                et.layoutParams = params
            }
        }
        view.findViewById<EditText>(R.id.coach_code)?.let { et ->
            val params = et.layoutParams as? ConstraintLayout.LayoutParams ?: return@let
            if (params.matchConstraintPercentHeight > 0f) {
                params.matchConstraintPercentHeight = 0f
                params.height = ViewUtils.dp(context, 64)
                et.layoutParams = params
            }
        }
    }

    /**
     * Scales down all TextView text sizes by [factor] after the handler has set them.
     * Spinners manage their own adapter text sizes and are skipped.
     */
    private fun reduceTextSizes(view: View, factor: Float = 0.82f) {
        if (view is Spinner) return
        if (view is TextView) {
            val dm = view.context.resources.displayMetrics
            val currentSp = view.textSize / dm.scaledDensity
            view.setTextSize(TypedValue.COMPLEX_UNIT_SP, currentSp * factor)
        }
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) reduceTextSizes(view.getChildAt(i), factor)
        }
    }

    private fun adaptInnerScrollView(root: View, scrollViewId: Int) {
        val sv = root.findViewById<ScrollView>(scrollViewId) ?: return
        val params = sv.layoutParams
        if (params is ConstraintLayout.LayoutParams) {
            params.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
            params.bottomToTop = ConstraintLayout.LayoutParams.UNSET
            params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
            sv.layoutParams = params
        }
        sv.isNestedScrollingEnabled = false
    }

    private fun wrapInCard(sectionView: View): MaterialCardView {
        val card = MaterialCardView(context).apply {
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            ).apply {
                val margin = ViewUtils.dp(context, 6)
                setMargins(0, margin, 0, margin)
            }
            radius = ViewUtils.dp(context, 16).toFloat()
            cardElevation = ViewUtils.dp(context, 2).toFloat()
            setCardBackgroundColor(Color.WHITE)
            strokeColor = Color.parseColor("#D8D1F0")
            strokeWidth = ViewUtils.dp(context, 1)
            setContentPadding(
                ViewUtils.dp(context, 4),
                ViewUtils.dp(context, 8),
                ViewUtils.dp(context, 4),
                ViewUtils.dp(context, 12)
            )
        }
        card.addView(sectionView)
        return card
    }

    // ---- Visibility walk algorithm (reads from UI state, not answers map) ----

    private fun recalculateVisibility() {
        val visibleIndices = mutableSetOf<Int>()
        var i = 0

        while (i < questions.size) {
            val q = questions[i]
            if (q is QuestionItem.LastPage) { i++; continue }

            visibleIndices.add(i)

            val nextTarget = resolveNextFromUi(q)
            when {
                nextTarget != null -> {
                    if (questions.any { it is QuestionItem.LastPage && it.id == nextTarget }) {
                        break
                    }
                    val targetIdx = questions.indexOfFirst { it.id == nextTarget }
                    i = if (targetIdx != -1) targetIdx else i + 1
                }
                hasBranchingOptions(q) && !hasAnswerInUi(q) -> break
                else -> i++
            }
        }

        for (section in sections) {
            val shouldShow = visibleIndices.contains(section.index)
            section.card.visibility = if (shouldShow) View.VISIBLE else View.GONE
        }
    }

    private fun resolveNextFromUi(q: QuestionItem): String? {
        val section = sections.find { it.question === q } ?: return null
        val view = section.sectionView

        return when (q) {
            is QuestionItem.RadioQuestion -> {
                val radioGroup = view.findViewById<RadioGroup>(R.id.RadioGroup) ?: return null
                val checkedId = radioGroup.checkedRadioButtonId
                if (checkedId == -1) return null
                val rb = radioGroup.findViewById<RadioButton>(checkedId) ?: return null
                val key = rb.tag?.toString() ?: return null
                q.options?.find { it.key == key }?.nextQuestionId
            }
            is QuestionItem.ValueSpinnerQuestion -> {
                val spinner = view.findViewById<Spinner>(R.id.value_spinner) ?: return null
                val selected = spinner.selectedItem?.toString() ?: return null
                val prompt = LanguageManager.getText(languageID, "choose_answer")
                if (selected == prompt) return null
                val selectedVal = selected.toIntOrNull()
                if (selectedVal != null) {
                    q.options?.find { it.value == selectedVal }?.nextQuestionId
                } else null
            }
            else -> null
        }
    }

    private fun hasBranchingOptions(q: QuestionItem): Boolean {
        return when (q) {
            is QuestionItem.RadioQuestion ->
                q.options?.any { it.nextQuestionId != null } ?: false
            is QuestionItem.ValueSpinnerQuestion ->
                q.options?.any { it.nextQuestionId != null } ?: false
            else -> false
        }
    }

    private fun hasAnswerInUi(q: QuestionItem): Boolean {
        val section = sections.find { it.question === q } ?: return false
        val view = section.sectionView

        return when (q) {
            is QuestionItem.RadioQuestion -> {
                val rg = view.findViewById<RadioGroup>(R.id.RadioGroup) ?: return false
                rg.checkedRadioButtonId != -1
            }
            is QuestionItem.ValueSpinnerQuestion -> {
                val spinner = view.findViewById<Spinner>(R.id.value_spinner) ?: return false
                val selected = spinner.selectedItem?.toString()
                val prompt = LanguageManager.getText(languageID, "choose_answer")
                !selected.isNullOrEmpty() && selected != prompt
            }
            else -> true
        }
    }

    // ---- Change listeners ----

    private fun installChangeListeners() {
        for (section in sections) {
            val view = section.sectionView

            when (section.question) {
                is QuestionItem.RadioQuestion -> {
                    view.findViewById<RadioGroup>(R.id.RadioGroup)
                        ?.setOnCheckedChangeListener { _, checkedId ->
                            if (!setupComplete || checkedId == -1) return@setOnCheckedChangeListener
                            recalculateVisibility()
                            updateSaveButtonState()
                        }
                }
                is QuestionItem.ValueSpinnerQuestion -> {
                    installSpinnerChangeListener(view, R.id.value_spinner)
                }
                is QuestionItem.StringSpinnerQuestion -> {
                    installSpinnerChangeListener(view, R.id.string_spinner)
                }
                is QuestionItem.DateSpinnerQuestion -> {
                    val listener = createSimpleSpinnerListener()
                    view.findViewById<Spinner>(R.id.spinner_value_day)?.onItemSelectedListener = listener
                    view.findViewById<Spinner>(R.id.spinner_value_month)?.onItemSelectedListener = listener
                    view.findViewById<Spinner>(R.id.spinner_value_year)?.onItemSelectedListener = listener
                }
                is QuestionItem.GlassScaleQuestion -> {
                    installGlassScaleClickListeners(view)
                }
                is QuestionItem.MultiCheckboxQuestion -> {
                    installCheckboxClickListeners(view)
                }
                is QuestionItem.ClientCoachCodeQuestion -> {
                    installEditTextWatcher(view, R.id.client_code)
                    installEditTextWatcher(view, R.id.coach_code)
                }
                is QuestionItem.ClientNotSigned -> {
                    installEditTextWatcher(view, R.id.coach_code)
                }
                else -> {}
            }
        }
    }

    private fun installSpinnerChangeListener(view: View, spinnerId: Int) {
        view.findViewById<Spinner>(spinnerId)?.onItemSelectedListener =
            object : AdapterView.OnItemSelectedListener {
                override fun onItemSelected(
                    parent: AdapterView<*>?, v: View?, pos: Int, id: Long
                ) {
                    if (!setupComplete) return
                    recalculateVisibility()
                    updateSaveButtonState()
                }
                override fun onNothingSelected(parent: AdapterView<*>?) {}
            }
    }

    private fun createSimpleSpinnerListener(): AdapterView.OnItemSelectedListener {
        return object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, v: View?, pos: Int, id: Long) {
                if (!setupComplete) return
                updateSaveButtonState()
            }
            override fun onNothingSelected(parent: AdapterView<*>?) {}
        }
    }

    private fun installGlassScaleClickListeners(view: View) {
        val table = view.findViewById<TableLayout>(R.id.glass_table) ?: return
        for (r in 0 until table.childCount) {
            val row = table.getChildAt(r) as? TableRow ?: continue
            val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue
            for (c in 0 until radioGroup.childCount) {
                val rb = getRadioFromChild(radioGroup.getChildAt(c)) ?: continue
                rb.setOnClickListener {
                    if (setupComplete) updateSaveButtonState()
                }
            }
        }
    }

    private fun getRadioFromChild(child: View): RadioButton? = when (child) {
        is RadioButton -> child
        is FrameLayout -> child.getChildAt(0) as? RadioButton
        else -> null
    }

    private fun installCheckboxClickListeners(view: View) {
        val cont = view.findViewById<LinearLayout>(R.id.CheckboxContainer) ?: return
        for (i in 0 until cont.childCount) {
            val cb = cont.getChildAt(i) as? CheckBox ?: continue
            cb.setOnClickListener {
                if (setupComplete) updateSaveButtonState()
            }
        }
    }

    private fun installEditTextWatcher(view: View, editTextId: Int) {
        view.findViewById<EditText>(editTextId)?.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(s: Editable?) {
                if (!setupComplete) return
                updateSaveButtonState()
            }
        })
    }

    // ---- Save button state ----

    private fun updateSaveButtonState() {
        val allValid = sections
            .filter { it.card.visibility == View.VISIBLE }
            .all { section ->
                val handler = section.handler ?: return@all true
                try { handler.validate() } catch (_: Exception) { false }
            }
        btnSave.isEnabled = allValid
        btnSave.alpha = if (allValid) 1f else 0.5f
    }

    // ---- Save flow ----

    private fun handleSave(progressBar: ProgressBar) {
        val visibleSections = sections.filter { it.card.visibility == View.VISIBLE }

        for (section in visibleSections) {
            val handler = section.handler ?: continue
            if (!handler.validate()) {
                showToast(LanguageManager.getText(languageID, "fill_all_fields"))
                scrollToSection(section)
                return
            }
        }

        for (section in visibleSections) {
            if (section.question is QuestionItem.ClientCoachCodeQuestion) {
                saveClientCoachCodeFromUi(section.sectionView)
            }
            section.handler?.saveAnswer()
        }

        showLoading(progressBar, true)

        CoroutineScope(Dispatchers.IO).launch {
            val startTime = System.currentTimeMillis()

            saveAnswersToDatabase(answers, questionnaireMeta.id)

            GlobalValues.INTEGRATION_INDEX = points.sum()
            val clientCode = answers["client_code"] as? String
            if (clientCode != null) {
                GlobalValues.LAST_CLIENT_CODE = clientCode
                GlobalValues.LOADED_CLIENT_CODE = clientCode
            }

            val elapsed = System.currentTimeMillis() - startTime
            if (elapsed < 1500L) delay(1500L - elapsed)

            withContext(Dispatchers.Main) {
                showLoading(progressBar, false)
                endQuestionnaire()
            }
        }
    }

    private fun saveClientCoachCodeFromUi(view: View) {
        val clientCode = view.findViewById<EditText>(R.id.client_code)?.text?.toString() ?: ""
        val coachCode = view.findViewById<EditText>(R.id.coach_code)?.text?.toString() ?: ""
        GlobalValues.LAST_CLIENT_CODE = clientCode
        answers["client_code"] = clientCode
        answers["coach_code"] = TokenStore.getUsername(context) ?: coachCode
    }

    private fun showLoading(progressBar: ProgressBar, show: Boolean) {
        progressBar.visibility = if (show) View.VISIBLE else View.GONE
        btnSave.isEnabled = !show
        btnSave.alpha = if (show) 0.5f else 1f
    }

    private fun scrollToSection(section: Section) {
        val scrollView = context.findViewById<androidx.core.widget.NestedScrollView>(R.id.scrollContainer)
        scrollView?.post {
            scrollView.smoothScrollTo(0, section.card.top)
        }
    }
}
