@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.utils.dictionary

import eu.gaudian.translator.model.repository.DictionaryWordEntry
import eu.gaudian.translator.utils.Log
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
 * Reusable parsing helpers for local (on-device) dictionary entries.
 *
 * This provides a stable, display-independent API so any feature can query
 * translations, synonyms, hyponyms, alternative spellings, etc. for a given word.
 */

data class LocalTranslationInfo(
    val languageCode: String,
    val word: String,
    val sense: String? = null,
    val tags: List<String> = emptyList()
)

data class LocalRelatedWordInfo(
    /** Relation type key from the JSON, e.g. "synonyms", "hyponyms", ... */
    val relationType: String,
    val word: String,
    val senseIndex: String? = null
)

data class LocalAlternativeForm(
    val form: String,
    val tags: List<String> = emptyList()
)

/**
 * Aggregated semantic information for a local dictionary word.
 */
data class LocalDictionaryWordInfo(
    val word: String,
    val langCode: String,
    val pos: String?,
    val translations: List<LocalTranslationInfo>,
    val relatedWords: List<LocalRelatedWordInfo>,
    val alternativeForms: List<LocalAlternativeForm>
) {
    val synonyms: List<LocalRelatedWordInfo>
        get() = relatedWords.filter { it.relationType == "synonyms" }

    val hyponyms: List<LocalRelatedWordInfo>
        get() = relatedWords.filter { it.relationType == "hyponyms" }
}

object LocalDictionaryAccess {

    private val json = Json { ignoreUnknownKeys = true }
    private const val TAG = "LocalDictionaryAccess"

    /**
     * Parse a [DictionaryWordEntry] (from the local DB) into a structured
     * [LocalDictionaryWordInfo] model.
     */
    fun parseWordInfo(entry: DictionaryWordEntry): LocalDictionaryWordInfo? {
        val root: JsonElement = try {
            json.parseToJsonElement(entry.json)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to parse local dictionary JSON for '${entry.word}': ${e.message}", e)
            return null
        }

        val obj = root as? JsonObject ?: return null

        val translations = parseTranslations(obj)
        val related = parseRelations(obj)
        val alternatives = parseAlternativeForms(obj)

        return LocalDictionaryWordInfo(
            word = entry.word,
            langCode = entry.langCode,
            pos = entry.pos,
            translations = translations,
            relatedWords = related,
            alternativeForms = alternatives
        )
    }

    // --- Internal helpers ---

    private fun parseTranslations(obj: JsonObject): List<LocalTranslationInfo> {
        val translationsElement = obj["translations"]
        val array = translationsElement as? JsonArray ?: return emptyList()

        return array.mapNotNull { element ->
            val o = element.jsonObject
            val langCode = o["lang_code"]?.jsonPrimitive?.contentOrNull
                ?: o["language_code"]?.jsonPrimitive?.contentOrNull
            val word = o["word"]?.jsonPrimitive?.contentOrNull

            if (langCode.isNullOrBlank() || word.isNullOrBlank()) {
                return@mapNotNull null
            }

            val sense = o["sense"]?.jsonPrimitive?.contentOrNull
                ?: o["sense_index"]?.jsonPrimitive?.contentOrNull
            val tags = (o["tags"] as? JsonArray)
                ?.mapNotNull { it.jsonPrimitive.contentOrNull }
                ?: emptyList()

            LocalTranslationInfo(
                languageCode = langCode,
                word = word,
                sense = sense,
                tags = tags
            )
        }
    }

    private fun parseRelations(obj: JsonObject): List<LocalRelatedWordInfo> {
        val relationsElement = obj["relations"] as? JsonObject ?: return emptyList()

        val result = mutableListOf<LocalRelatedWordInfo>()
        for ((relationType, value) in relationsElement) {
            val array = value as? JsonArray ?: continue
            array.forEach { element ->
                val o = element.jsonObject
                val word = o["word"]?.jsonPrimitive?.contentOrNull
                if (word.isNullOrBlank()) return@forEach
                val senseIndex = o["sense_index"]?.jsonPrimitive?.contentOrNull
                result += LocalRelatedWordInfo(
                    relationType = relationType,
                    word = word,
                    senseIndex = senseIndex
                )
            }
        }
        return result
    }

    /**
     * Extracts forms tagged as "alternative" (e.g. alternative spellings) from
     * the generic "forms" array used by some dictionaries.
     */
    private fun parseAlternativeForms(obj: JsonObject): List<LocalAlternativeForm> {
        val formsElement = obj["forms"] ?: return emptyList()
        val formsArray: JsonArray = when (formsElement) {
            is JsonArray -> formsElement
            is JsonObject -> formsElement["forms"] as? JsonArray ?: return emptyList()
            else -> return emptyList()
        }

        return formsArray.mapNotNull { element ->
            val o = element.jsonObject
            val form = o["form"]?.jsonPrimitive?.contentOrNull ?: return@mapNotNull null
            val tagsArray = o["tags"] as? JsonArray
            val tags = tagsArray?.mapNotNull { it.jsonPrimitive.contentOrNull } ?: emptyList()
            if ("alternative" !in tags) return@mapNotNull null
            LocalAlternativeForm(form = form, tags = tags)
        }
    }
}