@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.grammar

import eu.gaudian.translator.utils.Log
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

/**
 * Maps raw local dictionary JSON into grammar models used by the UI.
 *
 * This implementation uses the Strategy Pattern to delegate parsing logic
 * based on the language code.
 */
object LocalDictionaryMorphologyMapper {

    private val strategies = mapOf(
        "de" to GermanMorphologyStrategy(),
        "fr" to FrenchMorphologyStrategy()
    )

    private val genericStrategy = GenericMorphologyStrategy()

    /**
     * Parse morphology information (conjugation/declension/inflections) from a
     * local dictionary entry.
     *
     * @param langCode ISO language code, e.g. "de".
     * @param pos Part of speech as stored with the entry, if available.
     * @param lemma The base form / headword, used as infinitive for verbs.
     * @param data Root JSON object of the dictionary entry.
     * @param languageConfig Optional language configuration, used for display
     * metadata like pronoun order and tense/case labels when available.
     */
    fun parseMorphology(
        langCode: String,
        pos: String?,
        lemma: String,
        data: JsonObject,
        languageConfig: LanguageConfig? = null
    ): WordMorphology? {
        Log.d("LocalDictionaryMorphologyMapper", "Parsing morphology for $langCode, word: $lemma")

        // 1. Try specific language strategy
        val specificStrategy = strategies[langCode]
        if (specificStrategy != null) {
            val result = specificStrategy.parse(lemma, pos, data, languageConfig)
            if (result != null) return result
        }

        // 2. Fallback to generic strategy (valid for any language with 'inflections' array)
        return genericStrategy.parse(lemma, pos, data, languageConfig)
    }

    /**
     * Overload that uses parsed [DictionaryEntryData] instead of raw JSON.
     */
    @Suppress("unused")
    fun parseMorphology(
        langCode: String,
        pos: String?,
        lemma: String,
        entryData: DictionaryEntryData,
        languageConfig: LanguageConfig? = null
    ): WordMorphology? {
        Log.d("LocalDictionaryMorphologyMapper", "Parsing morphology (structured) for $langCode, word: $lemma")

        // 1. Handle adjective forms for applicable languages (check this first)
        Log.d("LocalDictionaryMorphologyMapper", "Checking adjective: langCode=$langCode, pos=$pos, formsCount=${entryData.forms.size}")
        
        if (AdjectiveVariationsParser.isAdjectiveWithVariations(langCode, pos, entryData.forms)) {
            Log.d("LocalDictionaryMorphologyMapper", "Creating adjective morphology for $lemma")
            val comparison = AdjectiveComparison() // Empty comparison, forms will be accessed directly from entryData
            return WordMorphology.Adjective(comparison)
        }

        // 2. Try specific language strategy if 'forms' data is available
        val specificStrategy = strategies[langCode]
        // Check isNotEmpty() because forms is now a List<FormData>, not nullable JsonElement
        if (specificStrategy != null && entryData.forms.isNotEmpty()) {

            // Reconstruct the JsonArray for forms because the strategy expects raw JSON structures
            val formsArray = JsonArray(entryData.forms.map { formData ->
                JsonObject(
                    mapOf(
                        "form" to JsonPrimitive(formData.form),
                        "tags" to JsonArray(formData.tags.map { JsonPrimitive(it) }),
                        "ipas" to JsonArray(formData.ipas.map { JsonPrimitive(it) })
                    )
                )
            })

            // Construct a minimal JsonObject for the strategy which expects "forms" key
            val syntheticData = JsonObject(mapOf("forms" to formsArray))

            val result = specificStrategy.parse(lemma, pos, syntheticData, languageConfig)
            if (result != null) return result
        }

        // 3. Fallback to generic strategy using parsed inflections
        if (entryData.inflections.isNotEmpty()) {
            val inflections = entryData.inflections.map {
                Inflection(it.form, it.grammaticalFeatures)
            }
            return WordMorphology.GenericInflections(inflections)
        }

        return null
    }
}

