/* SPDX-License-Identifier: AGPL-3.0-only
   SPDX-FileCopyrightText: 2026 Com'Together
   Com'Together — Ma communication. Copyright (C) 2026 Com'Together.
   Programme libre sous AGPL-3.0-only — voir LICENSE. */
/* ============================================================================
 * grammar.js — Moteur de grammaticalisation française
 * ----------------------------------------------------------------------------
 * Un système linguistiquement robuste doit permettre la GRAMMATICALISATION
 * (conjugaison, morphosyntaxe) — critère Suc-Mella / Van Tatenhove.
 *
 * Ici : conjugaison des verbes de base au présent, passé composé et futur
 * proche, en accordant automatiquement avec le pronom sujet présent dans
 * la phrase. Les formes irrégulières sont écrites explicitement (fiables),
 * jamais « devinées ».
 * ==========================================================================*/

// Ordre des personnes : 0=je 1=tu 2=il/elle/on 3=nous 4=vous 5=ils/elles
const SUBJECT_INDEX = {
  je: 0, tu: 1, il: 2, elle: 2, on: 2, iel: 2,   // iel : pronom neutre (3e pers. sing.)
  nous: 3, vous: 4, ils: 5, elles: 5,
};

// Table de conjugaison au PRÉSENT + participe passé + auxiliaire + radical du futur.
// aux: 'avoir' | 'etre'   (pour le passé composé)
// fut: radical du FUTUR simple / CONDITIONNEL (ex. "voudr" -> je voudrai / je voudrais).
//      Pour les verbes en -er réguliers, c'est simplement l'infinitif.
const VERBS = {
  "vouloir":  { p: ["veux","veux","veut","voulons","voulez","veulent"], pp: "voulu", aux: "avoir", fut: "voudr" },
  "pouvoir":  { p: ["peux","peux","peut","pouvons","pouvez","peuvent"], pp: "pu", aux: "avoir", fut: "pourr" },
  "aller":    { p: ["vais","vas","va","allons","allez","vont"], pp: "allé", aux: "etre", fut: "ir" },
  "faire":    { p: ["fais","fais","fait","faisons","faites","font"], pp: "fait", aux: "avoir", fut: "fer" },
  "avoir":    { p: ["ai","as","a","avons","avez","ont"], pp: "eu", aux: "avoir", fut: "aur" },
  "être":     { p: ["suis","es","est","sommes","êtes","sont"], pp: "été", aux: "avoir", fut: "ser" },
  "aimer":    { p: ["aime","aimes","aime","aimons","aimez","aiment"], pp: "aimé", aux: "avoir", fut: "aimer" },
  "manger":   { p: ["mange","manges","mange","mangeons","mangez","mangent"], pp: "mangé", aux: "avoir", fut: "manger" },
  "boire":    { p: ["bois","bois","boit","buvons","buvez","boivent"], pp: "bu", aux: "avoir", fut: "boir" },
  "jouer":    { p: ["joue","joues","joue","jouons","jouez","jouent"], pp: "joué", aux: "avoir", fut: "jouer" },
  "regarder": { p: ["regarde","regardes","regarde","regardons","regardez","regardent"], pp: "regardé", aux: "avoir", fut: "regarder" },
  "écouter":  { p: ["écoute","écoutes","écoute","écoutons","écoutez","écoutent"], pp: "écouté", aux: "avoir", fut: "écouter" },
  "parler":   { p: ["parle","parles","parle","parlons","parlez","parlent"], pp: "parlé", aux: "avoir", fut: "parler" },
  "aider":    { p: ["aide","aides","aide","aidons","aidez","aident"], pp: "aidé", aux: "avoir", fut: "aider" },
  "donner":   { p: ["donne","donnes","donne","donnons","donnez","donnent"], pp: "donné", aux: "avoir", fut: "donner" },
  "prendre":  { p: ["prends","prends","prend","prenons","prenez","prennent"], pp: "pris", aux: "avoir", fut: "prendr" },
  "venir":    { p: ["viens","viens","vient","venons","venez","viennent"], pp: "venu", aux: "etre", fut: "viendr" },
  "arrêter":  { p: ["arrête","arrêtes","arrête","arrêtons","arrêtez","arrêtent"], pp: "arrêté", aux: "avoir", fut: "arrêter" },
  "mettre":   { p: ["mets","mets","met","mettons","mettez","mettent"], pp: "mis", aux: "avoir", fut: "mettr" },
  "dire":     { p: ["dis","dis","dit","disons","dites","disent"], pp: "dit", aux: "avoir", fut: "dir" },
  "savoir":   { p: ["sais","sais","sait","savons","savez","savent"], pp: "su", aux: "avoir", fut: "saur" },
  "penser":   { p: ["pense","penses","pense","pensons","pensez","pensent"], pp: "pensé", aux: "avoir", fut: "penser" },
  "comprendre":{ p: ["comprends","comprends","comprend","comprenons","comprenez","comprennent"], pp: "compris", aux: "avoir", fut: "comprendr" },
  "attendre": { p: ["attends","attends","attend","attendons","attendez","attendent"], pp: "attendu", aux: "avoir", fut: "attendr" },
  "chercher": { p: ["cherche","cherches","cherche","cherchons","cherchez","cherchent"], pp: "cherché", aux: "avoir", fut: "chercher" },
  "décider":  { p: ["décide","décides","décide","décidons","décidez","décident"], pp: "décidé", aux: "avoir", fut: "décider" },
  "préférer": { p: ["préfère","préfères","préfère","préférons","préférez","préfèrent"], pp: "préféré", aux: "avoir", fut: "préférer" },
  "changer":  { p: ["change","changes","change","changeons","changez","changent"], pp: "changé", aux: "avoir", fut: "changer" },
  "continuer":{ p: ["continue","continues","continue","continuons","continuez","continuent"], pp: "continué", aux: "avoir", fut: "continuer" },
  "trouver":  { p: ["trouve","trouves","trouve","trouvons","trouvez","trouvent"], pp: "trouvé", aux: "avoir", fut: "trouver" },
  "voir":     { p: ["vois","vois","voit","voyons","voyez","voient"], pp: "vu", aux: "avoir", fut: "verr" },
  "envoyer":  { p: ["envoie","envoies","envoie","envoyons","envoyez","envoient"], pp: "envoyé", aux: "avoir", fut: "enverr" }, // futur IRRÉGULIER (enverr-) : donnée, pas règle
  // ---- 3e groupe VRAIMENT irréguliers (Phase 2) : données, car non dérivables par règle ----
  "devoir":   { p: ["dois","dois","doit","devons","devez","doivent"], pp: "dû", aux: "avoir", fut: "devr" },
  "recevoir": { p: ["reçois","reçois","reçoit","recevons","recevez","reçoivent"], pp: "reçu", aux: "avoir", fut: "recevr" },
  "courir":   { p: ["cours","cours","court","courons","courez","courent"], pp: "couru", aux: "avoir", fut: "courr" },
  "mourir":   { p: ["meurs","meurs","meurt","mourons","mourez","meurent"], pp: "mort", aux: "etre", fut: "mourr" },
  "tenir":    { p: ["tiens","tiens","tient","tenons","tenez","tiennent"], pp: "tenu", aux: "avoir", fut: "tiendr" },
  "lire":     { p: ["lis","lis","lit","lisons","lisez","lisent"], pp: "lu", aux: "avoir", fut: "lir" },
  "écrire":   { p: ["écris","écris","écrit","écrivons","écrivez","écrivent"], pp: "écrit", aux: "avoir", fut: "écrir" },
  "croire":   { p: ["crois","crois","croit","croyons","croyez","croient"], pp: "cru", aux: "avoir", fut: "croir" },
  "vivre":    { p: ["vis","vis","vit","vivons","vivez","vivent"], pp: "vécu", aux: "avoir", fut: "vivr" },
  "suivre":   { p: ["suis","suis","suit","suivons","suivez","suivent"], pp: "suivi", aux: "avoir", fut: "suivr" },
  "rire":     { p: ["ris","ris","rit","rions","riez","rient"], pp: "ri", aux: "avoir", fut: "rir" },
  "plaire":   { p: ["plais","plais","plaît","plaisons","plaisez","plaisent"], pp: "plu", aux: "avoir", fut: "plair" },
  "suffire":  { p: ["suffis","suffis","suffit","suffisons","suffisez","suffisent"], pp: "suffi", aux: "avoir", fut: "suffir" },
  "acquérir": { p: ["acquiers","acquiers","acquiert","acquérons","acquérez","acquièrent"], pp: "acquis", aux: "avoir", fut: "acquerr" },
  "conquérir":{ p: ["conquiers","conquiers","conquiert","conquérons","conquérez","conquièrent"], pp: "conquis", aux: "avoir", fut: "conquerr" },
  "fuir":     { p: ["fuis","fuis","fuit","fuyons","fuyez","fuient"], pp: "fui", aux: "avoir", fut: "fuir" },
  "naître":   { p: ["nais","nais","naît","naissons","naissez","naissent"], pp: "né", aux: "etre", fut: "naîtr" },
  "résoudre": { p: ["résous","résous","résout","résolvons","résolvez","résolvent"], pp: "résolu", aux: "avoir", fut: "résoudr" },
  "coudre":   { p: ["couds","couds","coud","cousons","cousez","cousent"], pp: "cousu", aux: "avoir", fut: "coudr" },
  "vaincre":  { p: ["vaincs","vaincs","vainc","vainquons","vainquez","vainquent"], pp: "vaincu", aux: "avoir", fut: "vaincr" },
  "convaincre":{ p: ["convaincs","convaincs","convainc","convainquons","convainquez","convainquent"], pp: "convaincu", aux: "avoir", fut: "convaincr" },
  "battre":   { p: ["bats","bats","bat","battons","battez","battent"], pp: "battu", aux: "avoir", fut: "battr" },
  "rompre":   { p: ["romps","romps","rompt","rompons","rompez","rompent"], pp: "rompu", aux: "avoir", fut: "rompr" },
  "taire":    { p: ["tais","tais","tait","taisons","taisez","taisent"], pp: "tu", aux: "avoir", fut: "tair" },
  "valoir":   { p: ["vaux","vaux","vaut","valons","valez","valent"], pp: "valu", aux: "avoir", fut: "vaudr" },
  "haïr":     { p: ["hais","hais","hait","haïssons","haïssez","haïssent"], pp: "haï", aux: "avoir", fut: "haïr" },
  "sourire":  { p: ["souris","souris","sourit","sourions","souriez","sourient"], pp: "souri", aux: "avoir", fut: "sourir" },
  "conclure": { p: ["conclus","conclus","conclut","concluons","concluez","concluent"], pp: "conclu", aux: "avoir", fut: "conclur" },
  "inclure":  { p: ["inclus","inclus","inclut","incluons","incluez","incluent"], pp: "inclus", aux: "avoir", fut: "inclur" },
  "exclure":  { p: ["exclus","exclus","exclut","excluons","excluez","excluent"], pp: "exclu", aux: "avoir", fut: "exclur" },
  // Résidu du 3e groupe (build 294) — irréguliers/défectifs restants, en données.
  "absoudre": { p: ["absous","absous","absout","absolvons","absolvez","absolvent"], pp: "absous", aux: "avoir", fut: "absoudr" },
  "dissoudre":{ p: ["dissous","dissous","dissout","dissolvons","dissolvez","dissolvent"], pp: "dissous", aux: "avoir", fut: "dissoudr" },
  "moudre":   { p: ["mouds","mouds","moud","moulons","moulez","moulent"], pp: "moulu", aux: "avoir", fut: "moudr" },
  "croître":  { p: ["croîs","croîs","croît","croissons","croissez","croissent"], pp: "crû", aux: "avoir", fut: "croîtr" },
  "accroître":{ p: ["accrois","accrois","accroît","accroissons","accroissez","accroissent"], pp: "accru", aux: "avoir", fut: "accroîtr" },
  "décroître":{ p: ["décrois","décrois","décroît","décroissons","décroissez","décroissent"], pp: "décru", aux: "avoir", fut: "décroîtr" },
  "bouillir": { p: ["bous","bous","bout","bouillons","bouillez","bouillent"], pp: "bouilli", aux: "avoir", fut: "bouillir" },
  "vêtir":    { p: ["vêts","vêts","vêt","vêtons","vêtez","vêtent"], pp: "vêtu", aux: "avoir", fut: "vêtir" },
  "revêtir":  { p: ["revêts","revêts","revêt","revêtons","revêtez","revêtent"], pp: "revêtu", aux: "avoir", fut: "revêtir" },
  "dévêtir":  { p: ["dévêts","dévêts","dévêt","dévêtons","dévêtez","dévêtent"], pp: "dévêtu", aux: "avoir", fut: "dévêtir" },
  "maudire":  { p: ["maudis","maudis","maudit","maudissons","maudissez","maudissent"], pp: "maudit", aux: "avoir", fut: "maudir" },
  "foutre":   { p: ["fous","fous","fout","foutons","foutez","foutent"], pp: "foutu", aux: "avoir", fut: "foutr" },
  "confire":  { p: ["confis","confis","confit","confisons","confisez","confisent"], pp: "confit", aux: "avoir", fut: "confir" },
  "clore":    { p: ["clos","clos","clôt","closons","closez","closent"], pp: "clos", aux: "avoir", fut: "clor" },
  "éclore":   { p: ["éclos","éclos","éclôt","éclosons","éclosez","éclosent"], pp: "éclos", aux: "etre", fut: "éclor" },
  "prévoir":  { p: ["prévois","prévois","prévoit","prévoyons","prévoyez","prévoient"], pp: "prévu", aux: "avoir", fut: "prévoir" },
  "pourvoir": { p: ["pourvois","pourvois","pourvoit","pourvoyons","pourvoyez","pourvoient"], pp: "pourvu", aux: "avoir", fut: "pourvoir" },
  "mouvoir":  { p: ["meus","meus","meut","mouvons","mouvez","meuvent"], pp: "mû", aux: "avoir", fut: "mouvr" },
  "émouvoir": { p: ["émeus","émeus","émeut","émouvons","émouvez","émeuvent"], pp: "ému", aux: "avoir", fut: "émouvr" },
  "promouvoir":{ p: ["promeus","promeus","promeut","promouvons","promouvez","promeuvent"], pp: "promu", aux: "avoir", fut: "promouvr" },
  "asseoir":  { p: ["assieds","assieds","assied","asseyons","asseyez","asseyent"], pp: "assis", aux: "avoir", fut: "assiér" },
  "prévaloir":{ p: ["prévaux","prévaux","prévaut","prévalons","prévalez","prévalent"], pp: "prévalu", aux: "avoir", fut: "prévaudr" },
  "falloir":  { p: ["faut","faut","faut","faut","faut","faut"], pp: "fallu", aux: "avoir", fut: "faudr" },   // impersonnel : « il faut »
  "pleuvoir": { p: ["pleut","pleut","pleut","pleut","pleut","pleuvent"], pp: "plu", aux: "avoir", fut: "pleuvr" }, // impersonnel : « il pleut »
  // conduire/peindre/éteindre/craindre/connaître : générés par les MODÈLES -uire/-indre/-aître (Phase 3), plus en table.
};

