package com.aletheia.app.ui.reader.tts

internal object RussianNumberNormalizer {
    private val yearInPrepositionalPattern = Regex(
        "(?<![\\p{L}\\p{N}])(\\d{4})\\s+(году|г\\.)",
        RegexOption.IGNORE_CASE
    )
    private val numberPattern = Regex(
        "(?<![\\p{L}\\p{N}])([+-]?)(\\d{1,18})(?:[,.](\\d+))?(?![\\p{L}\\p{N}])"
    )

    fun normalize(text: String): String {
        val prepared = text
            .replace(Regex("(?<=\\d)[\\u00A0\\u202F](?=\\d{3}(?:\\D|$))"), "")
            .replace(Regex("№\\s*(?=\\d)"), "номер ")
        val yearsNormalized = yearInPrepositionalPattern.replace(prepared) { match ->
            val year = match.groupValues[1].toInt()
            "${yearInPrepositionalWords(year)} ${match.groupValues[2]}"
        }
        return numberPattern.replace(yearsNormalized) { match ->
            val sign = when (match.groupValues[1]) {
                "-" -> "минус "
                "+" -> "плюс "
                else -> ""
            }
            val integerDigits = match.groupValues[2]
            val integer = if (integerDigits.length > 1 && integerDigits.startsWith('0')) {
                integerDigits.toDigitWords()
            } else {
                integerDigits.toLongOrNull()?.let(::integerToWords) ?: integerDigits.toDigitWords()
            }
            val fractionDigits = match.groupValues[3]
            if (fractionDigits.isBlank()) "$sign$integer" else "$sign$integer запятая ${fractionDigits.toDigitWords()}"
        }
    }

    private fun yearInPrepositionalWords(value: Int): String {
        require(value in 1000..9999)
        if (value % 1000 == 0) return EXACT_THOUSANDTH_PREPOSITIONAL[value / 1000]
        val thousands = value / 1000
        val thousandsCardinal = underThousandToWords(thousands, feminine = true)
        val thousandsWords = if (thousandsCardinal == "одна") {
            SCALES[1]!!.formFor(thousands)
        } else {
            "$thousandsCardinal ${SCALES[1]!!.formFor(thousands)}"
        }
        return "$thousandsWords ${ordinalUnderThousandPrepositional(value % 1000)}"
    }

    private fun ordinalUnderThousandPrepositional(value: Int): String {
        require(value in 1..999)
        val lastTwo = value % 100
        val units = value % 10
        return when {
            units != 0 && lastTwo !in 11..19 ->
                "${integerToWords((value - units).toLong())} ${ORDINAL_UNITS_PREPOSITIONAL[units]}".trim()
            lastTwo in 11..19 ->
                "${integerToWords((value - lastTwo).toLong())} ${ORDINAL_TEENS_PREPOSITIONAL[lastTwo - 10]}".trim()
            lastTwo >= 20 ->
                "${integerToWords((value - lastTwo).toLong())} ${ORDINAL_TENS_PREPOSITIONAL[lastTwo / 10]}".trim()
            else -> ORDINAL_HUNDREDS_PREPOSITIONAL[value / 100]
        }
    }

    private fun integerToWords(value: Long): String {
        if (value == 0L) return DIGITS[0]
        if (value < 0L) return "минус ${integerToWords(-value)}"
        val groups = mutableListOf<Int>()
        var remainder = value
        while (remainder > 0) {
            groups += (remainder % 1_000).toInt()
            remainder /= 1_000
        }
        return groups.indices.reversed().mapNotNull { groupIndex ->
            val group = groups[groupIndex]
            if (group == 0) return@mapNotNull null
            val feminine = groupIndex == 1
            val words = underThousandToWords(group, feminine)
            val scale = SCALES.getOrNull(groupIndex)
            if (scale == null) words else "$words ${scale.formFor(group)}"
        }.joinToString(" ")
    }

    private fun underThousandToWords(value: Int, feminine: Boolean): String {
        val words = mutableListOf<String>()
        val hundreds = value / 100
        if (hundreds > 0) words += HUNDREDS[hundreds]
        val tail = value % 100
        when {
            tail in 10..19 -> words += TEENS[tail - 10]
            else -> {
                val tens = tail / 10
                if (tens > 0) words += TENS[tens]
                val units = tail % 10
                if (units > 0) {
                    words += when {
                        feminine && units == 1 -> "одна"
                        feminine && units == 2 -> "две"
                        else -> DIGITS[units]
                    }
                }
            }
        }
        return words.joinToString(" ")
    }

    private fun String.toDigitWords(): String = map { DIGITS[it.digitToInt()] }.joinToString(" ")

    private data class Scale(val one: String, val few: String, val many: String) {
        fun formFor(value: Int): String {
            val lastTwo = value % 100
            if (lastTwo in 11..14) return many
            return when (value % 10) {
                1 -> one
                2, 3, 4 -> few
                else -> many
            }
        }
    }

    private val DIGITS = listOf("ноль", "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять")
    private val TEENS = listOf(
        "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать",
        "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"
    )
    private val TENS = listOf("", "десять", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто")
    private val HUNDREDS = listOf("", "сто", "двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот")
    private val ORDINAL_UNITS_PREPOSITIONAL = listOf("", "первом", "втором", "третьем", "четвёртом", "пятом", "шестом", "седьмом", "восьмом", "девятом")
    private val ORDINAL_TEENS_PREPOSITIONAL = listOf("десятом", "одиннадцатом", "двенадцатом", "тринадцатом", "четырнадцатом", "пятнадцатом", "шестнадцатом", "семнадцатом", "восемнадцатом", "девятнадцатом")
    private val ORDINAL_TENS_PREPOSITIONAL = listOf("", "десятом", "двадцатом", "тридцатом", "сороковом", "пятидесятом", "шестидесятом", "семидесятом", "восьмидесятом", "девяностом")
    private val ORDINAL_HUNDREDS_PREPOSITIONAL = listOf("", "сотом", "двухсотом", "трёхсотом", "четырёхсотом", "пятисотом", "шестисотом", "семисотом", "восьмисотом", "девятисотом")
    private val EXACT_THOUSANDTH_PREPOSITIONAL = listOf("", "тысячном", "двухтысячном", "трёхтысячном", "четырёхтысячном", "пятитысячном", "шеститысячном", "семитысячном", "восьмитысячном", "девятитысячном")
    private val SCALES = listOf(
        null,
        Scale("тысяча", "тысячи", "тысяч"),
        Scale("миллион", "миллиона", "миллионов"),
        Scale("миллиард", "миллиарда", "миллиардов"),
        Scale("триллион", "триллиона", "триллионов"),
        Scale("квадриллион", "квадриллиона", "квадриллионов")
    )
}
