// morphology-ru.el - Russian morphology: noun declension and verb conjugation.
//
// Russian is a fusional language with:
//   - 6 grammatical cases: nominative, accusative, genitive, dative,
//                          instrumental, prepositional
//   - 3 genders:           masculine (m), feminine (f), neuter (n)
//   - 2 numbers:           singular (sg), plural (pl)
//   - Aspect:              perfective / imperfective (caller feature)
//   - No articles
//   - Cyrillic script (El strings are Unicode-transparent)
//
// Conventions:
//   gender: "m" | "f" | "n"
//   case:   "nom" | "acc" | "gen" | "dat" | "ins" | "pre"
//   number: "sg" | "pl"
//   person: "1" | "2" | "3"
//   tense:  "present" | "past" | "future"
//
// Stem-type classification:
//   "hard"    - stem ends in a hard consonant
//   "soft"    - stem ends in a soft consonant (+ ь) or a hushing consonant
//   "special" - lexically irregular
//
// Depends on: language-profile (str_eq, str_len, str_slice, str_drop_last,
//             str_ends_with)

// ── Gender heuristic from ending ──────────────────────────────────────────────
//
// Russian gender is mostly predictable from the nominative singular ending:
//   ends in consonant        → masculine (стол, дом)
//   ends in -ь               → masculine or feminine (must be lexical)
//   ends in -а / -я          → feminine (женщина, земля)
//   ends in -о / -е          → neuter (слово, море)
//
// The heuristic returns the most probable gender.  Caller should override
// for known exceptions (путь, рубль are masc despite -ь).

import "morphology.el"
fn ru_gender(noun: String) -> String {
    let n: Int = str_len(noun)
    if n == 0 { return "m" }
    let last: String = str_slice(noun, n - 1, n)

    // Neuter: -о or -е
    if str_eq(last, "о") { return "n" }
    if str_eq(last, "е") { return "n" }
    if str_eq(last, "ё") { return "n" }

    // Feminine: -а or -я
    if str_eq(last, "а") { return "f" }
    if str_eq(last, "я") { return "f" }

    // Soft sign: ambiguous — default feminine (most common case)
    if str_eq(last, "ь") { return "f" }

    // Consonant or й: masculine
    return "m"
}

// ── Stem-type classifier ──────────────────────────────────────────────────────
//
// "hard"  — ends in a plain consonant (стол) or -а (женщина) or -о (слово)
// "soft"  — ends in -ь, -й, -я, -е, -ие, -ия
// "sibilant" — ends in ж/ш/ч/щ (triggers spelling rules)

fn ru_stem_type(noun: String, gender: String) -> String {
    let n: Int = str_len(noun)
    if n == 0 { return "hard" }
    let last: String = str_slice(noun, n - 1, n)

    if str_eq(last, "ь") { return "soft" }
    if str_eq(last, "й") { return "soft" }
    if str_eq(last, "я") { return "soft" }
    if str_eq(last, "е") { return "soft" }

    // Sibilants (hushing consonants: ж ш ч щ) — use sibilant rules
    if str_eq(last, "ж") { return "sibilant" }
    if str_eq(last, "ш") { return "sibilant" }
    if str_eq(last, "ч") { return "sibilant" }
    if str_eq(last, "щ") { return "sibilant" }

    return "hard"
}

// ── Noun declension ───────────────────────────────────────────────────────────
//
// Main entry point.  Handles irregulars by lemma, then dispatches to the
// paradigm appropriate for (gender × stem_type).

fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String {
    // ── Lexical irregulars ──────────────────────────────────────────────────

    // человек (person) — plural suppletive: люди
    if str_eq(noun, "человек") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "человек" }
            if str_eq(gram_case, "acc") { return "человека" }
            if str_eq(gram_case, "gen") { return "человека" }
            if str_eq(gram_case, "dat") { return "человеку" }
            if str_eq(gram_case, "ins") { return "человеком" }
            if str_eq(gram_case, "pre") { return "человеке" }
        }
        // plural: люди paradigm
        if str_eq(gram_case, "nom") { return "люди" }
        if str_eq(gram_case, "acc") { return "людей" }
        if str_eq(gram_case, "gen") { return "людей" }
        if str_eq(gram_case, "dat") { return "людям" }
        if str_eq(gram_case, "ins") { return "людьми" }
        if str_eq(gram_case, "pre") { return "людях" }
        return "люди"
    }

    // ребёнок (child) — plural suppletive: дети
    if str_eq(noun, "ребёнок") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "ребёнок" }
            if str_eq(gram_case, "acc") { return "ребёнка" }
            if str_eq(gram_case, "gen") { return "ребёнка" }
            if str_eq(gram_case, "dat") { return "ребёнку" }
            if str_eq(gram_case, "ins") { return "ребёнком" }
            if str_eq(gram_case, "pre") { return "ребёнке" }
        }
        if str_eq(gram_case, "nom") { return "дети" }
        if str_eq(gram_case, "acc") { return "детей" }
        if str_eq(gram_case, "gen") { return "детей" }
        if str_eq(gram_case, "dat") { return "детям" }
        if str_eq(gram_case, "ins") { return "детьми" }
        if str_eq(gram_case, "pre") { return "детях" }
        return "дети"
    }

    // время (time) — neuter in -мя (heteroclitic declension)
    if str_eq(noun, "время") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "время" }
            if str_eq(gram_case, "acc") { return "время" }
            if str_eq(gram_case, "gen") { return "времени" }
            if str_eq(gram_case, "dat") { return "времени" }
            if str_eq(gram_case, "ins") { return "временем" }
            if str_eq(gram_case, "pre") { return "времени" }
        }
        if str_eq(gram_case, "nom") { return "времена" }
        if str_eq(gram_case, "acc") { return "времена" }
        if str_eq(gram_case, "gen") { return "времён" }
        if str_eq(gram_case, "dat") { return "временам" }
        if str_eq(gram_case, "ins") { return "временами" }
        if str_eq(gram_case, "pre") { return "временах" }
        return "времена"
    }

    // имя (name) — neuter in -мя
    if str_eq(noun, "имя") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "имя" }
            if str_eq(gram_case, "acc") { return "имя" }
            if str_eq(gram_case, "gen") { return "имени" }
            if str_eq(gram_case, "dat") { return "имени" }
            if str_eq(gram_case, "ins") { return "именем" }
            if str_eq(gram_case, "pre") { return "имени" }
        }
        if str_eq(gram_case, "nom") { return "имена" }
        if str_eq(gram_case, "acc") { return "имена" }
        if str_eq(gram_case, "gen") { return "имён" }
        if str_eq(gram_case, "dat") { return "именам" }
        if str_eq(gram_case, "ins") { return "именами" }
        if str_eq(gram_case, "pre") { return "именах" }
        return "имена"
    }

    // путь (way/path) — masculine soft with unique instrumental
    if str_eq(noun, "путь") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "путь" }
            if str_eq(gram_case, "acc") { return "путь" }
            if str_eq(gram_case, "gen") { return "пути" }
            if str_eq(gram_case, "dat") { return "пути" }
            if str_eq(gram_case, "ins") { return "путём" }
            if str_eq(gram_case, "pre") { return "пути" }
        }
        if str_eq(gram_case, "nom") { return "пути" }
        if str_eq(gram_case, "acc") { return "пути" }
        if str_eq(gram_case, "gen") { return "путей" }
        if str_eq(gram_case, "dat") { return "путям" }
        if str_eq(gram_case, "ins") { return "путями" }
        if str_eq(gram_case, "pre") { return "путях" }
        return "пути"
    }

    // мать (mother) — feminine with -ер- insert
    if str_eq(noun, "мать") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "мать" }
            if str_eq(gram_case, "acc") { return "мать" }
            if str_eq(gram_case, "gen") { return "матери" }
            if str_eq(gram_case, "dat") { return "матери" }
            if str_eq(gram_case, "ins") { return "матерью" }
            if str_eq(gram_case, "pre") { return "матери" }
        }
        if str_eq(gram_case, "nom") { return "матери" }
        if str_eq(gram_case, "acc") { return "матерей" }
        if str_eq(gram_case, "gen") { return "матерей" }
        if str_eq(gram_case, "dat") { return "матерям" }
        if str_eq(gram_case, "ins") { return "матерями" }
        if str_eq(gram_case, "pre") { return "матерях" }
        return "матери"
    }

    // дочь (daughter) — same insert pattern
    if str_eq(noun, "дочь") {
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return "дочь" }
            if str_eq(gram_case, "acc") { return "дочь" }
            if str_eq(gram_case, "gen") { return "дочери" }
            if str_eq(gram_case, "dat") { return "дочери" }
            if str_eq(gram_case, "ins") { return "дочерью" }
            if str_eq(gram_case, "pre") { return "дочери" }
        }
        if str_eq(gram_case, "nom") { return "дочери" }
        if str_eq(gram_case, "acc") { return "дочерей" }
        if str_eq(gram_case, "gen") { return "дочерей" }
        if str_eq(gram_case, "dat") { return "дочерям" }
        if str_eq(gram_case, "ins") { return "дочерями" }
        if str_eq(gram_case, "pre") { return "дочерях" }
        return "дочери"
    }

    // ── Regular paradigm dispatch ────────────────────────────────────────────

    let stype: String = ru_stem_type(noun, gender)
    return ru_decline_regular(noun, gender, stype, gram_case, number)
}