// Terminaisons régulières (fiables, jamais devinées).
const IMPARFAIT_END = ["ais", "ais", "ait", "ions", "iez", "aient"];   // + conditionnel (sur radical futur)
const FUTUR_END     = ["ai", "as", "a", "ons", "ez", "ont"];
// Radical de l'imparfait : forme « nous » du présent moins « -ons » (règle du Bescherelle).
// Seule exception : « être » -> « ét- ». Ex. « nous voulons » -> « voul- » -> « je voulais ».
function imparfaitStem(v, infinitive) {
  if (infinitive === "être") return "ét";
  return v.p[3].replace(/ons$/, "");
}

const AVOIR = VERBS["avoir"].p;
const ETRE  = VERBS["être"].p;

// PASSÉ SIMPLE (surtout pour la lecture / littératie). Terminaisons -er régulières ;
// formes irrégulières écrites explicitement (fiables, jamais devinées).
const PS_END_ER = ["ai", "as", "a", "âmes", "âtes", "èrent"];
const PASSE_SIMPLE = {
  "être":     ["fus","fus","fut","fûmes","fûtes","furent"],
  "avoir":    ["eus","eus","eut","eûmes","eûtes","eurent"],
  "faire":    ["fis","fis","fit","fîmes","fîtes","firent"],
  "vouloir":  ["voulus","voulus","voulut","voulûmes","voulûtes","voulurent"],
  "pouvoir":  ["pus","pus","put","pûmes","pûtes","purent"],
  "boire":    ["bus","bus","but","bûmes","bûtes","burent"],
  "prendre":  ["pris","pris","prit","prîmes","prîtes","prirent"],
  "comprendre":["compris","compris","comprit","comprîmes","comprîtes","comprirent"],
  "venir":    ["vins","vins","vint","vînmes","vîntes","vinrent"],
  "mettre":   ["mis","mis","mit","mîmes","mîtes","mirent"],
  "dire":     ["dis","dis","dit","dîmes","dîtes","dirent"],
  "savoir":   ["sus","sus","sut","sûmes","sûtes","surent"],
  "voir":     ["vis","vis","vit","vîmes","vîtes","virent"],
  "attendre": ["attendis","attendis","attendit","attendîmes","attendîtes","attendirent"],
  // 3e groupe irréguliers en table (Phase 2/3) — passé simple explicite (familles -us / -is / -ins).
  "devoir":   ["dus","dus","dut","dûmes","dûtes","durent"],
  "recevoir": ["reçus","reçus","reçut","reçûmes","reçûtes","reçurent"],
  "courir":   ["courus","courus","courut","courûmes","courûtes","coururent"],
  "mourir":   ["mourus","mourus","mourut","mourûmes","mourûtes","moururent"],
  "tenir":    ["tins","tins","tint","tînmes","tîntes","tinrent"],
  "lire":     ["lus","lus","lut","lûmes","lûtes","lurent"],
  "écrire":   ["écrivis","écrivis","écrivit","écrivîmes","écrivîtes","écrivirent"],
  "croire":   ["crus","crus","crut","crûmes","crûtes","crurent"],
  "vivre":    ["vécus","vécus","vécut","vécûmes","vécûtes","vécurent"],
  "suivre":   ["suivis","suivis","suivit","suivîmes","suivîtes","suivirent"],
  "rire":     ["ris","ris","rit","rîmes","rîtes","rirent"],
  "plaire":   ["plus","plus","plut","plûmes","plûtes","plurent"],
  "suffire":  ["suffis","suffis","suffit","suffîmes","suffîtes","suffirent"],
  "courir":   ["courus","courus","courut","courûmes","courûtes","coururent"],
  "mourir":   ["mourus","mourus","mourut","mourûmes","mourûtes","moururent"],
  "naître":   ["naquis","naquis","naquit","naquîmes","naquîtes","naquirent"],
};
// IMPÉRATIF (tu / nous / vous). Irréguliers écrits explicitement.
const IMPER = {
  "être":   ["sois", "soyons", "soyez"],
  "avoir":  ["aie", "ayons", "ayez"],
  "savoir": ["sache", "sachons", "sachez"],
  "vouloir":["veuille", "voulons", "veuillez"],
};
function imperatif(infinitive, i) {
  const v = VERBS[infinitive] || regularVerb(infinitive); if (!v) return infinitive;   // régulier -er/-ir généré ; 3e groupe inconnu : forme brute
  const map = { 1: 0, 3: 1, 4: 2 };            // tu, nous, vous ; défaut = tu
  const k = map[i] !== undefined ? map[i] : 0;
  if (IMPER[infinitive]) return IMPER[infinitive][k];
  if (k === 1) return v.p[3];                  // nous : forme du présent
  if (k === 2) return v.p[4];                  // vous : forme du présent
  let tu = v.p[1];                             // tu : présent, sans « s » final pour -er / aller
  if ((infinitive.endsWith("er") || infinitive === "aller" || /(vrir|frir|ueillir|aillir)$/.test(infinitive)) && tu.endsWith("s")) tu = tu.slice(0, -1);
  return tu;
}
function passeSimple(infinitive, i) {
  if (PASSE_SIMPLE[infinitive]) return PASSE_SIMPLE[infinitive][i];
  if (infinitive.endsWith("er")) {              // -er réguliers (parler -> parlai ; manger -> mangeai)
    let st = infinitive.slice(0, -2), end = PS_END_ER[i];
    if (st.endsWith("g") && /^[aâ]/.test(end)) st += "e";
    if (st.endsWith("c") && /^[aâ]/.test(end)) st = st.slice(0, -1) + "ç";
    return st + end;
  }
  // 2e groupe + -ir réguliers (hors classe fermée, hors -oir) : finis/finit/finirent (famille -is).
  if (/ir$/.test(infinitive) && !/oir$/.test(infinitive) && !IR_GROUP3.has(infinitive)) return infinitive.slice(0, -2) + PS_END_IR[i];
  // Familles à modèle du 3e groupe : passé simple généré par règle.
  if (infinitive in G3_PARTIR) return infinitive.slice(0, -2) + PS_END_IR[i];   // partir → partis
  if (G3_OUVRIR.has(infinitive)) return infinitive.slice(0, -2) + PS_END_IR[i]; // ouvrir → ouvris
  if (G3_CUEILLIR.has(infinitive)) return infinitive.slice(0, -2) + PS_END_IR[i]; // cueillir → cueillis
  if (G3_COURIR.has(infinitive))  return infinitive.slice(0, -2) + PS_END_US[i]; // accourir → accourus
  if (infinitive in G3_VENIR)     return infinitive.slice(0, -4) + PS_END_INS[i]; // devenir → devins
  if (G3_PRENDRE.has(infinitive)) return infinitive.slice(0, -7) + "pr" + PS_END_IR[i]; // apprendre → appris
  if (G3_METTRE.has(infinitive))  return infinitive.slice(0, -6) + "m" + PS_END_IR[i];  // permettre → permis
  if (G3_BATTRE.has(infinitive))  return infinitive.slice(0, -3) + "t" + PS_END_IR[i];  // abattre → abattis
  if (G3_ENDRE.has(infinitive))  return infinitive.slice(0, -2) + PS_END_IR[i]; // rendre → rendis
  if (G3_INDRE.has(infinitive))  return infinitive.slice(0, -5) + "ign" + PS_END_IR[i]; // peindre → peignis
  if (G3_UIRE.has(infinitive))   return infinitive.slice(0, -3) + "is" + PS_END_IR[i];  // conduire → conduisis
  if (G3_AITRE.has(infinitive))  return infinitive.slice(0, -5) + PS_END_US[i]; // connaître → connus
  const v = VERBS[infinitive] || regularVerb(infinitive);
  return v ? v.p[i] : infinitive;               // secours : forme présent (jamais deviné à tort)
}

