// morphology-fr.el - French morphology for the NLG engine.
//
// Implements fusional French verb conjugation, noun pluralization, gender
// inference, article agreement, and interrogative inversion.  Designed as a
// companion to morphology.el and called by the engine when the language
// profile code is "fr".
//
// Verb tenses covered: present, future, imparfait, passé composé.
// Persons: first/second/third × singular/plural.
// Verb groups: -er (regular), -ir (regular finir-type), -re (regular vendre-type)
//              + a core set of common irregular verbs.
//
// Liaison / elision notes:
//   - le/la → l' before vowel-initial nouns (handled in fr_agree_article)
//   - est-ce que can precede any statement for yes/no questions (fr_question_inversion)
//   - Inversion inserts -t- between vowel-final verb form and 3s pronoun: parle-t-il
//
// Depends on: morphology.el (str_ends_with, str_slice, str_len helpers)

// ── String helpers (local, matching morphology.el conventions) ────────────────

fn fr_str_ends(s: String, suf: String) -> Bool {
    return str_ends_with(s, suf)
}

fn fr_str_drop_last(s: String, n: Int) -> String {
    let len: Int = str_len(s)
    if n >= len {
        return ""
    }
    return str_slice(s, 0, len - n)
}

fn fr_str_last_char(s: String) -> String {
    let n: Int = str_len(s)
    if n == 0 {
        return ""
    }
    return str_slice(s, n - 1, n)
}

fn fr_str_last2(s: String) -> String {
    let n: Int = str_len(s)
    if n < 2 {
        return s
    }
    return str_slice(s, n - 2, n)
}

fn fr_is_vowel_start(s: String) -> Bool {
    let n: Int = str_len(s)
    if n == 0 {
        return false
    }
    let c: String = str_slice(s, 0, 1)
    if str_eq(c, "a") { return true }
    if str_eq(c, "e") { return true }
    if str_eq(c, "é") { return true }
    if str_eq(c, "è") { return true }
    if str_eq(c, "ê") { return true }
    if str_eq(c, "i") { return true }
    if str_eq(c, "î") { return true }
    if str_eq(c, "o") { return true }
    if str_eq(c, "ô") { return true }
    if str_eq(c, "u") { return true }
    if str_eq(c, "û") { return true }
    if str_eq(c, "h") { return true }
    return false
}

// ── Verb group detection ──────────────────────────────────────────────────────
//
// Returns "er" | "ir" | "re" | "irregular".
// Irregular detection is done by checking against a known list first; all other
// verbs are classified by ending.

fn fr_is_known_irregular(verb: String) -> Bool {
    if str_eq(verb, "être")    { return true }
    if str_eq(verb, "avoir")   { return true }
    if str_eq(verb, "aller")   { return true }
    if str_eq(verb, "faire")   { return true }
    if str_eq(verb, "pouvoir") { return true }
    if str_eq(verb, "vouloir") { return true }
    if str_eq(verb, "venir")   { return true }
    if str_eq(verb, "dire")    { return true }
    if str_eq(verb, "voir")    { return true }
    if str_eq(verb, "prendre") { return true }
    if str_eq(verb, "mettre")  { return true }
    if str_eq(verb, "savoir")  { return true }
    return false
}

fn fr_verb_group(base: String) -> String {
    if fr_is_known_irregular(base) { return "irregular" }
    if fr_str_ends(base, "er") { return "er" }
    if fr_str_ends(base, "ir") { return "ir" }
    if fr_str_ends(base, "re") { return "re" }
    return "er"
}

fn fr_stem(base: String) -> String {
    return fr_str_drop_last(base, 2)
}

// ── Person/number slot index ──────────────────────────────────────────────────
//
//   0 = 1s (je), 1 = 2s (tu), 2 = 3s (il/elle), 3 = 1p (nous), 4 = 2p (vous), 5 = 3p (ils/elles)

fn fr_slot(person: String, number: String) -> Int {
    if str_eq(person, "first") {
        if str_eq(number, "singular") { return 0 }
        return 3
    }
    if str_eq(person, "second") {
        if str_eq(number, "singular") { return 1 }
        return 4
    }
    if str_eq(number, "singular") { return 2 }
    return 5
}