// ── Regular declension paradigms ─────────────────────────────────────────────
//
// Handles hard, soft, and sibilant stems across all three genders.

fn ru_decline_regular(noun: String, gender: String, stype: String, gram_case: String, number: String) -> String {
    if str_eq(gender, "m") {
        return ru_decline_masc(noun, stype, gram_case, number)
    }
    if str_eq(gender, "f") {
        return ru_decline_fem(noun, stype, gram_case, number)
    }
    return ru_decline_neut(noun, stype, gram_case, number)
}

// ── Masculine declension ──────────────────────────────────────────────────────
//
// Hard (стол): stem = noun itself (zero ending in nom sg)
//   Sg: стол, стол, стола, столу, столом, столе
//   Pl: столы, столы, столов, столам, столами, столах
//
// Soft ending in -й (трамвай → трамва-):
//   Sg: трамвай, трамвай, трамвая, трамваю, трамваем, трамвае
//   Pl: трамваи, трамваи, трамваев, трамваям, трамваями, трамваях
//
// Soft ending in -ь (рубль → рубл-):
//   Sg: рубль, рубль, рубля, рублю, рублём, рубле
//   Pl: рубли, рубли, рублей, рублям, рублями, рублях
//
// Sibilant (нож → нож-):
//   Same as hard but ins sg = -ом only when stressed; unstressed = -ем
//   Here we assume stressed stem and use -ом.

fn ru_decline_masc(noun: String, stype: String, gram_case: String, number: String) -> String {
    let n: Int = str_len(noun)

    if str_eq(stype, "soft") {
        let last: String = str_slice(noun, n - 1, n)

        // Stem in -й (трамвай)
        if str_eq(last, "й") {
            let stem: String = str_drop_last(noun, 1)
            if str_eq(number, "sg") {
                if str_eq(gram_case, "nom") { return noun }
                if str_eq(gram_case, "acc") { return noun }   // inanimate
                if str_eq(gram_case, "gen") { return stem + "я" }
                if str_eq(gram_case, "dat") { return stem + "ю" }
                if str_eq(gram_case, "ins") { return stem + "ем" }
                if str_eq(gram_case, "pre") { return stem + "е" }
                return noun
            }
            if str_eq(gram_case, "nom") { return stem + "и" }
            if str_eq(gram_case, "acc") { return stem + "и" }
            if str_eq(gram_case, "gen") { return stem + "ев" }
            if str_eq(gram_case, "dat") { return stem + "ям" }
            if str_eq(gram_case, "ins") { return stem + "ями" }
            if str_eq(gram_case, "pre") { return stem + "ях" }
            return stem + "и"
        }

        // Stem in -ь (рубль, гость)
        if str_eq(last, "ь") {
            let stem: String = str_drop_last(noun, 1)
            if str_eq(number, "sg") {
                if str_eq(gram_case, "nom") { return noun }
                if str_eq(gram_case, "acc") { return noun }
                if str_eq(gram_case, "gen") { return stem + "я" }
                if str_eq(gram_case, "dat") { return stem + "ю" }
                if str_eq(gram_case, "ins") { return stem + "ём" }
                if str_eq(gram_case, "pre") { return stem + "е" }
                return noun
            }
            if str_eq(gram_case, "nom") { return stem + "и" }
            if str_eq(gram_case, "acc") { return stem + "и" }
            if str_eq(gram_case, "gen") { return stem + "ей" }
            if str_eq(gram_case, "dat") { return stem + "ям" }
            if str_eq(gram_case, "ins") { return stem + "ями" }
            if str_eq(gram_case, "pre") { return stem + "ях" }
            return stem + "и"
        }
    }

    // Hard and sibilant: stem = noun (zero ending in nom sg)
    let stem: String = noun
    if str_eq(number, "sg") {
        if str_eq(gram_case, "nom") { return stem }
        if str_eq(gram_case, "acc") { return stem }           // inanimate; animate = gen
        if str_eq(gram_case, "gen") { return stem + "а" }
        if str_eq(gram_case, "dat") { return stem + "у" }
        if str_eq(gram_case, "ins") { return stem + "ом" }
        if str_eq(gram_case, "pre") { return stem + "е" }
        return stem
    }
    // Plural
    if str_eq(gram_case, "nom") { return stem + "ы" }
    if str_eq(gram_case, "acc") { return stem + "ы" }
    if str_eq(gram_case, "gen") { return stem + "ов" }
    if str_eq(gram_case, "dat") { return stem + "ам" }
    if str_eq(gram_case, "ins") { return stem + "ами" }
    if str_eq(gram_case, "pre") { return stem + "ах" }
    return stem + "ы"
}

