@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.grammar

import eu.gaudian.translator.model.grammar.GrammarConstants.CONDITIONAL_PRESENT
import eu.gaudian.translator.model.grammar.GrammarConstants.IMPERATIVE
import eu.gaudian.translator.model.grammar.GrammarConstants.INDICATIVE_FUTURE
import eu.gaudian.translator.model.grammar.GrammarConstants.INDICATIVE_IMPERFECT
import eu.gaudian.translator.model.grammar.GrammarConstants.INDICATIVE_PRESENT
import eu.gaudian.translator.model.grammar.GrammarConstants.INDICATIVE_SIMPLE_PAST
import eu.gaudian.translator.model.grammar.GrammarConstants.INFINITIVE
import eu.gaudian.translator.model.grammar.GrammarConstants.KEY_DATA
import eu.gaudian.translator.model.grammar.GrammarConstants.KEY_FORMS
import eu.gaudian.translator.model.grammar.GrammarConstants.KEY_INFLECTIONS
import eu.gaudian.translator.model.grammar.GrammarConstants.KEY_TYPE
import eu.gaudian.translator.model.grammar.GrammarConstants.PARTICIPLE_PAST
import eu.gaudian.translator.model.grammar.GrammarConstants.PARTICIPLE_PRESENT
import eu.gaudian.translator.model.grammar.GrammarConstants.PAST
import eu.gaudian.translator.model.grammar.GrammarConstants.POS_NOUN
import eu.gaudian.translator.model.grammar.GrammarConstants.POS_VERB
import eu.gaudian.translator.model.grammar.GrammarConstants.PRESENT
import eu.gaudian.translator.model.grammar.GrammarConstants.SUBJUNCTIVE_I
import eu.gaudian.translator.model.grammar.GrammarConstants.SUBJUNCTIVE_II
import eu.gaudian.translator.model.grammar.GrammarConstants.SUBJUNCTIVE_PRESENT
import eu.gaudian.translator.model.grammar.GrammarConstants.TYPE_DE_ADJ
import eu.gaudian.translator.model.grammar.GrammarConstants.TYPE_DE_VERB
import eu.gaudian.translator.model.grammar.WordMorphology.Adjective
import eu.gaudian.translator.model.grammar.WordMorphology.GenericInflections
import eu.gaudian.translator.model.grammar.WordMorphology.Noun
import eu.gaudian.translator.model.grammar.WordMorphology.Verb
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
 * Interface for language-specific morphology parsing.
 */
interface MorphologyStrategy {
    fun parse(
        lemma: String,
        pos: String?,
        rootData: JsonObject,
        config: LanguageConfig?
    ): WordMorphology?
}

/**
 * Strategy for German (de).
 */
class GermanMorphologyStrategy : MorphologyStrategy {
    override fun parse(
        lemma: String,
        pos: String?,
        rootData: JsonObject,
        config: LanguageConfig?
    ): WordMorphology? {
        val formsElement = rootData[KEY_FORMS] ?: return null
        val categories = config?.categories
        val normalizedPos = pos?.lowercase()?.trim()

        return when (formsElement) {
            is JsonObject -> {
                val type = formsElement[KEY_TYPE]?.jsonPrimitive?.contentOrNull
                val innerData = formsElement[KEY_DATA]?.jsonObject

                when (type) {
                    TYPE_DE_VERB if innerData != null -> {
                        val verbConfig = categories?.get(POS_VERB)
                        val conjugation = mapJsonToVerbConjugation(lemma, innerData, verbConfig)
                        Verb(listOf(conjugation))
                    }
                    TYPE_DE_ADJ if innerData != null -> {
                        val comparison = parseAdjectiveComparison(innerData)
                        Adjective(comparison)
                    }
                    else -> {
                        null
                    }
                }
            }
            is JsonArray -> {
                if (formsElement.isEmpty()) return null

                if (SharedMorphologyUtils.isNounLikeStructure(formsElement, normalizedPos)) {
                    val nounConfig = categories?.get(POS_NOUN)
                    val declension = SharedMorphologyUtils.parseNounDeclension(formsElement, nounConfig)
                    return Noun(declension)
                }

                // Handle array of verb blocks
                val verbBlocks = formsElement.mapNotNull { element ->
                    val obj = element.jsonObject
                    val type = obj[KEY_TYPE]?.jsonPrimitive?.contentOrNull
                    val innerData = obj[KEY_DATA]?.jsonObject
                    if (innerData != null && (type == TYPE_DE_VERB || obj.containsKey(KEY_DATA))) innerData else null
                }

                if (verbBlocks.isNotEmpty()) {
                    val verbConfig = categories?.get(POS_VERB)
                    val conjugations = verbBlocks.map { innerData ->
                        mapJsonToVerbConjugation(lemma, innerData, verbConfig)
                    }
                    return Verb(conjugations)
                }
                null
            }
            else -> null
        }
    }

    private fun mapJsonToVerbConjugation(
        infinitive: String,
        data: JsonObject,
        categoryConfig: CategoryConfig?
    ): VerbConjugation {
        fun getForms(key: String): List<String> =
            data[key]?.jsonArray?.mapNotNull { (it as? JsonPrimitive)?.contentOrNull } ?: emptyList()

        val conjugationMap = mutableMapOf<String, List<String>>()
        val tenseLabels = categoryConfig?.conjugation_display?.tense_labels

        fun addTense(key: String) {
            val forms = getForms(key)
            if (forms.isNotEmpty()) {
                val label = tenseLabels?.get(key) ?: key.replaceFirstChar { it.titlecase() }
                conjugationMap[label] = forms
            }
        }

        addTense(PRESENT)
        addTense(PAST)
        addTense(SUBJUNCTIVE_I)
        addTense(SUBJUNCTIVE_II)

        val pronouns = categoryConfig?.conjugation_display?.pronouns
            ?: listOf("ich", "du", "er/sie/es", "wir", "ihr", "sie")

        val aux = data["aux"]?.jsonPrimitive?.contentOrNull

        return VerbConjugation(infinitive, conjugationMap, pronouns, aux)
    }

