package dev.m1sk9.lunaticChat.engine.converter

/**
 * Converts romanji text to hiragana using Trie data structure.
 */
object KanaConverter {
    sealed class TrieNode {
        data class Leaf(
            val value: String,
        ) : TrieNode()

        data class Branch(
            val children: Map<Char, TrieNode>,
            val value: String? = null,
        ) : TrieNode()
    }

    private val romanjiTrie: TrieNode = buildTrie()

    private fun buildTrie(): TrieNode {
        val mappings =
            listOf(
                // 4文字変換 (x/l prefix small characters)
                "xtsu" to "っ",
                "ltsu" to "っ",
                // 3文字変換 (x/l prefix small characters)
                "xtu" to "っ",
                "ltu" to "っ",
                "xya" to "ゃ",
                "lya" to "ゃ",
                "xyu" to "ゅ",
                "lyu" to "ゅ",
                "xyo" to "ょ",
                "lyo" to "ょ",
                "xwa" to "ゎ",
                "lwa" to "ゎ",
                // 3文字変換
                "kya" to "きゃ",
                "kyi" to "きぃ",
                "kyu" to "きゅ",
                "kye" to "きぇ",
                "kyo" to "きょ",
                "gya" to "ぎゃ",
                "gyi" to "ぎぃ",
                "gyu" to "ぎゅ",
                "gye" to "ぎぇ",
                "gyo" to "ぎょ",
                "sha" to "しゃ",
                "shi" to "し",
                "shu" to "しゅ",
                "she" to "しぇ",
                "sho" to "しょ",
                "sya" to "しゃ",
                "syi" to "しぃ",
                "syu" to "しゅ",
                "sye" to "しぇ",
                "syo" to "しょ",
                "zya" to "じゃ",
                "zyi" to "じぃ",
                "zyu" to "じゅ",
                "zye" to "じぇ",
                "zyo" to "じょ",
                "jya" to "じゃ",
                "jyi" to "じぃ",
                "jyu" to "じゅ",
                "jye" to "じぇ",
                "jyo" to "じょ",
                "cha" to "ちゃ",
                "chi" to "ち",
                "chu" to "ちゅ",
                "che" to "ちぇ",
                "cho" to "ちょ",
                "tya" to "ちゃ",
                "tyi" to "ちぃ",
                "tyu" to "ちゅ",
                "tye" to "ちぇ",
                "tyo" to "ちょ",
                "dya" to "ぢゃ",
                "dyi" to "ぢぃ",
                "dyu" to "ぢゅ",
                "dye" to "ぢぇ",
                "dyo" to "ぢょ",
                "nya" to "にゃ",
                "nyi" to "にぃ",
                "nyu" to "にゅ",
                "nye" to "にぇ",
                "nyo" to "にょ",
                "hya" to "ひゃ",
                "hyi" to "ひぃ",
                "hyu" to "ひゅ",
                "hye" to "ひぇ",
                "hyo" to "ひょ",
                "bya" to "びゃ",
                "byi" to "びぃ",
                "byu" to "びゅ",
                "bye" to "びぇ",
                "byo" to "びょ",
                "pya" to "ぴゃ",
                "pyi" to "ぴぃ",
                "pyu" to "ぴゅ",
                "pye" to "ぴぇ",
                "pyo" to "ぴょ",
                "mya" to "みゃ",
                "myi" to "みぃ",
                "myu" to "みゅ",
                "mye" to "みぇ",
                "myo" to "みょ",
                "rya" to "りゃ",
                "ryi" to "りぃ",
                "ryu" to "りゅ",
                "rye" to "りぇ",
                "ryo" to "りょ",
                "tsu" to "つ",
                "thi" to "てぃ",
                "dhi" to "でぃ",
                "dhu" to "でゅ",
                "wha" to "うぁ",
                "whi" to "うぃ",
                "whe" to "うぇ",
                "who" to "うぉ",
                // 2文字変換 (x/l prefix small vowels)
                "xa" to "ぁ",
                "la" to "ぁ",
                "xi" to "ぃ",
                "li" to "ぃ",
                "xu" to "ぅ",
                "lu" to "ぅ",
                "xe" to "ぇ",
                "le" to "ぇ",
                "xo" to "ぉ",
                "lo" to "ぉ",
                // 2文字変換
                "ka" to "か",
                "ki" to "き",
                "ku" to "く",
                "ke" to "け",
                "ko" to "こ",
                "ga" to "が",
                "gi" to "ぎ",
                "gu" to "ぐ",
                "ge" to "げ",
                "go" to "ご",
                "sa" to "さ",
                "si" to "し",
                "su" to "す",
                "se" to "せ",
                "so" to "そ",
                "za" to "ざ",
                "zi" to "じ",
                "zu" to "ず",
                "ze" to "ぜ",
                "zo" to "ぞ",
                "ja" to "じゃ",
                "ji" to "じ",
                "ju" to "じゅ",
                "je" to "じぇ",
                "jo" to "じょ",
                "ta" to "た",
                "ti" to "ち",
                "tu" to "つ",
                "te" to "て",
                "to" to "と",
                "da" to "だ",
                "di" to "ぢ",
                "du" to "づ",
                "de" to "で",
                "do" to "ど",
                "na" to "な",
                "ni" to "に",
                "nu" to "ぬ",
                "ne" to "ね",
                "no" to "の",
                "ha" to "は",
                "hi" to "ひ",
                "hu" to "ふ",
                "he" to "へ",
                "ho" to "ほ",
                "fu" to "ふ",
                "ba" to "ば",
                "bi" to "び",
                "bu" to "ぶ",
                "be" to "べ",
                "bo" to "ぼ",
                "pa" to "ぱ",
                "pi" to "ぴ",
                "pu" to "ぷ",
                "pe" to "ぺ",
                "po" to "ぽ",
                "ma" to "ま",
                "mi" to "み",
                "mu" to "む",
                "me" to "め",
                "mo" to "も",
                "ya" to "や",
                "yi" to "い",
                "yu" to "ゆ",
                "ye" to "いぇ",
                "yo" to "よ",
                "ra" to "ら",
                "ri" to "り",
                "ru" to "る",
                "re" to "れ",
                "ro" to "ろ",
                "wa" to "わ",
                "wi" to "ゐ",
                "wu" to "う",
                "we" to "ゑ",
                "wo" to "を",
                "nn" to "ん",
                // 1文字変換
                "a" to "あ",
                "i" to "い",
                "u" to "う",
                "e" to "え",
                "o" to "お",
                "n" to "ん",
            )

        return insertAll(TrieNode.Branch(emptyMap()), mappings)
    }