// ── Irregular present tense ───────────────────────────────────────────────────
//
// être:    suis, es, est, sommes, êtes, sont
// avoir:   ai, as, a, avons, avez, ont
// aller:   vais, vas, va, allons, allez, vont
// faire:   fais, fais, fait, faisons, faites, font
// pouvoir: peux, peux, peut, pouvons, pouvez, peuvent
// vouloir: veux, veux, veut, voulons, voulez, veulent
// venir:   viens, viens, vient, venons, venez, viennent
// dire:    dis, dis, dit, disons, dites, disent
// voir:    vois, vois, voit, voyons, voyez, voient
// prendre: prends, prends, prend, prenons, prenez, prennent
// mettre:  mets, mets, met, mettons, mettez, mettent
// savoir:  sais, sais, sait, savons, savez, savent

fn fr_irregular_present(verb: String, person: String, number: String) -> String {
    let slot: Int = fr_slot(person, number)

    if str_eq(verb, "être") {
        if slot == 0 { return "suis" }
        if slot == 1 { return "es" }
        if slot == 2 { return "est" }
        if slot == 3 { return "sommes" }
        if slot == 4 { return "etes" }
        return "sont"
    }

    // ASCII alias used by morph_map_canonical — identical conjugation.
    if str_eq(verb, "etre") {
        if slot == 0 { return "suis" }
        if slot == 1 { return "es" }
        if slot == 2 { return "est" }
        if slot == 3 { return "sommes" }
        if slot == 4 { return "etes" }
        return "sont"
    }

    if str_eq(verb, "avoir") {
        if slot == 0 { return "ai" }
        if slot == 1 { return "as" }
        if slot == 2 { return "a" }
        if slot == 3 { return "avons" }
        if slot == 4 { return "avez" }
        return "ont"
    }

    if str_eq(verb, "aller") {
        if slot == 0 { return "vais" }
        if slot == 1 { return "vas" }
        if slot == 2 { return "va" }
        if slot == 3 { return "allons" }
        if slot == 4 { return "allez" }
        return "vont"
    }

    if str_eq(verb, "faire") {
        if slot == 0 { return "fais" }
        if slot == 1 { return "fais" }
        if slot == 2 { return "fait" }
        if slot == 3 { return "faisons" }
        if slot == 4 { return "faites" }
        return "font"
    }

    if str_eq(verb, "pouvoir") {
        if slot == 0 { return "peux" }
        if slot == 1 { return "peux" }
        if slot == 2 { return "peut" }
        if slot == 3 { return "pouvons" }
        if slot == 4 { return "pouvez" }
        return "peuvent"
    }

    if str_eq(verb, "vouloir") {
        if slot == 0 { return "veux" }
        if slot == 1 { return "veux" }
        if slot == 2 { return "veut" }
        if slot == 3 { return "voulons" }
        if slot == 4 { return "voulez" }
        return "veulent"
    }

    if str_eq(verb, "venir") {
        if slot == 0 { return "viens" }
        if slot == 1 { return "viens" }
        if slot == 2 { return "vient" }
        if slot == 3 { return "venons" }
        if slot == 4 { return "venez" }
        return "viennent"
    }

    if str_eq(verb, "dire") {
        if slot == 0 { return "dis" }
        if slot == 1 { return "dis" }
        if slot == 2 { return "dit" }
        if slot == 3 { return "disons" }
        if slot == 4 { return "dites" }
        return "disent"
    }

    if str_eq(verb, "voir") {
        if slot == 0 { return "vois" }
        if slot == 1 { return "vois" }
        if slot == 2 { return "voit" }
        if slot == 3 { return "voyons" }
        if slot == 4 { return "voyez" }
        return "voient"
    }

    if str_eq(verb, "prendre") {
        if slot == 0 { return "prends" }
        if slot == 1 { return "prends" }
        if slot == 2 { return "prend" }
        if slot == 3 { return "prenons" }
        if slot == 4 { return "prenez" }
        return "prennent"
    }

    if str_eq(verb, "mettre") {
        if slot == 0 { return "mets" }
        if slot == 1 { return "mets" }
        if slot == 2 { return "met" }
        if slot == 3 { return "mettons" }
        if slot == 4 { return "mettez" }
        return "mettent"
    }

    if str_eq(verb, "savoir") {
        if slot == 0 { return "sais" }
        if slot == 1 { return "sais" }
        if slot == 2 { return "sait" }
        if slot == 3 { return "savons" }
        if slot == 4 { return "savez" }
        return "savent"
    }

    return ""
}

