package com.tomhempel.rbarometer.questionnaire

import com.tomhempel.rbarometer.LanguageManager

object QuestionFlowResolver {

    fun questionLevelNextId(nextQuestionId: String?): String? =
        nextQuestionId?.trim()?.takeIf { it.isNotEmpty() }

    fun navigateAfterAnswer(
        nextQuestionId: String?,
        goToQuestionById: (String) -> Unit,
        goToNextQuestion: () -> Unit,
    ) {
        val nextId = questionLevelNextId(nextQuestionId)
        if (nextId != null) {
            goToQuestionById(nextId)
        } else {
            goToNextQuestion()
        }
    }

    fun nextQuestionIdForRadio(question: QuestionItem.RadioQuestion, selectedKey: String): String? =
        question.options?.find { it.key == selectedKey }?.nextQuestionId

    fun nextQuestionIdForValueSpinner(
        question: QuestionItem.ValueSpinnerQuestion,
        selectedValue: Int?,
    ): String? {
        if (selectedValue != null) {
            question.options?.find { it.value == selectedValue }?.nextQuestionId
                ?.trim()
                ?.takeIf { it.isNotEmpty() }
                ?.let { return it }
        }
        return questionLevelNextId(question.nextQuestionId)
    }

    fun nextQuestionIdForMultiCheckbox(
        question: QuestionItem.MultiCheckboxQuestion,
        selectedKeys: Set<String>,
    ): String? {
        val otherKey = question.otherOptionKey?.trim()?.takeIf { it.isNotEmpty() }
        val otherNext = question.otherNextQuestionId?.trim()?.takeIf { it.isNotEmpty() }
        if (otherKey != null && otherNext != null && selectedKeys.contains(otherKey)) {
            return otherNext
        }
        return questionLevelNextId(question.nextQuestionId)
    }

    fun nextQuestionIdForStringSpinner(
        question: QuestionItem.StringSpinnerQuestion,
        languageId: String,
        selected: String,
    ): String? {
        val otherKey = question.otherOptionKey?.trim()?.takeIf { it.isNotEmpty() }
        val otherNext = question.otherNextQuestionId?.trim()?.takeIf { it.isNotEmpty() }
        if (otherKey != null && otherNext != null) {
            val otherLabel = LanguageManager.getText(languageId, otherKey)
            if (selected == otherLabel || selected == otherKey) return otherNext
        }
        return questionLevelNextId(question.nextQuestionId)
    }

    fun hasBranchingOptions(question: QuestionItem): Boolean = when (question) {
        is QuestionItem.RadioQuestion ->
            question.options?.any { !it.nextQuestionId.isNullOrBlank() } == true
        is QuestionItem.ValueSpinnerQuestion ->
            question.options?.any { !it.nextQuestionId.isNullOrBlank() } == true ||
                !question.nextQuestionId.isNullOrBlank()
        is QuestionItem.StringSpinnerQuestion ->
            !question.nextQuestionId.isNullOrBlank() || !question.otherNextQuestionId.isNullOrBlank()
        is QuestionItem.FreeTextQuestion ->
            !question.nextQuestionId.isNullOrBlank()
        is QuestionItem.MultiCheckboxQuestion ->
            !question.nextQuestionId.isNullOrBlank() || !question.otherNextQuestionId.isNullOrBlank()
        is QuestionItem.DateSpinnerQuestion ->
            !question.nextQuestionId.isNullOrBlank()
        is QuestionItem.SliderQuestion ->
            !question.nextQuestionId.isNullOrBlank()
        is QuestionItem.GlassScaleQuestion ->
            !question.nextQuestionId.isNullOrBlank()
        is QuestionItem.ClientCoachCodeQuestion ->
            !question.nextQuestionId.isNullOrBlank()
        else -> false
    }

    /**
     * Walk questions in order using [answerProvider] for branching decisions (All-in-One visibility).
     */
    fun visibleQuestionIndices(
        questions: List<QuestionItem>,
        answerProvider: (QuestionItem) -> BranchAnswer?,
    ): Set<Int> {
        val visible = mutableSetOf<Int>()
        var i = 0
        while (i < questions.size) {
            val q = questions[i]
            if (q is QuestionItem.LastPage) {
                i++
                continue
            }
            visible.add(i)
            val branch = answerProvider(q)
            val nextId = branch?.let { resolveNextId(q, it) }
            when {
                nextId != null -> {
                    if (questions.any { it is QuestionItem.LastPage && it.id == nextId }) break
                    val target = questions.indexOfFirst { it.id == nextId }
                    i = if (target != -1) target else i + 1
                }
                hasBranchingOptions(q) && branch == null -> break
                else -> i++
            }
        }
        return visible
    }

    private fun resolveNextId(question: QuestionItem, branch: BranchAnswer): String? = when (question) {
        is QuestionItem.RadioQuestion -> branch.selectedKey?.let { nextQuestionIdForRadio(question, it) }
        is QuestionItem.ValueSpinnerQuestion -> nextQuestionIdForValueSpinner(question, branch.selectedInt)
        is QuestionItem.FreeTextQuestion -> questionLevelNextId(question.nextQuestionId)
        is QuestionItem.StringSpinnerQuestion ->
            branch.selectedDisplay?.let { nextQuestionIdForStringSpinner(question, branch.languageId, it) }
        is QuestionItem.MultiCheckboxQuestion ->
            branch.selectedKeys?.let { nextQuestionIdForMultiCheckbox(question, it) }
        is QuestionItem.DateSpinnerQuestion -> questionLevelNextId(question.nextQuestionId)
        is QuestionItem.SliderQuestion -> questionLevelNextId(question.nextQuestionId)
        is QuestionItem.GlassScaleQuestion -> questionLevelNextId(question.nextQuestionId)
        is QuestionItem.ClientCoachCodeQuestion -> questionLevelNextId(question.nextQuestionId)
        else -> null
    }

    data class BranchAnswer(
        val languageId: String,
        val selectedKey: String? = null,
        val selectedInt: Int? = null,
        val selectedDisplay: String? = null,
        val selectedKeys: Set<String>? = null,
    )
}