// ── Feminine declension ───────────────────────────────────────────────────────
//
// Hard in -а (женщина):
//   Sg: женщина, женщину, женщины, женщине, женщиной, женщине
//   Pl: женщины, женщин, женщин, женщинам, женщинами, женщинах
//
// Soft in -я (земля):
//   Sg: земля, землю, земли, земле, землёй, земле
//   Pl: земли, земли, земель, землям, землями, землях
//
// Soft in -ь (тетрадь, ночь):
//   Sg: тетрадь, тетрадь, тетради, тетради, тетрадью, тетради
//   Pl: тетради, тетради, тетрадей, тетрадям, тетрадями, тетрадях

fn ru_decline_fem(noun: String, stype: String, gram_case: String, number: String) -> String {
    let n: Int = str_len(noun)
    let last: String = str_slice(noun, n - 1, n)

    // Soft in -ь
    if str_eq(last, "ь") {
        let stem: String = str_drop_last(noun, 1)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return noun }
            if str_eq(gram_case, "gen") { return stem + "и" }
            if str_eq(gram_case, "dat") { return stem + "и" }
            if str_eq(gram_case, "ins") { return stem + "ью" }
            if str_eq(gram_case, "pre") { return stem + "и" }
            return noun
        }
        if str_eq(gram_case, "nom") { return stem + "и" }
        if str_eq(gram_case, "acc") { return stem + "и" }
        if str_eq(gram_case, "gen") { return stem + "ей" }
        if str_eq(gram_case, "dat") { return stem + "ям" }
        if str_eq(gram_case, "ins") { return stem + "ями" }
        if str_eq(gram_case, "pre") { return stem + "ях" }
        return stem + "и"
    }

    // Soft in -я (земля, семья)
    if str_eq(last, "я") {
        let stem: String = str_drop_last(noun, 1)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return stem + "ю" }
            if str_eq(gram_case, "gen") { return stem + "и" }
            if str_eq(gram_case, "dat") { return stem + "е" }
            if str_eq(gram_case, "ins") { return stem + "ей" }
            if str_eq(gram_case, "pre") { return stem + "е" }
            return noun
        }
        if str_eq(gram_case, "nom") { return stem + "и" }
        if str_eq(gram_case, "acc") { return stem + "и" }
        // Genitive plural: zero ending (with possible fleeting vowel) — use -ей for soft
        if str_eq(gram_case, "gen") { return stem + "ей" }
        if str_eq(gram_case, "dat") { return stem + "ям" }
        if str_eq(gram_case, "ins") { return stem + "ями" }
        if str_eq(gram_case, "pre") { return stem + "ях" }
        return stem + "и"
    }

    // Hard in -а (женщина, страна, рука)
    if str_eq(last, "а") {
        let stem: String = str_drop_last(noun, 1)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return stem + "у" }
            if str_eq(gram_case, "gen") { return stem + "ы" }
            if str_eq(gram_case, "dat") { return stem + "е" }
            if str_eq(gram_case, "ins") { return stem + "ой" }
            if str_eq(gram_case, "pre") { return stem + "е" }
            return noun
        }
        // Plural: gen pl = zero ending (stem only) for most -а feminines
        if str_eq(gram_case, "nom") { return stem + "ы" }
        if str_eq(gram_case, "acc") { return stem + "ы" }
        if str_eq(gram_case, "gen") { return stem }
        if str_eq(gram_case, "dat") { return stem + "ам" }
        if str_eq(gram_case, "ins") { return stem + "ами" }
        if str_eq(gram_case, "pre") { return stem + "ах" }
        return stem + "ы"
    }

    // Fallback: treat as hard -а (unexpected ending)
    return noun
}