// ── Regular present tense ─────────────────────────────────────────────────────
//
// -er: -e, -es, -e, -ons, -ez, -ent
// -ir: -is, -is, -it, -issons, -issez, -issent   (finir-type; stem gets -iss- in plural)
// -re: -s, -s, -(nothing), -ons, -ez, -ent

fn fr_regular_present(stem: String, vgroup: String, slot: Int) -> String {
    if str_eq(vgroup, "er") {
        if slot == 0 { return stem + "e" }
        if slot == 1 { return stem + "es" }
        if slot == 2 { return stem + "e" }
        if slot == 3 { return stem + "ons" }
        if slot == 4 { return stem + "ez" }
        return stem + "ent"
    }

    if str_eq(vgroup, "ir") {
        // finir-type: singular uses bare stem, plural uses stem + -iss-
        if slot == 0 { return stem + "is" }
        if slot == 1 { return stem + "is" }
        if slot == 2 { return stem + "it" }
        if slot == 3 { return stem + "issons" }
        if slot == 4 { return stem + "issez" }
        return stem + "issent"
    }

    // -re (vendre-type)
    if slot == 0 { return stem + "s" }
    if slot == 1 { return stem + "s" }
    if slot == 2 { return stem }
    if slot == 3 { return stem + "ons" }
    if slot == 4 { return stem + "ez" }
    return stem + "ent"
}

// ── Regular future tense ──────────────────────────────────────────────────────
//
// Future is formed from the infinitive (minus silent -e for -re verbs) + endings:
// -ai, -as, -a, -ons, -ez, -ont

fn fr_future_stem(base: String, vgroup: String) -> String {
    // -re verbs drop the final -e before adding future endings
    if str_eq(vgroup, "re") {
        return fr_str_drop_last(base, 1)
    }
    return base
}

fn fr_regular_future(fstem: String, slot: Int) -> String {
    if slot == 0 { return fstem + "ai" }
    if slot == 1 { return fstem + "as" }
    if slot == 2 { return fstem + "a" }
    if slot == 3 { return fstem + "ons" }
    if slot == 4 { return fstem + "ez" }
    return fstem + "ont"
}

// ── Irregular future stems ────────────────────────────────────────────────────
//
// Returns the irregular future stem, or "" if regular.

fn fr_irregular_future_stem(verb: String) -> String {
    if str_eq(verb, "être")    { return "ser" }
    if str_eq(verb, "avoir")   { return "aur" }
    if str_eq(verb, "aller")   { return "ir" }
    if str_eq(verb, "faire")   { return "fer" }
    if str_eq(verb, "pouvoir") { return "pourr" }
    if str_eq(verb, "vouloir") { return "voudr" }
    if str_eq(verb, "venir")   { return "viendr" }
    if str_eq(verb, "voir")    { return "verr" }
    if str_eq(verb, "savoir")  { return "saur" }
    return ""
}

// ── Regular imparfait ─────────────────────────────────────────────────────────
//
// Imparfait is formed from the nous-present stem (infinitive minus -er/-ir/-re,
// then add -iss for -ir verbs in nous-form) + endings:
// -ais, -ais, -ait, -ions, -iez, -aient
//
// For -er verbs: stem = infinitive minus -er
// For -ir verbs: stem = infinitive minus -ir (bare stem, not -iss- — imparfait
//   uses the basic stem, unlike present plural which uses -iss-)
// For -re verbs: stem = infinitive minus -re
// Exception: être uses ét- as the imparfait stem.

fn fr_imperfect_stem(base: String, vgroup: String) -> String {
    if str_eq(base, "être") { return "ét" }
    return fr_stem(base)
}

fn fr_regular_imperfect(istem: String, slot: Int) -> String {
    if slot == 0 { return istem + "ais" }
    if slot == 1 { return istem + "ais" }
    if slot == 2 { return istem + "ait" }
    if slot == 3 { return istem + "ions" }
    if slot == 4 { return istem + "iez" }
    return istem + "aient"
}

