/* SPDX-License-Identifier: AGPL-3.0-only
   SPDX-FileCopyrightText: 2026 Com'Together */
/* COPIE FIGÉE (build 257) de l'ANCIEN moteur de phrase, tel qu'il était dans app.js AVANT
   l'extraction dans sentence.js. Corps VERBATIM ; seul l'accès à l'état est encapsulé
   (state local + window.Grammar). ARCHIVE d'audit — ne PAS charger en production. */
window.legacyBuild = function (tokens, ctx) {
  ctx = ctx || {};
  const state = { tokens: tokens, tense: ctx.tense || "present", settings: { registre: ctx.registre, genreLocuteur: ctx.genre } };
  const Grammar = window.Grammar;

  const DET_PLURAL = { "le": "les", "la": "les", "l'": "les", "les": "les", "un": "des", "une": "des",
    "des": "des", "mon": "mes", "ma": "mes", "mes": "mes", "ton": "tes", "ta": "tes", "ce": "ces",
    "cet": "ces", "cette": "ces", "ces": "ces", "du": "des" };
  const DET_FEM = { "le": "la", "un": "une", "mon": "ma", "ce": "cette", "ton": "ta", "du": "de la" };
  const DET_GENDER = { "le": "m", "un": "m", "mon": "m", "ce": "m", "cet": "m", "ton": "m", "du": "m",
    "la": "f", "une": "f", "ma": "f", "cette": "f", "ta": "f" };
  function isDeterminer(label) { return Object.prototype.hasOwnProperty.call(DET_PLURAL, (label || "").toLowerCase().trim()); }
  function genderBefore(tokens, index) {
    let g = "m";
    for (let k = 0; k < index; k++) {
      const t = tokens[k], lab = (t.label || "").toLowerCase().trim();
      if (DET_GENDER[lab] !== undefined) g = DET_GENDER[lab];
      if (t.fem) g = "f";
    }
    return g;
  }
  const SELF_SUBJ = new Set(["je", "j'", "j", "nous", "on", "me", "m'", "moi"]);
  const FEM_SUBJ = new Set(["elle", "elles"]);
  const OTHER_SUBJ = new Set(["il", "ils", "tu", "vous", "iel"]);
  function refFeminine(tokens, index) {
    for (let k = index - 1; k >= 0; k--) {
      const t = tokens[k]; if (t.verb || t.phrase) continue;
      const lab = (t.label || "").toLowerCase().trim();
      if (FEM_SUBJ.has(lab)) return true;
      if (OTHER_SUBJ.has(lab)) return false;
      if (SELF_SUBJ.has(lab)) return state.settings.genreLocuteur === "feminin";
    }
    return state.settings.genreLocuteur === "feminin";
  }
  function pluralize(w) {
    if (/[sxz]$/i.test(w)) return w;
    if (/(au|eu)$/i.test(w)) return w + "x";
    if (/al$/i.test(w)) return w.replace(/al$/i, "aux");
    return w + "s";
  }
  const ADJ_FEM = { "beau": "belle", "nouveau": "nouvelle", "vieux": "vieille", "fou": "folle",
    "mou": "molle", "blanc": "blanche", "franc": "franche", "frais": "fraîche", "sec": "sèche",
    "long": "longue", "gentil": "gentille", "gros": "grosse", "bas": "basse", "gras": "grasse",
    "épais": "épaisse", "bon": "bonne", "mignon": "mignonne", "roux": "rousse", "doux": "douce",
    "faux": "fausse", "jaloux": "jalouse", "fier": "fière", "favori": "favorite",
    "inquiet": "inquiète", "complet": "complète", "secret": "secrète", "coquet": "coquette",
    "muet": "muette", "net": "nette", "malin": "maligne", "public": "publique", "turc": "turque",
    "grec": "grecque", "menteur": "menteuse", "vengeur": "vengeresse", "protecteur": "protectrice",
    "neuf": "neuve", "vif": "vive", "actif": "active", "sportif": "sportive" };
  const ETAT_AVOIR = new Set(["froid", "chaud", "faim", "soif", "envie", "mal", "peur", "sommeil", "besoin", "raison", "tort", "honte"]);
  const ETAT_ETRE = new Set(["content", "triste", "fatigué", "malade", "fâché", "inquiet", "fier", "surpris", "jaloux", "amoureux", "calme", "stressé", "heureux", "désolé", "seul", "prêt"]);
  function stateAux(tokens, i) {
    const lab = (tokens[i].label || "").toLowerCase().trim();
    const aux = ETAT_AVOIR.has(lab) ? "avoir" : ETAT_ETRE.has(lab) ? "être" : null;
    if (!aux) return null;
    for (let k = i - 1; k >= 0; k--) {
      const p = tokens[k];
      if (p.phrase) return null;
      if (p.verb) return null;
      if (p.color === "nom" || isDeterminer(p.label)) return null;
      if (p.subject) return { aux, subj: p.subject };
    }
    return null;
  }
  function feminine(w) {
    const k = w.toLowerCase();
    if (ADJ_FEM[k]) return ADJ_FEM[k];
    if (/e$/.test(k)) return w;
    if (/er$/.test(k)) return w.slice(0, -2) + "ère";
    if (/(eux|eur)$/.test(k)) return w.replace(/(eux|eur)$/, "euse");
    if (/f$/.test(k)) return w.slice(0, -1) + "ve";
    if (/(el|eil|ul|il)$/.test(k)) return w + "le";
    if (/(en|on)$/.test(k)) return w + "ne";
    if (/(et|at|ot)$/.test(k)) return w + "te";
    return w + "e";
  }
  function tokenDisplay(tok, i) {
    if (tok.phrase) return conjugatePhrase(tok.label);
    if (tok.verb) {
      const prev = state.tokens[i - 1];
      if (prev && prev.verb) return tok.verb;
      let subj = Grammar.subjectBefore(state.tokens, i);
      const before = state.tokens.slice(0, i);
      if (state.tense !== "imperatif" && !before.some(t => t.subject) && before.some(t => (t.label || "").toLowerCase().trim() === "y")) subj = "il";
      return Grammar.conjugate(tok.verb, subj, state.tense);
    }
    const st = stateAux(state.tokens, i);
    if (st) {
      let adj = tok.label;
      if (st.aux === "être" && (tok.fem || refFeminine(state.tokens, i))) adj = feminine(adj);
      if (tok.plural) adj = pluralize(adj);
      return Grammar.conjugate(st.aux, st.subj, state.tense) + " " + adj;
    }
    let w = tok.label;
    if (isDeterminer(w)) {
      if (tok.fem) w = DET_FEM[w.toLowerCase().trim()] || w;
      if (tok.plural) return DET_PLURAL[w.toLowerCase().trim()] || w;
      return w;
    }
    const fem = tok.fem || genderBefore(state.tokens, i) === "f" || refFeminine(state.tokens, i);
    if (tok.color === "descriptif") {
      if (fem) w = feminine(w);
      if (tok.plural) w = pluralize(w);
      return w;
    }
    if (tok.plural) return pluralize(w);
    return w;
  }
  const IMPER_CLI = { me: "moi", te: "toi", se: "toi", moi: "moi", toi: "toi",
    le: "le", la: "la", les: "les", lui: "lui", leur: "leur", nous: "nous", vous: "vous", y: "y", en: "en" };
  function assembleImperative(toks, words) {
    const isCli = t => t && !t.verb && !t.subject && IMPER_CLI[(t.label || "").toLowerCase().trim()] !== undefined;
    const attach = {}, consumed = new Set();
    toks.forEach((t, i) => {
      if (!isCli(t)) return;
      let L = i - 1; while (L >= 0 && isCli(toks[L])) L--;
      let R = i + 1; while (R < toks.length && isCli(toks[R])) R++;
      let vi = -1;
      if (toks[i + 1] && toks[i + 1].verb) vi = i + 1;
      else if (toks[i - 1] && toks[i - 1].verb) vi = i - 1;
      else if (toks[R] && toks[R].verb) vi = R;
      else if (L >= 0 && toks[L] && toks[L].verb) vi = L;
      if (vi >= 0) { (attach[vi] = attach[vi] || []).push(IMPER_CLI[(t.label || "").toLowerCase().trim()]); consumed.add(i); }
    });
    const out = [];
    toks.forEach((t, i) => {
      if (consumed.has(i) || t.subject) return;
      let w = words[i];
      if (attach[i]) {
        const cl = attach[i];
        if (/^(y|en)$/.test(cl[0]) && /[ae]$/i.test(w)) w += "s";
        w += "-" + cl.join("-");
      }
      out.push(w);
    });
    return out.join(" ");
  }
  function contract(s) {
    return s
      .replace(/(^|\s)à\s+les(?=\s|$)/gi, "$1aux").replace(/(^|\s)à\s+le(?=\s|$)/gi, "$1au")
      .replace(/(^|\s)de\s+les(?=\s|$)/gi, "$1des").replace(/(^|\s)de\s+le(?=\s|$)/gi, "$1du");
  }
  const _SUBJ = { "j": "je", "je": "je", "tu": "tu", "il": "il", "elle": "elle", "iel": "iel", "on": "on", "nous": "nous", "vous": "vous", "ils": "ils" };
  function conjugatePhrase(label) {
    const t = state.tense;
    const oral = state.settings.registre === "oral";
    if (t === "present") {
      if (!oral) return label;
      const mm = label.match(/^ce\s+n'est\s+pas\s+(.*)$/i);
      if (mm) return "c'est pas " + mm[1];
      return label.replace(/\bne\s+/i, "").replace(/\bn'/i, "");
    }
    const CEST = { imparfait: "c'était ", passe: "ça a été ", futur: "ça va être ", futursimple: "ce sera ", conditionnel: "ce serait " };
    const CNEST = { imparfait: "ce n'était pas ", passe: "ça n'a pas été ", futur: "ça ne va pas être ", futursimple: "ce ne sera pas ", conditionnel: "ce ne serait pas " };
    let m = label.match(/^c'est\s+(.*)$/i);
    if (m) return (CEST[t] || "c'est ") + m[1];
    m = label.match(/^ce\s+n'est\s+pas\s+(.*)$/i);
    if (m) return oral ? (CEST[t] || "c'est ") + "pas " + m[1] : (CNEST[t] || "ce n'est pas ") + m[1];
    m = label.match(/^(j'|je|tu|il|elle|iel|on|nous|vous|ils)\s*(ne\s+|n')?(\S+?)(?:\s+(pas|plus|jamais))?(\s.*)?$/i);
    if (!m) return label;
    const subj = _SUBJ[m[1].replace(/'/g, "").toLowerCase()];
    if (!subj) return label;
    const inf = Grammar.presentToInf(m[3]);
    if (!inf) return label;
    const neg = m[2] ? (m[4] || "pas") : null;
    const rest = m[5] || "";
    const conj = Grammar.conjugate(inf, subj, t);
    let verb;
    if (neg) {
      const parts = conj.split(" ");
      verb = `${oral ? "" : "ne "}${parts[0]} ${neg} ${parts.slice(1).join(" ")}`.trim();
    } else { verb = conj; }
    return contract(elide(`${subj} ${verb}${rest}`.replace(/\s+/g, " ").trim()));
  }
  function elide(s) {
    return s.replace(/\b(je|le|la|me|te|se|ne|de|que)\s+(?=[aeiouyéèêàâîïôûh])/gi,
      (m, w) => w.toLowerCase().slice(0, -1) + "'");
  }
  function sentence() {
    const words = state.tokens.map((t, i) => (t.say && !t.verb && !t.plural) ? t.say : tokenDisplay(t, i));
    const joined = (state.tense === "imperatif") ? assembleImperative(state.tokens, words) : words.join(" ");
    return contract(elide(joined)).replace(/\s+([?!.,])/g, "$1");
  }
  return sentence();
};