// ── Neuter declension ─────────────────────────────────────────────────────────
//
// Hard in -о (слово):
//   Sg: слово, слово, слова, слову, словом, слове
//   Pl: слова, слова, слов, словам, словами, словах
//
// Soft in -е (море, поле):
//   Sg: море, море, моря, морю, морем, море
//   Pl: моря, моря, морей, морям, морями, морях
//
// Soft in -ие (здание):
//   Sg: здание, здание, здания, зданию, зданием, здании
//   Pl: здания, здания, зданий, зданиям, зданиями, зданиях

fn ru_decline_neut(noun: String, stype: String, gram_case: String, number: String) -> String {
    let n: Int = str_len(noun)
    let last: String = str_slice(noun, n - 1, n)

    // Nouns in -ие (здание, здравоохранение)
    if str_ends_with(noun, "ие") {
        let stem: String = str_drop_last(noun, 2)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return noun }
            if str_eq(gram_case, "gen") { return stem + "ия" }
            if str_eq(gram_case, "dat") { return stem + "ию" }
            if str_eq(gram_case, "ins") { return stem + "ием" }
            if str_eq(gram_case, "pre") { return stem + "ии" }
            return noun
        }
        if str_eq(gram_case, "nom") { return stem + "ия" }
        if str_eq(gram_case, "acc") { return stem + "ия" }
        if str_eq(gram_case, "gen") { return stem + "ий" }
        if str_eq(gram_case, "dat") { return stem + "иям" }
        if str_eq(gram_case, "ins") { return stem + "иями" }
        if str_eq(gram_case, "pre") { return stem + "иях" }
        return stem + "ия"
    }

    // Soft in -е (море, поле)
    if str_eq(last, "е") {
        let stem: String = str_drop_last(noun, 1)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return noun }
            if str_eq(gram_case, "gen") { return stem + "я" }
            if str_eq(gram_case, "dat") { return stem + "ю" }
            if str_eq(gram_case, "ins") { return stem + "ем" }
            if str_eq(gram_case, "pre") { return noun }
            return noun
        }
        if str_eq(gram_case, "nom") { return stem + "я" }
        if str_eq(gram_case, "acc") { return stem + "я" }
        if str_eq(gram_case, "gen") { return stem + "ей" }
        if str_eq(gram_case, "dat") { return stem + "ям" }
        if str_eq(gram_case, "ins") { return stem + "ями" }
        if str_eq(gram_case, "pre") { return stem + "ях" }
        return stem + "я"
    }

    // Hard in -о (слово, окно, место)
    if str_eq(last, "о") {
        let stem: String = str_drop_last(noun, 1)
        if str_eq(number, "sg") {
            if str_eq(gram_case, "nom") { return noun }
            if str_eq(gram_case, "acc") { return noun }
            if str_eq(gram_case, "gen") { return stem + "а" }
            if str_eq(gram_case, "dat") { return stem + "у" }
            if str_eq(gram_case, "ins") { return stem + "ом" }
            if str_eq(gram_case, "pre") { return stem + "е" }
            return noun
        }
        if str_eq(gram_case, "nom") { return stem + "а" }
        if str_eq(gram_case, "acc") { return stem + "а" }
        // Genitive plural: zero ending (слов, окон, мест)
        if str_eq(gram_case, "gen") { return stem }
        if str_eq(gram_case, "dat") { return stem + "ам" }
        if str_eq(gram_case, "ins") { return stem + "ами" }
        if str_eq(gram_case, "pre") { return stem + "ах" }
        return stem + "а"
    }

    // Fallback
    return noun
}

// ── Past-tense agreement ──────────────────────────────────────────────────────
//
// Russian past tense agrees with the subject in gender and number.
// The past stem is derived from the infinitive (drop -ть/-ти/-чь).
//
// Endings: masc → (bare stem), fem → -а, neut → -о, pl → -и
//
// Special: verbs in -чь (мочь → мог), -ти (идти → шёл) are irregular.

fn ru_past_agree(verb_stem: String, gender: String, number: String) -> String {
    if str_eq(number, "pl") {
        return verb_stem + "и"
    }
    if str_eq(gender, "f") { return verb_stem + "а" }
    if str_eq(gender, "n") { return verb_stem + "о" }
    return verb_stem
}