// ── Passé composé (past compound) ────────────────────────────────────────────
//
// Passé composé = auxiliary (avoir or être) + past participle.
// Most verbs use avoir; a core set of motion/state verbs use être.
//
// This function returns a two-word string "auxiliary participle".
// The caller is responsible for agreement of the past participle when être is
// used (feminine adds -e, plural adds -s); this function returns the masculine
// singular participle unconditionally.

fn fr_uses_etre(verb: String) -> Bool {
    if str_eq(verb, "aller")    { return true }
    if str_eq(verb, "venir")    { return true }
    if str_eq(verb, "partir")   { return true }
    if str_eq(verb, "arriver")  { return true }
    if str_eq(verb, "entrer")   { return true }
    if str_eq(verb, "sortir")   { return true }
    if str_eq(verb, "naître")   { return true }
    if str_eq(verb, "mourir")   { return true }
    if str_eq(verb, "rester")   { return true }
    if str_eq(verb, "tomber")   { return true }
    if str_eq(verb, "monter")   { return true }
    if str_eq(verb, "descendre") { return true }
    if str_eq(verb, "rentrer")  { return true }
    if str_eq(verb, "retourner") { return true }
    if str_eq(verb, "passer")   { return true }
    return false
}

// Returns the past participle (masculine singular form).
fn fr_past_participle(verb: String) -> String {
    // Irregular participles
    if str_eq(verb, "être")    { return "été" }
    if str_eq(verb, "avoir")   { return "eu" }
    if str_eq(verb, "aller")   { return "allé" }
    if str_eq(verb, "faire")   { return "fait" }
    if str_eq(verb, "pouvoir") { return "pu" }
    if str_eq(verb, "vouloir") { return "voulu" }
    if str_eq(verb, "venir")   { return "venu" }
    if str_eq(verb, "dire")    { return "dit" }
    if str_eq(verb, "voir")    { return "vu" }
    if str_eq(verb, "prendre") { return "pris" }
    if str_eq(verb, "mettre")  { return "mis" }
    if str_eq(verb, "savoir")  { return "su" }
    if str_eq(verb, "naître")  { return "né" }
    if str_eq(verb, "mourir")  { return "mort" }
    // Regular participles by group
    let vgroup: String = fr_verb_group(verb)
    if str_eq(vgroup, "er") {
        return fr_str_drop_last(verb, 2) + "é"
    }
    if str_eq(vgroup, "ir") {
        return fr_str_drop_last(verb, 2) + "i"
    }
    // -re verbs: drop -re, add -u
    return fr_str_drop_last(verb, 2) + "u"
}

// Conjugates the avoir auxiliary in the present (for passé composé).
fn fr_avoir_present(slot: Int) -> String {
    if slot == 0 { return "ai" }
    if slot == 1 { return "as" }
    if slot == 2 { return "a" }
    if slot == 3 { return "avons" }
    if slot == 4 { return "avez" }
    return "ont"
}

// Conjugates the être auxiliary in the present (for passé composé).
fn fr_etre_present(slot: Int) -> String {
    if slot == 0 { return "suis" }
    if slot == 1 { return "es" }
    if slot == 2 { return "est" }
    if slot == 3 { return "sommes" }
    if slot == 4 { return "êtes" }
    return "sont"
}

// ── Full conjugation entry point ──────────────────────────────────────────────
//
// fr_conjugate: conjugate a French verb.
//
// verb:   French infinitive (e.g. "parler", "être", "venir")
// tense:  "present" | "future" | "imperfect" | "past"
//         (note: "past" returns the passé composé as "aux participle")
// person: "first" | "second" | "third"
// number: "singular" | "plural"