// Accord du participe passé (auxiliaire être) — simplifié : genre/nombre du sujet.
function ppAgreement(pp, subject) {
  if (SUBJECT_INDEX[subject] === undefined) return pp;
  let s = pp;
  const fem = subject === "elle" || subject === "elles";
  const plur = ["nous", "vous", "ils", "elles"].includes(subject);
  if (fem) s += "e";
  if (plur) s += "s";
  return s;
}

/**
 * Conjugue un verbe.
 * @param {string} infinitive  clé dans VERBS
 * @param {string} subject     'je','tu','il',... (défaut 'je')
 * @param {string} tense       'present' | 'passe' | 'futur'
 * @returns {string} la forme verbale (ex. "je vais manger" -> ici "mange"/"ai mangé"/"vais manger")
 */
// ============================================================================
// MOTEUR MORPHOLOGIQUE (Phase 1) — génère la conjugaison des verbes RÉGULIERS par RÈGLE
// (pas une liste de formes), d'après Le Bon Usage / le modèle Bescherelle. Couvre :
//   • 1er groupe -er + TOUTES les variantes orthographiques (-cer, -ger, -yer, -eler/-eter,
//     é→è, e→è) — dérivable sans données ;
//   • 2e groupe -ir/-issons (finir…) — via une liste de rattachement (on ne DEVINE jamais qu'un
//     -ir est du 2e groupe : partir/dormir sont du 3e → laissés à la table / au repli brut).
// Les irréguliers (être, aller, faire…) restent dans VERBS (données, priorité). Tous les autres
// temps (imparfait, futur, participes, impératif, passé simple) se dérivent ensuite de l'objet
// {p, pp, aux, fut} produit ici, via le code existant.
const V = "aeiouyàâäéèêëîïôöûüÿ";                                   // voyelles (accents inclus)
const RE_E_ACUTE = new RegExp("é[^" + V + "]+$");                   // é + consonne(s) finales : céder, préférer
const RE_E_MUTE  = new RegExp("e[^" + V + "]$");                    // e muet + UNE seule consonne finale : lever→lève, mener→mène (PAS commencer : « nc » = groupe → commence)
// -eler/-eter qui prennent è (et NON le doublement) — liste Bescherelle (acheter/geler-types).
const ELER_ETER_GRAVE = new Set(["acheter","racheter","crocheter","fureter","haleter","geler","dégeler",
  "congeler","surgeler","celer","déceler","receler","ciseler","démanteler","écarteler","marteler","modeler","peler"]);