// ── First-conjugation present tense ──────────────────────────────────────────
//
// Model: читать (чита-) → читаю, читаешь, читает, читаем, читаете, читают
// Model: писать (пиш-) — stem changes in conjugation (handled by caller)
//
// This function takes an already-computed present stem (e.g. "чита") and
// applies first-conjugation endings.
//
//   1sg: stem + -ю      (after vowel) or -у (after consonant)
//   2sg: stem + -ешь
//   3sg: stem + -ет
//   1pl: stem + -ем
//   2pl: stem + -ете
//   3pl: stem + -ют     (after vowel) or -ут (after consonant)

fn ru_conjugate_1st(stem: String, tense: String, person: String, number: String) -> String {
    if str_eq(tense, "present") {
        let n: Int = str_len(stem)
        let last: String = str_slice(stem, n - 1, n)
        let vowels: Bool = false
        let vowels =
            str_eq(last, "а") ||
            str_eq(last, "е") ||
            str_eq(last, "и") ||
            str_eq(last, "о") ||
            str_eq(last, "у") ||
            str_eq(last, "ю") ||
            str_eq(last, "я") ||
            str_eq(last, "э") ||
            str_eq(last, "ё") ||
            str_eq(last, "ы")

        if str_eq(number, "sg") {
            if str_eq(person, "1") {
                if vowels { return stem + "ю" }
                return stem + "у"
            }
            if str_eq(person, "2") { return stem + "ешь" }
            return stem + "ет"
        }
        if str_eq(person, "1") { return stem + "ем" }
        if str_eq(person, "2") { return stem + "ете" }
        if vowels { return stem + "ют" }
        return stem + "ут"
    }
    // Past and future handled by ru_conjugate
    return stem
}

// ── Second-conjugation present tense ─────────────────────────────────────────
//
// Model: говорить (говор-) → говорю, говоришь, говорит, говорим, говорите, говорят
//
//   1sg: stem + -ю  / -у
//   2sg: stem + -ишь
//   3sg: stem + -ит
//   1pl: stem + -им
//   2pl: stem + -ите
//   3pl: stem + -ят  / -ат

fn ru_conjugate_2nd(stem: String, tense: String, person: String, number: String) -> String {
    if str_eq(tense, "present") {
        let n: Int = str_len(stem)
        let last: String = str_slice(stem, n - 1, n)
        let after_vowel: Bool =
            str_eq(last, "а") ||
            str_eq(last, "е") ||
            str_eq(last, "и") ||
            str_eq(last, "о") ||
            str_eq(last, "у") ||
            str_eq(last, "ю") ||
            str_eq(last, "я") ||
            str_eq(last, "э") ||
            str_eq(last, "ё") ||
            str_eq(last, "ы")

        if str_eq(number, "sg") {
            if str_eq(person, "1") {
                if after_vowel { return stem + "ю" }
                return stem + "у"
            }
            if str_eq(person, "2") { return stem + "ишь" }
            return stem + "ит"
        }
        if str_eq(person, "1") { return stem + "им" }
        if str_eq(person, "2") { return stem + "ите" }
        if after_vowel { return stem + "ят" }
        return stem + "ат"
    }
    return stem
}

// ── Irregular verb forms ──────────────────────────────────────────────────────
//
// Returns the inflected form for known irregular verbs, or "" if unknown.
// gender is used for past tense agreement.