fn fr_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let slot: Int = fr_slot(person, number)

    if str_eq(tense, "present") {
        let irreg: String = fr_irregular_present(verb, person, number)
        if !str_eq(irreg, "") {
            return irreg
        }
        let vgroup: String = fr_verb_group(verb)
        let stem: String = fr_stem(verb)
        return fr_regular_present(stem, vgroup, slot)
    }

    if str_eq(tense, "future") {
        let irreg_stem: String = fr_irregular_future_stem(verb)
        if !str_eq(irreg_stem, "") {
            return fr_regular_future(irreg_stem, slot)
        }
        let vgroup: String = fr_verb_group(verb)
        let fstem: String = fr_future_stem(verb, vgroup)
        return fr_regular_future(fstem, slot)
    }

    if str_eq(tense, "imperfect") {
        let vgroup: String = fr_verb_group(verb)
        let istem: String = fr_imperfect_stem(verb, vgroup)
        return fr_regular_imperfect(istem, slot)
    }

    if str_eq(tense, "past") {
        // Passé composé: auxiliary + past participle
        let pp: String = fr_past_participle(verb)
        if fr_uses_etre(verb) {
            let aux: String = fr_etre_present(slot)
            return aux + " " + pp
        }
        let aux: String = fr_avoir_present(slot)
        return aux + " " + pp
    }

    // Unknown tense: return infinitive unchanged
    return verb
}

// ── Noun gender inference ─────────────────────────────────────────────────────
//
// Returns "m" (masculine), "f" (feminine), or "unknown".
//
// Heuristics (common French patterns):
//   ends in -tion/-sion/-xion  -> feminine  (nation, passion, connexion)
//   ends in -ure               -> feminine  (voiture, culture)
//   ends in -ette              -> feminine  (omelette, cigarette)
//   ends in -eur (abstract)    -> feminine  (couleur, peur, chaleur)
//   ends in -eur (agent)       -> masculine (acteur, serveur) — can't always distinguish
//   ends in -eur (no rule)     -> try -teur → masculine (docteur, auteur)
//   ends in -ment              -> masculine (sentiment, gouvernement)
//   ends in -age               -> masculine (voyage, fromage)
//   ends in -isme              -> masculine (socialisme)
//   ends in -eau               -> masculine (tableau, gâteau)
//   ends in -er/-é             -> masculine (boucher, café)
//   ends in -ée                -> feminine  (journée, idée)
//   ends in -ie                -> feminine  (philosophie, géographie)
//   ends in -ance/-ence        -> feminine  (chance, science)
//   ends in -té/-tié           -> feminine  (beauté, amitié)
//   ends in -ude               -> feminine  (attitude, solitude)
//   ends in -ade               -> feminine  (salade, promenade)
//   ends in -ette              -> feminine  (already covered)
//   ends in -e (generic)       -> often feminine but not reliable; return "unknown"

fn fr_gender(noun: String) -> String {
    // Feminine patterns (check more specific before general)
    if fr_str_ends(noun, "tion")  { return "f" }
    if fr_str_ends(noun, "sion")  { return "f" }
    if fr_str_ends(noun, "xion")  { return "f" }
    if fr_str_ends(noun, "ure")   { return "f" }
    if fr_str_ends(noun, "ette")  { return "f" }
    if fr_str_ends(noun, "ance")  { return "f" }
    if fr_str_ends(noun, "ence")  { return "f" }
    if fr_str_ends(noun, "ité")   { return "f" }
    if fr_str_ends(noun, "té")    { return "f" }
    if fr_str_ends(noun, "tié")   { return "f" }
    if fr_str_ends(noun, "ude")   { return "f" }
    if fr_str_ends(noun, "ade")   { return "f" }
    if fr_str_ends(noun, "ée")    { return "f" }
    if fr_str_ends(noun, "ie")    { return "f" }
    // Masculine patterns
    if fr_str_ends(noun, "ment")  { return "m" }
    if fr_str_ends(noun, "age")   { return "m" }
    if fr_str_ends(noun, "isme")  { return "m" }
    if fr_str_ends(noun, "eau")   { return "m" }
    if fr_str_ends(noun, "eur")   { return "m" }
    if fr_str_ends(noun, "er")    { return "m" }
    if fr_str_ends(noun, "é")     { return "m" }
    return "unknown"
}

