package dev.m1sk9.lunaticChat.paper.converter

/**
 * Converts romanji text to hiragana using Trie data structure.
 */
object KanaConverter {
    private class TrieNode(
        val children: Map<Char, TrieNode>,
        val value: String? = null,
    )

    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(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 TrieNode(node.children, value)

        val char = key[0]
        val child = node.children[char] ?: TrieNode(emptyMap())
        return TrieNode(node.children + (char to insert(child, key.substring(1), value)), node.value)
    }

    /**
     * Walks the trie from [start] and returns the longest mapping that matches, paired with the
     * number of characters it consumed, or null when no prefix of the input maps to kana.
     */
    private fun longestMatch(
        input: String,
        start: Int,
    ): Pair<String, Int>? {
        var node = romanjiTrie
        var match: Pair<String, Int>? = null
        var i = start

        while (true) {
            node.value?.let { match = it to (i - start) }
            if (i >= input.length) break
            node = node.children[input[i]] ?: break
            i++
        }

        return match
    }

    /**
     * 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
                }
            }

            // If no match found, this character cannot be converted - not valid romaji
            val (_, matchLength) = longestMatch(lowerInput, i) ?: 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
                }
            }

            val match = longestMatch(lowerInput, i)
            if (match != null) {
                result.append(match.first)
                i += match.second
            } else {
                result.append(lowerInput[i])
                i++
            }
        }

        return result.toString()
    }
}