fn ru_irregular(verb: String, tense: String, person: String, number: String) -> String {
    // быть (to be) — present tense: есть (invariant) / rarely conjugated
    if str_eq(verb, "быть") {
        if str_eq(tense, "present") {
            // Modern Russian uses invariant "есть" for all persons in present
            return "есть"
        }
        // Future: буду, будешь, будет, будем, будете, будут
        if str_eq(tense, "future") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "буду" }
                if str_eq(person, "2") { return "будешь" }
                return "будет"
            }
            if str_eq(person, "1") { return "будем" }
            if str_eq(person, "2") { return "будете" }
            return "будут"
        }
        // Past: returned without gender agreement — caller must call ru_past_agree
        return ""
    }

    // идти (to go on foot) — present: иду, идёшь, идёт, идём, идёте, идут
    if str_eq(verb, "идти") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "иду" }
                if str_eq(person, "2") { return "идёшь" }
                return "идёт"
            }
            if str_eq(person, "1") { return "идём" }
            if str_eq(person, "2") { return "идёте" }
            return "идут"
        }
        // Past stem: шёл (m), шла (f), шло (n), шли (pl)
        return ""
    }

    // ехать (to go by vehicle) — present: еду, едешь, едет, едем, едете, едут
    if str_eq(verb, "ехать") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "еду" }
                if str_eq(person, "2") { return "едешь" }
                return "едет"
            }
            if str_eq(person, "1") { return "едем" }
            if str_eq(person, "2") { return "едете" }
            return "едут"
        }
        return ""
    }

    // говорить (to speak) — 2nd conjugation
    if str_eq(verb, "говорить") {
        if str_eq(tense, "present") {
            return ru_conjugate_2nd("говор", "present", person, number)
        }
        return ""
    }

    // знать (to know) — 1st conjugation, regular: зна-
    if str_eq(verb, "знать") {
        if str_eq(tense, "present") {
            return ru_conjugate_1st("зна", "present", person, number)
        }
        return ""
    }

    // видеть (to see) — 2nd conjugation, 1sg mutation: вижу
    if str_eq(verb, "видеть") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "вижу" }
                if str_eq(person, "2") { return "видишь" }
                return "видит"
            }
            if str_eq(person, "1") { return "видим" }
            if str_eq(person, "2") { return "видите" }
            return "видят"
        }
        return ""
    }

    // делать (to do) — 1st conjugation: дела-
    if str_eq(verb, "делать") {
        if str_eq(tense, "present") {
            return ru_conjugate_1st("дела", "present", person, number)
        }
        return ""
    }

    // хотеть (to want) — mixed conjugation (1st pl, 2nd sg/3sg)
    if str_eq(verb, "хотеть") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "хочу" }
                if str_eq(person, "2") { return "хочешь" }
                return "хочет"
            }
            if str_eq(person, "1") { return "хотим" }
            if str_eq(person, "2") { return "хотите" }
            return "хотят"
        }
        return ""
    }

    // мочь (can/be able) — present: могу, можешь, может, можем, можете, могут
    if str_eq(verb, "мочь") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "могу" }
                if str_eq(person, "2") { return "можешь" }
                return "может"
            }
            if str_eq(person, "1") { return "можем" }
            if str_eq(person, "2") { return "можете" }
            return "могут"
        }
        return ""
    }

    // сказать (to say, pfv) — present (= future perfective):
    //   скажу, скажешь, скажет, скажем, скажете, скажут
    if str_eq(verb, "сказать") {
        if str_eq(tense, "present") {
            if str_eq(number, "sg") {
                if str_eq(person, "1") { return "скажу" }
                if str_eq(person, "2") { return "скажешь" }
                return "скажет"
            }
            if str_eq(person, "1") { return "скажем" }
            if str_eq(person, "2") { return "скажете" }
            return "скажут"
        }
        return ""
    }

    return ""
}

// ── Past stem table ───────────────────────────────────────────────────────────
//
// Returns the masculine past stem (without agreement ending) for known verbs.
// The caller must run ru_past_agree(stem, gender, number) to get the full form.

fn ru_past_stem(verb: String) -> String {
    // Regular -ать: drop -ть, add nothing (past stem = infinitive minus -ть)
    if str_eq(verb, "читать")   { return "чита" }
    if str_eq(verb, "знать")    { return "зна" }
    if str_eq(verb, "делать")   { return "дела" }
    if str_eq(verb, "сказать")  { return "сказа" }
    if str_eq(verb, "думать")   { return "дума" }
    if str_eq(verb, "работать") { return "работа" }
    if str_eq(verb, "писать")   { return "писа" }
    if str_eq(verb, "слушать")  { return "слуша" }
    if str_eq(verb, "отвечать") { return "отвеча" }

    // Regular -ить/-еть
    if str_eq(verb, "говорить") { return "говори" }
    if str_eq(verb, "видеть")   { return "виде" }
    if str_eq(verb, "смотреть") { return "смотре" }
    if str_eq(verb, "иметь")    { return "име" }
    if str_eq(verb, "хотеть")   { return "хоте" }

    // Irregular pasts
    if str_eq(verb, "быть")     { return "бы" }    // был/была/было/были
    if str_eq(verb, "идти")     { return "шё" }    // шёл — caller must handle -л suffix
    if str_eq(verb, "ехать")    { return "еха" }   // ехал
    if str_eq(verb, "мочь")     { return "мо" }    // мог (m: мог, not мол)
    if str_eq(verb, "нести")    { return "нё" }    // нёс
    if str_eq(verb, "вести")    { return "вё" }    // вёл

    // Derive from infinitive: drop -ть
    let n: Int = str_len(verb)
    if n > 2 {
        let last2: String = str_slice(verb, n - 2, n)
        if str_eq(last2, "ть") {
            return str_drop_last(verb, 2)
        }
    }
    return verb
}