// ── Noun pluralization ────────────────────────────────────────────────────────
//
// French plural rules:
//   already ends in -s, -x, or -z   -> unchanged
//   ends in -eau                     -> add -x  (bateau → bateaux)
//   ends in -eu                      -> add -x  (jeu → jeux; bleu → bleus is exception)
//   ends in -al                      -> replace -al with -aux  (animal → animaux)
//   ends in -ail (most)              -> replace -ail with -aux (travail → travaux; bail)
//   otherwise                        -> add -s

fn fr_invariant_plural(noun: String) -> String {
    // Words already ending in -s, -x, -z are unchanged in plural
    let last: String = fr_str_last_char(noun)
    if str_eq(last, "s") { return noun }
    if str_eq(last, "x") { return noun }
    if str_eq(last, "z") { return noun }
    return ""
}

fn fr_pluralize(noun: String) -> String {
    let inv: String = fr_invariant_plural(noun)
    if !str_eq(inv, "") {
        return inv
    }
    if fr_str_ends(noun, "eau") {
        return noun + "x"
    }
    if fr_str_ends(noun, "eu") {
        return noun + "x"
    }
    if fr_str_ends(noun, "al") {
        return fr_str_drop_last(noun, 2) + "aux"
    }
    if fr_str_ends(noun, "ail") {
        return fr_str_drop_last(noun, 3) + "aux"
    }
    return noun + "s"
}

// ── Article agreement ─────────────────────────────────────────────────────────
//
// fr_agree_article: return the correct French article for a noun.
//
// noun:     the noun (used for gender inference and elision check)
// definite: "true" for definite (le/la/l'/les), "false" for indefinite (un/une/des)
// number:   "singular" | "plural"
//
// Elision: le/la → l' before a vowel- or h-initial noun (handled here).

fn fr_agree_article(noun: String, definite: String, number: String) -> String {
    let gender: String = fr_gender(noun)
    let is_plural: Bool = str_eq(number, "plural")
    let is_def: Bool = str_eq(definite, "true")
    let vowel_start: Bool = fr_is_vowel_start(noun)

    if is_def {
        if is_plural {
            return "les"
        }
        // singular
        if vowel_start {
            return "l'"
        }
        if str_eq(gender, "f") {
            return "la"
        }
        return "le"
    }

    // indefinite
    if is_plural {
        return "des"
    }
    if str_eq(gender, "f") {
        return "une"
    }
    return "un"
}

// ── Question inversion ────────────────────────────────────────────────────────
//
// fr_question_inversion: form a yes/no question using subject-verb inversion.
//
// subject:   pronoun string: "je" | "tu" | "il" | "elle" | "nous" | "vous" | "ils" | "elles"
// verb_form: the conjugated verb form (e.g. "parle", "mange", "est")
//
// Rules:
//   - Verb and subject are joined with a hyphen: "parle-t-il ?"
//   - When the verb form ends in a vowel and the subject starts with a vowel
//     (il, elle, ils, elles), insert euphonic -t-: "parle-t-il ?"
//   - Je inversion is archaic; "est-ce que je ...?" is preferred — this function
//     generates "est-ce que je <verb_form> ?" for first-person singular.
//   - The result ends with " ?"

fn fr_subject_starts_vowel(subject: String) -> Bool {
    if str_eq(subject, "il")    { return true }
    if str_eq(subject, "elle")  { return true }
    if str_eq(subject, "ils")   { return true }
    if str_eq(subject, "elles") { return true }
    return false
}

fn fr_verb_ends_vowel(verb_form: String) -> Bool {
    let last: String = fr_str_last_char(verb_form)
    if str_eq(last, "a") { return true }
    if str_eq(last, "e") { return true }
    if str_eq(last, "é") { return true }
    if str_eq(last, "i") { return true }
    if str_eq(last, "o") { return true }
    if str_eq(last, "u") { return true }
    return false
}

fn fr_question_inversion(subject: String, verb_form: String) -> String {
    // First-person singular: use est-ce que construction
    if str_eq(subject, "je") {
        return "est-ce que je " + verb_form + " ?"
    }

    // Determine whether to insert -t-
    let need_t: Bool = false
    if fr_verb_ends_vowel(verb_form) {
        if fr_subject_starts_vowel(subject) {
            let need_t = true
        }
    }

    if need_t {
        return verb_form + "-t-" + subject + " ?"
    }
    return verb_form + "-" + subject + " ?"
}