// 3e groupe en -ir = CLASSE FERMÉE. Passage à l'échelle : tout verbe en -ir HORS de cet ensemble est
// traité comme 2e groupe (règle sûre — le 2e groupe est la classe productive ; le 3e groupe en -ir est
// fini et connu). Ceux d'ici sont routés vers un modèle (partir/ouvrir/venir/courir/cueillir) ou la
// table VERBS ; le reliquat rare sans modèle (bouillir/vêtir/gésir/ouïr/saillir…) retombe à l'infinitif
// brut — jamais mal conjugué. [[references-linguistiques-caa]]
const IR_GROUP3 = new Set([
  "partir","repartir","sortir","ressortir","dormir","endormir","rendormir","sentir","ressentir","consentir","pressentir",
  "servir","desservir","resservir","mentir","démentir","repentir",
  "courir","accourir","concourir","discourir","parcourir","recourir","secourir","mourir",
  "ouvrir","rouvrir","entrouvrir","couvrir","découvrir","recouvrir","offrir","souffrir",
  "cueillir","accueillir","recueillir","assaillir","tressaillir","défaillir","saillir","faillir",
  "tenir","obtenir","retenir","contenir","maintenir","soutenir","détenir","entretenir","appartenir","abstenir",
  "venir","devenir","revenir","parvenir","survenir","advenir","intervenir","provenir","redevenir","convenir",
  "prévenir","subvenir","contrevenir","disconvenir","circonvenir","souvenir",
  "acquérir","conquérir","requérir","enquérir","reconquérir",
  "fuir","enfuir","bouillir","vêtir","revêtir","dévêtir","gésir","ouïr","quérir","férir"]);