    private fun parseAdjectiveComparison(data: JsonObject): AdjectiveComparison {
        return AdjectiveComparison(
            positive = data["positive_masculine"]?.jsonPrimitive?.contentOrNull
                ?: data["positive_feminine"]?.jsonPrimitive?.contentOrNull,
            comparative = data["comparative"]?.jsonPrimitive?.contentOrNull,
            superlative = data["superlative"]?.jsonPrimitive?.contentOrNull
        )
    }
}

/**
 * Strategy for French (fr).
 */
class FrenchMorphologyStrategy : MorphologyStrategy {
    override fun parse(
        lemma: String,
        pos: String?,
        rootData: JsonObject,
        config: LanguageConfig?
    ): WordMorphology? {
        val formsElement = rootData[KEY_FORMS] ?: return null
        val categories = config?.categories
        val normalizedPos = pos?.lowercase()?.trim()

        return when (formsElement) {
            is JsonObject -> {
                // Heuristic: Check for French verb keys
                val hasVerbForms = formsElement.keys.any { key ->
                    key.startsWith("indicative_") || key.startsWith("subjunctive_") ||
                            key.startsWith(IMPERATIVE) || key.startsWith("conditional_") ||
                            key == INFINITIVE || key == PARTICIPLE_PRESENT || key == PARTICIPLE_PAST
                }

                if (hasVerbForms) {
                    val verbConfig = categories?.get(POS_VERB)
                    val conjugation = mapFrenchJsonToVerbConjugation(lemma, formsElement, verbConfig)
                    Verb(listOf(conjugation))
                } else null
            }
            is JsonArray -> {
                if (SharedMorphologyUtils.isNounLikeStructure(formsElement, normalizedPos)) {
                    val nounConfig = categories?.get(POS_NOUN)
                    val declension = SharedMorphologyUtils.parseNounDeclension(formsElement, nounConfig)
                    return Noun(declension)
                }
                null
            }
            else -> null
        }
    }

    private fun mapFrenchJsonToVerbConjugation(
        infinitive: String,
        data: JsonObject,
        categoryConfig: CategoryConfig?
    ): VerbConjugation {
        fun getForms(key: String): List<String> =
            data[key]?.jsonArray?.mapNotNull { (it as? JsonPrimitive)?.contentOrNull } ?: emptyList()

        val conjugationMap = mutableMapOf<String, List<String>>()
        val tenseLabels = categoryConfig?.conjugation_display?.tense_labels

        fun addFrenchTense(frenchKey: String, displayKey: String) {
            val forms = getForms(frenchKey)
            if (forms.isNotEmpty()) {
                val label = tenseLabels?.get(frenchKey) ?: displayKey
                conjugationMap[label] = forms
            }
        }

        addFrenchTense(INDICATIVE_PRESENT, "Indicative Present")
        addFrenchTense(INDICATIVE_IMPERFECT, "Indicative Imperfect")
        addFrenchTense(INDICATIVE_SIMPLE_PAST, "Indicative Simple Past")
        addFrenchTense(INDICATIVE_FUTURE, "Indicative Future")
        addFrenchTense(SUBJUNCTIVE_PRESENT, "Subjunctive Present")
        addFrenchTense(CONDITIONAL_PRESENT, "Conditional Present")
        addFrenchTense(IMPERATIVE, "Imperative")

        val pronouns = categoryConfig?.conjugation_display?.pronouns
            ?: listOf("je", "tu", "il/elle", "nous", "vous", "ils/elles")

        // French data sometimes puts aux in an array
        val aux = data["auxiliary"]?.jsonArray?.firstOrNull()?.jsonPrimitive?.contentOrNull
            ?: data["aux"]?.jsonPrimitive?.contentOrNull

        return VerbConjugation(infinitive, conjugationMap, pronouns, aux)
    }
}

/**
 * Fallback Strategy (Portuguese, Generic, etc.).
 * Handles flat list of inflections.
 */
class GenericMorphologyStrategy : MorphologyStrategy {
    override fun parse(
        lemma: String,
        pos: String?,
        rootData: JsonObject,
        config: LanguageConfig?
    ): WordMorphology? {
        val inflectionsElement = rootData[KEY_INFLECTIONS] as? JsonArray

        if (!inflectionsElement.isNullOrEmpty()) {
            val inflections = inflectionsElement.mapNotNull { element ->
                val obj = element.jsonObject
                val form = obj["form"]?.jsonPrimitive?.contentOrNull ?: return@mapNotNull null

                val features = (obj["grammatical_features"] as? JsonArray)
                    ?.mapNotNull { it.jsonPrimitive.contentOrNull }

                // FIX: Use 'tags' instead of 'grammatical_features'
                // FIX: Handle nullability with '?: emptyList()'
                Inflection(form = form, tags = features ?: emptyList())
            }

            // Note: Ensure GenericInflections accepts List<Inflection> if you haven't updated it yet
            if (inflections.isNotEmpty()) {
                return GenericInflections(inflections)
            }
        }
        return null
    }
}