// ── Unified Russian verb conjugation ─────────────────────────────────────────
//
// tense:  "present" | "past" | "future"
// person: "1" | "2" | "3"
// number: "sg" | "pl"
// gender: "m" | "f" | "n"  — used for past tense agreement only

fn ru_conjugate(verb: String, tense: String, person: String, number: String, gender: String) -> String {
    // ── Copula "byt" (transliterated быть) ────────────────────────────────
    // Russian present tense has zero copula — "Кошка большая" not "Кошка есть большая".
    // Return empty string so gram_order_constituents skips the verb slot.
    if str_eq(verb, "byt") {
        if str_eq(tense, "present") { return "" }
        // Future: буду/будешь/будет...  Past: был/была/было/были
        // For now return transliterated placeholder; full Unicode tables TBD.
        if str_eq(tense, "future") { return "budet" }
        return "byl"
    }

    // ── Past tense ──────────────────────────────────────────────────────────
    if str_eq(tense, "past") {
        // Special-case verbs with non-standard past forms
        if str_eq(verb, "идти") {
            if str_eq(number, "pl") { return "шли" }
            if str_eq(gender, "f")  { return "шла" }
            if str_eq(gender, "n")  { return "шло" }
            return "шёл"
        }
        if str_eq(verb, "мочь") {
            if str_eq(number, "pl") { return "могли" }
            if str_eq(gender, "f")  { return "могла" }
            if str_eq(gender, "n")  { return "могло" }
            return "мог"
        }
        if str_eq(verb, "нести") {
            if str_eq(number, "pl") { return "несли" }
            if str_eq(gender, "f")  { return "несла" }
            if str_eq(gender, "n")  { return "несло" }
            return "нёс"
        }
        if str_eq(verb, "вести") {
            if str_eq(number, "pl") { return "вели" }
            if str_eq(gender, "f")  { return "вела" }
            if str_eq(gender, "n")  { return "вело" }
            return "вёл"
        }
        let ps: String = ru_past_stem(verb)
        return ru_past_agree(ps, gender, number)
    }

    // ── Future tense (imperfective) ─────────────────────────────────────────
    //
    // Imperfective future = быть (conjugated) + infinitive
    if str_eq(tense, "future") {
        let aux: String = ru_irregular("быть", "future", person, number)
        return aux + " " + verb
    }

    // ── Present tense ───────────────────────────────────────────────────────

    // Try the irregular table first
    let irr: String = ru_irregular(verb, tense, person, number)
    if !str_eq(irr, "") {
        return irr
    }

    // Derive conjugation class from infinitive ending
    let n: Int = str_len(verb)
    if n > 4 {
        let last4: String = str_slice(verb, n - 4, n)
        // -ить verbs → 2nd conjugation (most)
        if str_eq(last4, "ить ") {
            // remove trailing space if any — defensive
        }
    }

    // Detect -ить suffix → 2nd conjugation
    if str_ends_with(verb, "ить") {
        let stem: String = str_drop_last(verb, 3)
        return ru_conjugate_2nd(stem, "present", person, number)
    }

    // Detect -еть suffix → 2nd conjugation
    if str_ends_with(verb, "еть") {
        let stem: String = str_drop_last(verb, 3)
        return ru_conjugate_2nd(stem, "present", person, number)
    }

    // Detect -ать suffix → 1st conjugation
    if str_ends_with(verb, "ать") {
        let stem: String = str_drop_last(verb, 2)
        return ru_conjugate_1st(stem, "present", person, number)
    }

    // Detect -ять suffix → 1st conjugation
    if str_ends_with(verb, "ять") {
        let stem: String = str_drop_last(verb, 2)
        return ru_conjugate_1st(stem, "present", person, number)
    }

    // Detect -овать suffix → 1st conjugation with -у- stem
    if str_ends_with(verb, "овать") {
        let stem: String = str_drop_last(verb, 5) + "у"
        return ru_conjugate_1st(stem, "present", person, number)
    }

    // Detect -нуть suffix → 1st conjugation
    if str_ends_with(verb, "нуть") {
        let stem: String = str_drop_last(verb, 4) + "н"
        return ru_conjugate_1st(stem, "present", person, number)
    }

    // Fallback: return the infinitive
    return verb
}