const PS_END_IR  = ["is","is","it","îmes","îtes","irent"];         // passé simple famille -is (2e groupe + partir/rendre/-indre/-uire…)
const PS_END_US  = ["us","us","ut","ûmes","ûtes","urent"];         // passé simple famille -us (devoir/connaître/vivre…)
const PS_END_INS = ["ins","ins","int","înmes","întes","inrent"];   // passé simple famille -ins (venir/tenir)

// 1er groupe -er (+ variantes orthographiques). Retourne {p, pp, aux, fut} ou null.
function regular1(inf) {
  if (!/er$/.test(inf) || inf === "aller" || inf.length < 3 || VERBS[inf]) return null;
  const r = inf.slice(0, -2);                        // radical de base (« dessin », « appel », « préfér »)
  let hard = r, fut = inf;                            // hard = radical devant e muet (je/tu/il/ils) ; fut = radical du futur
  if (/(el|et)$/.test(r)) {                           // -eler / -eter : doublement OU è (liste)
    if (ELER_ETER_GRAVE.has(inf)) hard = r.slice(0, -2) + "è" + r.slice(-1);   // ach + è + t → achèt
    else hard = r + r.slice(-1);                                               // appel + l → appell ; jet + t → jett
    fut = hard + "er";
  } else if (RE_E_ACUTE.test(r)) {                    // é_er : céder/préférer/protéger → é→è (présent ; futur garde é)
    hard = r.replace(new RegExp("é([^" + V + "]+)$"), "è$1");
  } else if (RE_E_MUTE.test(r)) {                     // e_er muet : lever/mener/peser → e→è (présent ET futur)
    hard = r.replace(new RegExp("e([^" + V + "])$"), "è$1");
    fut = hard + "er";
  } else if (/y$/.test(r)) {                          // -yer : y→i devant e muet (présent ET futur)
    hard = r.slice(0, -1) + "i";
    fut = hard + "er";
  }
  let nous;                                           // « nous » : -cer → ç, -ger → ge, sinon +ons (sur le radical de base)
  if (/c$/.test(r)) nous = r.slice(0, -1) + "çons";
  else if (/g$/.test(r)) nous = r + "eons";
  else nous = r + "ons";
  return { p: [hard + "e", hard + "es", hard + "e", nous, r + "ez", hard + "ent"], pp: r + "é", aux: "avoir", fut };
}
// 2e groupe -ir/-issons : PAR DÉFAUT pour tout -ir hors classe fermée du 3e groupe (finir, choisir,
// grandir, bondir, vrombir… et tous les néologismes en -ir). Sûr car le 3e groupe -ir est fini (IR_GROUP3).
function regular2(inf) {
  // -oir se termine aussi par « ir » mais n'est JAMAIS du 2e groupe (avoir/savoir/valoir…) → exclu.
  if (!/ir$/.test(inf) || /oir$/.test(inf) || inf.length < 4 || IR_GROUP3.has(inf)) return null;
  const r = inf.slice(0, -2);                         // « fin » pour « finir »
  return { p: [r + "is", r + "is", r + "it", r + "issons", r + "issez", r + "issent"], pp: r + "i", aux: "avoir", fut: inf };
}

