/* SPDX-License-Identifier: AGPL-3.0-only
   SPDX-FileCopyrightText: 2026 Com'Together */
/* PROPRIÉTÉS GÉNÉRALES du moteur de conjugaison : des règles vérifiées sur TOUTE la table de
   verbes × sujets × temps (des centaines de combinaisons), pas cas par cas. Elles attrapent des
   FAMILLES entières de bugs. Source unique, lue par run.html et le runner en ligne de commande.
   window.runProperties(G) -> { total, passed, failures:[{property, example}] }. */
window.runProperties = function (G) {
  const subjects = ["je", "tu", "il", "elle", "nous", "vous", "ils", "elles"];
  const tenses = ["present", "imparfait", "passe", "futur", "futursimple", "conditionnel", "passesimple", "imperatif"];
  const verbs = Object.keys(G.VERBS || {});
  const failures = [];
  let total = 0;
  const fail = (property, example) => { if (!failures.find(f => f.property === property)) failures.push({ property, example }); };

  // Instantané de la table AVANT (pour vérifier la non-mutation).
  const before = JSON.stringify(G.VERBS);

  verbs.forEach(v => subjects.forEach(s => tenses.forEach(t => {
    total++;
    let out, threw = false;
    try { out = G.conjugate(v, s, t); } catch (e) { threw = true; }
    const label = `${v}/${s}/${t}`;
    if (threw) { fail("ne jette jamais d'exception", label); return; }
    if (typeof out !== "string") { fail("renvoie toujours une chaîne", label + " → " + typeof out); return; }
    if (out.length === 0) fail("jamais une sortie vide", label);
    if (/undefined|\[object Object\]|null|NaN/.test(out)) fail("jamais 'undefined' / '[object Object]' / 'null' / 'NaN'", label + " → " + out);
    if (/\s{2,}/.test(out)) fail("jamais d'espace double", label + " → " + out);
    if (out !== out.trim()) fail("jamais d'espace en tête ou en fin", label + " → '" + out + "'");
    // Déterminisme : deux appels identiques donnent le même résultat.
    let out2; try { out2 = G.conjugate(v, s, t); } catch (e) { out2 = "∅"; }
    if (out2 !== out) fail("résultat déterministe (stable)", label);
  })));

  // Non-mutation de la table de verbes.
  if (JSON.stringify(G.VERBS) !== before) fail("conjuguer ne modifie pas la table VERBS", "table modifiée");

  // Entrées dégradées : ne jamais planter.
  [["", "je", "present"], ["   ", "je", "present"], ["zzz", "je", "present"], ["xyzer", "je", "present"],
   ["manger", "??", "present"], ["manger", "je", "tempsbidon"], [null, "je", "present"]].forEach(a => {
    total++;
    try { const o = G.conjugate(a[0], a[1], a[2]); if (typeof o !== "string" && o !== a[0]) fail("entrée dégradée : échoue proprement (chaîne, pas de crash)", JSON.stringify(a) + " → " + o); }
    catch (e) { fail("entrée dégradée : échoue proprement (chaîne, pas de crash)", JSON.stringify(a) + " → EXCEPTION " + e.message); }
  });

  // Discipline d'accord : avec AVOIR, le participe est INVARIABLE selon le genre/nombre du sujet ;
  // avec ÊTRE, il VARIE. (L'accord n'apparaît que quand la règle le demande.)
  const avoirVerb = verbs.find(v => G.VERBS[v].aux !== "etre");
  const etreVerb = verbs.find(v => G.VERBS[v].aux === "etre");
  if (avoirVerb) {
    total++;
    const il = G.conjugate(avoirVerb, "il", "passe"), elle = G.conjugate(avoirVerb, "elle", "passe");
    if (il !== elle) fail("passé composé avec avoir : participe invariable selon le genre", avoirVerb + " : '" + il + "' vs '" + elle + "'");
  }
  if (etreVerb) {
    total++;
    const il = G.conjugate(etreVerb, "il", "passe"), elle = G.conjugate(etreVerb, "elle", "passe");
    if (il === elle) fail("passé composé avec être : participe accordé au féminin", etreVerb + " : '" + il + "' == '" + elle + "'");
  }

  return { checks: total, propertiesFailed: failures.length, failures, ok: failures.length === 0 };
};