    private fun insertAll(
        root: TrieNode,
        mappings: List<Pair<String, String>>,
    ): TrieNode {
        var current = root
        for ((key, value) in mappings) {
            current = insert(current, key, value)
        }
        return current
    }

    private fun insert(
        node: TrieNode,
        key: String,
        value: String,
    ): TrieNode {
        if (key.isEmpty()) {
            return when (node) {
                is TrieNode.Branch -> TrieNode.Branch(node.children, value)
                is TrieNode.Leaf -> TrieNode.Leaf(value)
            }
        }

        return when (node) {
            is TrieNode.Branch -> {
                val char = key[0]
                val child = node.children[char] ?: TrieNode.Branch(emptyMap())
                val newChild = insert(child, key.substring(1), value)
                TrieNode.Branch(node.children + (char to newChild), node.value)
            }
            is TrieNode.Leaf -> node
        }
    }

    /**
     * Checks if the input can be fully converted to hiragana without any remaining alphabetic characters.
     * This validates that the input is valid romaji before attempting conversion.
     *
     * @param input The text to validate
     * @return true if the input is valid romaji, false otherwise
     */
    fun isValidRomaji(input: String): Boolean {
        val lowerInput = input.lowercase()
        var i = 0

        while (i < lowerInput.length) {
            val char = lowerInput[i]

            // Non-alphabetic characters are allowed (spaces, numbers, symbols)
            if (char !in 'a'..'z') {
                i++
                continue
            }

            // Check for double consonant (valid in romaji for っ)
            if (i + 1 < lowerInput.length) {
                val next = lowerInput[i + 1]
                if (char == next && char in "bcdfghjklmpqrstvwxyz") {
                    i++
                    continue
                }
            }

            // Try to find the longest match in the trie
            var node: TrieNode = romanjiTrie
            var matchLength = 0
            var j = i

            while (j < lowerInput.length && lowerInput[j] in 'a'..'z') {
                node =
                    when (node) {
                        is TrieNode.Branch -> {
                            if (node.value != null) {
                                matchLength = j - i
                            }
                            node.children[lowerInput[j]] ?: break
                        }
                        is TrieNode.Leaf -> {
                            matchLength = j - i
                            break
                        }
                    }
                j++
            }

            // Check for terminal match
            if (node is TrieNode.Leaf) {
                matchLength = j - i
            } else if (node is TrieNode.Branch && node.value != null) {
                matchLength = j - i
            }

            // If no match found, this character cannot be converted - not valid romaji
            if (matchLength == 0) {
                return false
            }

            i += matchLength
        }

        return true
    }

    /**
     * Converts romanji text to hiragana.
     *
     * @param input The romanji text to convert
     * @return The converted hiragana text
     */
    fun toHiragana(input: String): String {
        val result = StringBuilder()
        var i = 0
        val lowerInput = input.lowercase()

        while (i < lowerInput.length) {
            if (i + 1 < lowerInput.length) {
                val current = lowerInput[i]
                val next = lowerInput[i + 1]
                if (current == next && current in "bcdfghjklmpqrstvwxyz") {
                    result.append('っ')
                    i++
                    continue
                }
            }

            var node: TrieNode = romanjiTrie
            var lastMatch: Pair<String, Int>? = null
            var j = i

            while (j < lowerInput.length) {
                node =
                    when (node) {
                        is TrieNode.Branch -> {
                            if (node.value != null) {
                                lastMatch = node.value to (j - i)
                            }

                            node.children[lowerInput[j]] ?: break
                        }
                        is TrieNode.Leaf -> {
                            lastMatch = node.value to (j - i)
                            break
                        }
                    }
                j++
            }

            if (node is TrieNode.Leaf) {
                lastMatch = node.value to (j - i)
            } else if (node is TrieNode.Branch && node.value != null) {
                lastMatch = node.value to (j - i)
            }

            if (lastMatch != null) {
                result.append(lastMatch.first)
                i += lastMatch.second
            } else {
                result.append(lowerInput[i])
                i++
            }
        }

        return result.toString()
    }
}