// ---- 3e groupe : MODÈLES réguliers dans leur famille (Phase 2). Chaque famille = une règle + une
// liste de rattachement (les VRAIS irréguliers, eux, sont en données dans VERBS). ----
// Modèle « partir » : la consonne finale du radical tombe au singulier (je pars / nous partons).
// valeur = auxiliaire (partir/sortir = être ; dormir/sentir/servir/mentir = avoir).
const G3_PARTIR = { "partir": "etre", "repartir": "etre", "sortir": "etre", "ressortir": "avoir",
  "dormir": "avoir", "endormir": "avoir", "rendormir": "avoir", "sentir": "avoir", "ressentir": "avoir",
  "consentir": "avoir", "pressentir": "avoir", "servir": "avoir", "desservir": "avoir", "resservir": "avoir",
  "mentir": "avoir", "démentir": "avoir" };
// Modèle « ouvrir » : présent en -e comme le 1er groupe ; participe passé en -ert.
const G3_OUVRIR = new Set(["ouvrir", "rouvrir", "entrouvrir", "couvrir", "découvrir", "recouvrir", "offrir", "souffrir"]);
// Modèle « rendre » : -dre régulier (je rends / il rend / nous rendons ; pp en -u). EXCLUT prendre/-indre/-oudre.
const G3_ENDRE = new Set(["rendre", "vendre", "revendre", "répondre", "correspondre", "perdre", "entendre",
  "descendre", "redescendre", "défendre", "dépendre", "suspendre", "pendre", "tendre", "étendre", "prétendre",
  "détendre", "fondre", "confondre", "tondre", "pondre", "répandre", "fendre", "mordre", "tordre", "attendre"]);
// Modèle « peindre » : -eindre/-aindre/-oindre (je peins / il peint / nous peignons ; pp -eint/-aint/-oint).
const G3_INDRE = new Set(["peindre", "repeindre", "éteindre", "atteindre", "teindre", "déteindre", "feindre",
  "geindre", "ceindre", "restreindre", "astreindre", "étreindre", "empreindre", "craindre", "contraindre",
  "plaindre", "joindre", "rejoindre", "adjoindre", "disjoindre", "enjoindre", "poindre"]);
// Modèle « conduire » : -uire (je conduis / il conduit / nous conduisons ; pp -uit). EXCLUT nuire/luire (pp nui/lui).
const G3_UIRE = new Set(["conduire", "reconduire", "construire", "reconstruire", "détruire", "instruire",
  "produire", "reproduire", "introduire", "réduire", "traduire", "séduire", "déduire", "induire", "enduire", "cuire", "recuire"]);
// Modèle « connaître » : -aître (je connais / il connaît / nous connaissons ; pp -u). EXCLUT naître (né/être).
const G3_AITRE = new Set(["connaître", "reconnaître", "méconnaître", "paraître", "apparaître", "disparaître",
  "comparaître", "réapparaître", "transparaître"]);
// Modèle « venir/tenir » : je viens / il vient / nous venons / ils viennent ; futur -iendr, pp -enu, ps -ins.
// (venir & tenir eux-mêmes sont en table VERBS.) valeur = auxiliaire.
const G3_VENIR = { "devenir": "etre", "revenir": "etre", "parvenir": "etre", "survenir": "etre", "advenir": "etre",
  "intervenir": "etre", "provenir": "etre", "redevenir": "etre", "souvenir": "etre", "abstenir": "etre",
  "obtenir": "avoir", "retenir": "avoir", "contenir": "avoir", "maintenir": "avoir", "soutenir": "avoir",
  "détenir": "avoir", "entretenir": "avoir", "appartenir": "avoir", "convenir": "avoir", "prévenir": "avoir",
  "subvenir": "avoir", "contrevenir": "avoir", "disconvenir": "avoir", "circonvenir": "avoir" };
// Modèle « courir » : je cours / il court / nous courons ; pp -u, futur -rr, ps -us. (courir/mourir en table.)
const G3_COURIR = new Set(["accourir", "concourir", "discourir", "parcourir", "recourir", "secourir"]);
// Modèle « cueillir » : présent en -e (comme -er) ; pp -i, futur -erai.
const G3_CUEILLIR = new Set(["cueillir", "accueillir", "recueillir"]);
// Modèle « prendre » : je prends / il prend / nous prenons / ils prennent ; pp -pris. (prendre/comprendre en table.)
const G3_PRENDRE = new Set(["apprendre", "reprendre", "surprendre", "entreprendre", "méprendre", "déprendre", "désapprendre", "réapprendre"]);
// Modèle « mettre » : je mets / il met / nous mettons ; pp -mis. (mettre en table.)
const G3_METTRE = new Set(["permettre", "remettre", "admettre", "commettre", "promettre", "soumettre", "transmettre",
  "émettre", "compromettre", "omettre", "démettre", "retransmettre", "entremettre"]);
// Modèle « battre » : je bats / il bat / nous battons ; pp -ttu. (battre lui-même en table.)
const G3_BATTRE = new Set(["abattre", "combattre", "débattre", "rabattre", "ébattre", "rebattre", "embattre"]);
// Modèle « traire » : je trais / il trait / nous trayons (i→y) ; pp -ait, futur -airai.
const G3_TRAIRE = new Set(["traire", "extraire", "soustraire", "distraire", "abstraire", "retraire", "détraire"]);
// Modèle « assaillir » : présent en -e (j'assaille / nous assaillons) ; pp -i, futur -irai (≠ cueillir).
const G3_ASSAILLIR = new Set(["assaillir", "tressaillir", "défaillir"]);
function model_partir(inf, aux) {
  const long = inf.slice(0, -2), short = long.slice(0, -1);   // partir → « part » / « par »
  return { p: [short + "s", short + "s", short + "t", long + "ons", long + "ez", long + "ent"], pp: inf.slice(0, -1), aux, fut: inf };
}
function model_ouvrir(inf) {
  const st = inf.slice(0, -2);                                // ouvrir → « ouvr »
  return { p: [st + "e", st + "es", st + "e", st + "ons", st + "ez", st + "ent"], pp: inf.slice(0, -3) + "ert", aux: "avoir", fut: inf };
}
function model_endre(inf) {
  const st = inf.slice(0, -2);                                // rendre → « rend »
  return { p: [st + "s", st + "s", st, st + "ons", st + "ez", st + "ent"], pp: st + "u", aux: "avoir", fut: inf.slice(0, -1) };
}
function model_indre(inf) {
  const b = inf.slice(0, -5);                                 // peindre → « pe » ; craindre → « cra » ; joindre → « jo »
  return { p: [b + "ins", b + "ins", b + "int", b + "ignons", b + "ignez", b + "ignent"], pp: b + "int", aux: "avoir", fut: inf.slice(0, -1) };
}
function model_uire(inf) {
  const st = inf.slice(0, -3);                                // conduire → « condu »
  return { p: [st + "is", st + "is", st + "it", st + "isons", st + "isez", st + "isent"], pp: st + "it", aux: "avoir", fut: inf.slice(0, -1) };
}
function model_aitre(inf) {
  const st = inf.slice(0, -5);                                // connaître → « conn » ; paraître → « par »
  return { p: [st + "ais", st + "ais", st + "aît", st + "aissons", st + "aissez", st + "aissent"], pp: st + "u", aux: "avoir", fut: inf.slice(0, -1) };
}
function model_venir(inf, aux) {
  const b = inf.slice(0, -4);                                 // devenir → « dev » ; obtenir → « obt »
  return { p: [b + "iens", b + "iens", b + "ient", b + "enons", b + "enez", b + "iennent"], pp: b + "enu", aux, fut: b + "iendr" };
}
function model_courir(inf) {
  const b = inf.slice(0, -2);                                 // accourir → « accour »
  return { p: [b + "s", b + "s", b + "t", b + "ons", b + "ez", b + "ent"], pp: b + "u", aux: "avoir", fut: b + "r" };
}
function model_cueillir(inf) {
  const b = inf.slice(0, -2);                                 // accueillir → « accueill »
  return { p: [b + "e", b + "es", b + "e", b + "ons", b + "ez", b + "ent"], pp: b + "i", aux: "avoir", fut: b + "er" };
}
function model_prendre(inf) {
  const b = inf.slice(0, -7);                                 // apprendre → « ap »
  return { p: [b + "prends", b + "prends", b + "prend", b + "prenons", b + "prenez", b + "prennent"], pp: b + "pris", aux: "avoir", fut: b + "prendr" };
}
function model_mettre(inf) {
  const b = inf.slice(0, -6);                                 // permettre → « per »
  return { p: [b + "mets", b + "mets", b + "met", b + "mettons", b + "mettez", b + "mettent"], pp: b + "mis", aux: "avoir", fut: b + "mettr" };
}
function model_battre(inf) {
  const b = inf.slice(0, -3);                                 // abattre → « abat » (garde un seul t)
  return { p: [b + "s", b + "s", b, b + "tons", b + "tez", b + "tent"], pp: b + "tu", aux: "avoir", fut: inf.slice(0, -1) };
}
function model_traire(inf) {
  const b = inf.slice(0, -3);                                 // traire → « tra » ; extraire → « extra »
  return { p: [b + "is", b + "is", b + "it", b + "yons", b + "yez", b + "ient"], pp: b + "it", aux: "avoir", fut: b + "ir" };
}
function model_assaillir(inf) {
  const b = inf.slice(0, -2);                                 // assaillir → « assaill »
  return { p: [b + "e", b + "es", b + "e", b + "ons", b + "ez", b + "ent"], pp: b + "i", aux: "avoir", fut: inf };
}
function regular3(inf) {
  if (inf in G3_PARTIR) return model_partir(inf, G3_PARTIR[inf]);
  if (G3_OUVRIR.has(inf)) return model_ouvrir(inf);
  if (G3_ENDRE.has(inf)) return model_endre(inf);
  if (G3_INDRE.has(inf)) return model_indre(inf);
  if (G3_UIRE.has(inf)) return model_uire(inf);
  if (G3_AITRE.has(inf)) return model_aitre(inf);
  if (inf in G3_VENIR) return model_venir(inf, G3_VENIR[inf]);
  if (G3_COURIR.has(inf)) return model_courir(inf);
  if (G3_CUEILLIR.has(inf)) return model_cueillir(inf);
  if (G3_PRENDRE.has(inf)) return model_prendre(inf);
  if (G3_METTRE.has(inf)) return model_mettre(inf);
  if (G3_BATTRE.has(inf)) return model_battre(inf);
  if (G3_TRAIRE.has(inf)) return model_traire(inf);
  if (G3_ASSAILLIR.has(inf)) return model_assaillir(inf);
  return null;
}
// Verbe régulier/à modèle (1er groupe -er, 2e groupe -ir, familles régulières du 3e groupe).
// null si non reconnu → forme brute (jamais deviné). Les vrais irréguliers sont dans VERBS (priorité).
function regularVerb(inf) { return regular1(inf) || regular3(inf) || regular2(inf); }
// Participe présent : radical du « nous » présent (mange-ons) + -ant. 3 irréguliers fiables.
// Jamais deviné à tort : verbe inconnu → forme infinitive brute (via le garde-fou de conjugate).
const PART_PRESENT_IRR = { "être": "étant", "avoir": "ayant", "savoir": "sachant" };
function participePresent(infinitive, v) {
  if (PART_PRESENT_IRR[infinitive]) return PART_PRESENT_IRR[infinitive];
  const nous = v && v.p && v.p[3];
  if (nous && /ons$/.test(nous)) return nous.slice(0, -3) + "ant";
  return infinitive;
}
function conjugate(infinitive, subject = "je", tense = "present") {
  const v = VERBS[infinitive] || regularVerb(infinitive);
  const i = SUBJECT_INDEX[subject] ?? 0;
  // FORMES NON FINIES (indépendantes du sujet) : accès direct depuis n'importe quel verbe.
  if (tense === "infinitif") return infinitive;                         // aller, manger, être…
  if (tense === "participepresent") return participePresent(infinitive, v); // allant, mangeant, étant…
  if (tense === "gerondif") return "en " + participePresent(infinitive, v); // en allant, en mangeant…
  if (!v) return infinitive; // verbe non répertorié (2e/3e groupe) : forme brute, jamais deviné
  if (tense === "participepasse") return v.pp;                          // allé, mangé, été, eu…
  if (tense === "present") return v.p[i];
  if (tense === "imparfait") {
    let stem = imparfaitStem(v, infinitive), end = IMPARFAIT_END[i];
    // Verbes en -ger : le « e » (mange-) tombe devant « i » (nous mangions) mais reste devant « a » (je mangeais).
    if (stem.endsWith("ge") && end[0] === "i") stem = stem.slice(0, -1);
    // Verbes en -cer : la cédille (plaç-) disparaît devant « i » (nous placions) mais reste devant « a » (je plaçais).
    if (stem.endsWith("ç") && end[0] === "i") stem = stem.slice(0, -1) + "c";
    return stem + end;
  }
  if (tense === "futur")   return `${ALLER_PRES[i]} ${infinitive}`;        // futur proche (aller + infinitif)
  if (tense === "futursimple") return (v.fut || infinitive) + FUTUR_END[i]; // futur simple
  if (tense === "conditionnel") return (v.fut || infinitive) + IMPARFAIT_END[i]; // conditionnel présent
  if (tense === "passesimple") return passeSimple(infinitive, i);          // passé simple (lecture/littératie)
  if (tense === "imperatif") return imperatif(infinitive, i);              // impératif (mange ! allons ! …)
  if (tense === "passe") {                                                 // passé composé
    const aux = v.aux === "etre" ? ETRE[i] : AVOIR[i];
    const part = v.aux === "etre" ? ppAgreement(v.pp, subject) : v.pp;
    return `${aux} ${part}`;
  }
  return v.p[i];
}
const ALLER_PRES = VERBS["aller"].p;

function isVerb(infinitive) { return !!VERBS[infinitive]; }

// Détermine le sujet courant dans une liste de tokens, jusqu'à l'index donné.
const _DEMON_SUBJ = new Set(["ca", "cela", "ceci", "tout", "on"]);
function _naLabel(t) { return (t.label || "").toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").trim(); }
function subjectBefore(tokens, index) {
  let subj = "je", sawPronoun = false, nounSubj = null;
  for (let k = 0; k < index; k++) {
    const t = tokens[k];
    if (t.subject && SUBJECT_INDEX[t.subject] !== undefined) { subj = t.subject; sawPronoun = true; }
    // Démonstratif/indéfini sujet (« ça dépend », « cela marche », « tout va bien ») → 3e pers. sing.
    else if (_DEMON_SUBJ.has(_naLabel(t)) && !t.verb) nounSubj = { plural: false };
    // Un NOM en position sujet (avant le verbe, hors pronom) impose la 3e personne :
    // « le chat dort » (pas « dors »), « les chiens mangent » (pluriel). Un pronom sujet
    // explicite reste prioritaire (« moi, le chien, je pars » → « je »).
    else if ((t.color === "nom" || t.color === "descriptif") && !t.subject && !t.verb) nounSubj = t;
  }
  if (!sawPronoun && nounSubj) return nounSubj.plural ? "ils" : "il";
  return subj;
}

// Index inverse : forme au présent -> infinitif (pour conjuguer les phrases rapides).
const PRESENT_TO_INF = {};
Object.keys(VERBS).forEach(inf => VERBS[inf].p.forEach(f => { if (!(f in PRESENT_TO_INF)) PRESENT_TO_INF[f] = inf; }));
function presentToInf(form) { return PRESENT_TO_INF[(form || "").toLowerCase()] || null; }

window.Grammar = {
  VERBS, SUBJECT_INDEX, conjugate, isVerb, subjectBefore, presentToInf,
  labels: { present: "présent", imparfait: "imparfait", passe: "passé composé",
            futur: "futur proche", futursimple: "futur", conditionnel: "conditionnel",
            passesimple: "passé simple", imperatif: "impératif",
            infinitif: "infinitif", participepresent: "participe présent", participepasse: "participe passé", gerondif: "gérondif" },
};
