// ============================================================
// MOTORE — algoritmo di generazione e modifica delle ricette
// Sceglie gli ingredienti dal dataset rispettando i limiti
// nutrizionali del paziente (sodio, potassio, fosforo, proteine)
// ============================================================
(function(){
// I dati arrivano dal backend dopo il bootstrap: si sincronizzano
// all'ingresso di ogni funzione pubblica, non al caricamento dello script.
let FOODS = [], FOOD_BY_ID = {};
function syncData(){ ({ FOODS, FOOD_BY_ID } = window.NefroData); }

const byCat = (c) => FOODS.filter(f => f.categoria === c);
// Elenco nutrienti centralizzato nel modulo clinico (include i parametri A3:
// liquidi, grassi saturi, fosforo inorganico). Fallback se non caricato.
const NUTRIENTS = (window.NefroRx && window.NefroRx.NUTRIENTS) ||
  ["proteine","sodio","potassio","fosforo","calorie","calcio","lipidi","carbo","fibre"];

// —— RNG deterministico (mulberry32) per settimane riproducibili ——
function rng(seed){
  let a = seed >>> 0;
  return function(){
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}
function hashStr(s){ let h=2166136261; for(let i=0;i<s.length;i++){ h^=s.charCodeAt(i); h=Math.imul(h,16777619);} return h>>>0; }

// —— Calcolo dei totali di una ricetta ——
function totals(recipe){
  syncData();
  const t = Object.fromEntries(NUTRIENTS.map(n=>[n,0]));
  for(const ing of recipe.ingredienti){
    const f = FOOD_BY_ID[ing.id]; if(!f) continue;
    const k = ing.grammi/100;
    for(const n of NUTRIENTS) t[n] += (f[n]||0)*k;
  }
  for(const n of NUTRIENTS) t[n] = Math.round(t[n]*10)/10;
  return t;
}

// —— Totali di un'intera giornata (3 pasti) ——
function dayTotals(day){
  syncData();
  const t = Object.fromEntries(NUTRIENTS.map(n=>[n,0]));
  for(const slot of ["colazione","pranzo","cena"]){
    const r = day && day[slot]; if(!r) continue;
    const tt = totals(r);
    for(const n of NUTRIENTS) t[n] += tt[n]||0;
  }
  for(const n of NUTRIENTS) t[n] = Math.round(t[n]*10)/10;
  return t;
}

// —— Validazione clinica dell'intera settimana (A5/A6) ——
// Per ogni giorno: totali + alert rispetto alla prescrizione tipizzata.
function validateWeek(week, patient){
  const Rx = window.NefroRx;
  return (week||[]).map(g=>{
    const tot = dayTotals(g);
    const ev = Rx ? Rx.evaluateDay(tot, patient) : { alerts:[], worst:"ok" };
    return { id:g.id, nome:g.nome, totals:tot, alerts:ev.alerts, worst:ev.worst };
  });
}
// Vero se almeno un giorno sfora un tetto invalicabile.
function weekHasOver(validation){ return (validation||[]).some(d=>d.worst==="over"); }

// —— Pool di ingredienti per slot, filtrati per paziente ——
function pools(patient){
  const ex = new Set((patient.preferenze?.esclusi)||[]);
  const veg = patient.diet === "vegetariana";
  const keep = arr => arr.filter(f => !ex.has(f.id));
  let proteine = veg
    ? [...byCat("Legumi"), ...byCat("Uova"), ...byCat("Formaggi")]
    : [...byCat("Carne"), ...byCat("Pesce"), ...byCat("Uova"), ...byCat("Legumi")];
  return {
    base_colaz: keep([...byCat("Latticini"), ...byCat("Veg")]),
    cereale_colaz: keep(FOODS.filter(f=>["fette_biscottate","pane_bianco","avena","cornflakes","pane_aprot","pane_integrale"].includes(f.id))),
    frutta: keep(byCat("Frutta")),
    carbo: keep(FOODS.filter(f=>["pasta_semola","riso","pasta_aprot","pane_bianco","polenta","orzo","farro","riso_integrale"].includes(f.id))),
    proteine: keep(proteine),
    verdura: keep(byCat("Verdure")),
    condimento: keep(byCat("Grassi")),
    formaggio: keep(byCat("Formaggi")),
  };
}

// Punteggio: privilegia alimenti "renali" (basso K/P/Na). Più basso = meglio.
function renalScore(f){ return (f.potassio||0) + (f.fosforo||0)*1.3 + (f.sodio||0)*0.4; }

// Scelta pesata verso il punteggio migliore, con varietà controllata
function pick(pool, rand, avoid){
  let cand = pool.filter(f => !avoid.has(f.id));
  if(!cand.length) cand = pool;
  if(!cand.length) return null;
  const ranked = [...cand].sort((a,b)=>renalScore(a)-renalScore(b));
  // pesca dal primo 60% con bias verso la testa
  const span = Math.max(1, Math.ceil(ranked.length*0.6));
  const idx = Math.floor(Math.pow(rand(),1.7)*span);
  return ranked[Math.min(idx, ranked.length-1)];
}

const cap = s => s.charAt(0).toUpperCase()+s.slice(1);

// —— Costruzione dei tre pasti ——
function buildColazione(P, rand, avoid){
  const base = pick(P.base_colaz, rand, avoid);
  const cer = pick(P.cereale_colaz, rand, avoid);
  const fru = pick(P.frutta, rand, avoid);
  const ing = [
    {id:base.id, grammi:200},
    {id:cer.id, grammi:40},
    {id:fru.id, grammi:150},
  ];
  return mk("colazione", `${cap(base.nome.toLowerCase())} con ${cer.nome.toLowerCase()} e ${fru.nome.toLowerCase()}`, ing);
}
function buildPranzo(P, rand, avoid){
  const carbo = pick(P.carbo, rand, avoid);
  const prot = pick(P.proteine, rand, avoid);
  const verd = pick(P.verdura, rand, avoid);
  const cond = P.condimento[0];
  const ing = [
    {id:carbo.id, grammi:80},
    {id:prot.id, grammi:120},
    {id:verd.id, grammi:200},
    {id:cond.id, grammi:10},
  ];
  return mk("pranzo", `${cap(carbo.nome.split(" ")[0].toLowerCase())} con ${prot.nome.toLowerCase()} e ${verd.nome.toLowerCase()}`, ing);
}
function buildCena(P, rand, avoid){
  const prot = pick(P.proteine, rand, avoid);
  const verd = pick(P.verdura, rand, avoid);
  const carbo = pick(P.carbo, rand, avoid);
  const cond = P.condimento[0];
  const ing = [
    {id:prot.id, grammi:130},
    {id:verd.id, grammi:200},
    {id:carbo.id, grammi:50},
    {id:cond.id, grammi:8},
  ];
  return mk("cena", `${cap(prot.nome.toLowerCase())} con ${verd.nome.toLowerCase()}`, ing);
}

// —— Passaggi di preparazione generati dagli ingredienti ——
// Se l'alimento ha un procedimento proprio (dal prontuario) si usa quello.
const VERBO_COTTURA = { crudo:"Servire crudo", bollito:"Lessare", "al vapore":"Cuocere al vapore",
  "al forno":"Cuocere al forno", "alla griglia":"Grigliare", "in padella":"Cuocere in padella" };
function generaPassaggi(pasto, ingredienti){
  const items = ingredienti.map(i => ({ f: FOOD_BY_ID[i.id], ing: i })).filter(x => x.f);
  if(!items.length) return "";
  const steps = [];
  if(pasto === "colazione"){
    const [a, b, c] = items;
    if(a) steps.push(`Versare ${a.f.nome.toLowerCase()} (${a.ing.grammi} g) in una tazza o ciotola.`);
    if(b) steps.push(`Aggiungere ${b.f.nome.toLowerCase()} (${b.ing.grammi} g).`);
    if(c) steps.push(`Completare con ${c.f.nome.toLowerCase()} (${c.ing.grammi} g) a pezzi.`);
  } else {
    for(const { f, ing } of items){
      if(f.categoria === "Grassi") continue;
      if(f.procedimento){ steps.push(f.procedimento); continue; }
      const cott = ing.cottura || f.cottura;
      const verbo = cott ? (VERBO_COTTURA[cott] || `Cuocere ${cott}`) : "Preparare";
      steps.push(`${verbo} ${f.nome.toLowerCase()} (${ing.grammi} g).`);
    }
    const olio = items.find(x => x.f.categoria === "Grassi");
    if(olio) steps.push(`Condire con ${olio.f.nome.toLowerCase()} (${olio.ing.grammi} g) a crudo.`);
    steps.push("Impiattare e servire.");
  }
  return steps.map((s, i) => `${i + 1}. ${s}`).join("\n");
}

let _uid = 0;
function mk(pasto, nome, ingredienti){
  return { id:`r${Date.now().toString(36)}${(_uid++).toString(36)}`, pasto, nome, ingredienti,
    fonte:"generata", procedimento: generaPassaggi(pasto, ingredienti),
    motivo:"Generata dal motore scegliendo alimenti a basso carico renale (K/P/Na)." };
}

// —— Ricetta dal ricettario adatta al paziente (o null) ——
const VEG_VIETATE = new Set(["Carne","Pesce","Carne lavorata","Conservati"]);
function pickFromCatalog(pasto, patient, rand, usate){
  const catalogo = window.NefroData.RECIPES || [];
  const ex = new Set((patient.preferenze?.esclusi)||[]);
  const veg = patient.diet === "vegetariana";
  const ok = catalogo.filter(r =>
    r.pasto === pasto && !usate.has(r.id) &&
    (r.ingredienti||[]).length > 0 &&
    r.ingredienti.every(i => {
      const f = FOOD_BY_ID[i.id];
      if(!f || ex.has(i.id)) return false;
      if(veg && VEG_VIETATE.has(f.categoria)) return false;
      return true;
    })
  );
  if(!ok.length) return null;
  const r = ok[Math.floor(rand()*ok.length)];
  usate.add(r.id);
  return { ...mk(pasto, r.nome, r.ingredienti.map(i=>({...i}))), fonte:"ricettario", ricettaId:r.id,
    motivo:"Inclusa dal ricettario perché compatibile con esclusioni e dieta del paziente.",
    ...(r.cottura ? { cottura:r.cottura } : {}),
    ...(r.procedimento ? { procedimento:r.procedimento } : {}) };
}

const GIORNI = ["Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato","Domenica"];

// —— Genera la settimana completa ——
function generateWeek(patient, salt=0){
  syncData();
  const P = pools(patient);
  const rand = rng(hashStr(patient.id)+salt*7919);
  const usate = new Set(); // ricette del ricettario già impiegate nella settimana
  const giorni = GIORNI.map((nome,i)=>{
    // metà circa dei pasti viene recuperata dal ricettario (se compatibile
    // col paziente), il resto viene inventato dal generatore
    const c = (rand() < 0.5 && pickFromCatalog("colazione", patient, rand, usate)) || buildColazione(P, rand, new Set());
    const p = (rand() < 0.5 && pickFromCatalog("pranzo", patient, rand, usate)) || buildPranzo(P, rand, new Set());
    const ce = (rand() < 0.5 && pickFromCatalog("cena", patient, rand, usate)) || buildCena(P, rand, new Set());
    return { id:`g${i}`, nome, colazione:c, pranzo:p, cena:ce };
  });
  return giorni;
}

// —— Sostituzione di un singolo ingrediente con alternativa ——
function categoriaSlot(food, patient){
  syncData();
  const P = pools(patient);
  for(const key of ["base_colaz","cereale_colaz","frutta","carbo","proteine","verdura","formaggio"]){
    if(P[key].some(f=>f.id===food.id)) return P[key];
  }
  // fallback: stessa categoria
  return FOODS.filter(f=>f.categoria===food.categoria);
}
function substitute(recipe, oldId, patient, salt=0){
  syncData();
  const old = FOOD_BY_ID[oldId];
  const pool = categoriaSlot(old, patient).filter(f=>f.id!==oldId);
  const ex = new Set((patient.preferenze?.esclusi)||[]);
  const rand = rng(hashStr(recipe.id+oldId)+salt*104729);
  const cand = pool.filter(f=>!ex.has(f.id));
  const chosen = pick(cand.length?cand:pool, rand, new Set());
  if(!chosen) return recipe;
  const ingredienti = recipe.ingredienti.map(ing => ing.id===oldId ? {...ing, id:chosen.id} : ing);
  const nome = rebuildName(recipe.pasto, ingredienti);
  // i passaggi scritti a mano (ricette del ricettario) non vengono toccati
  const procedimento = recipe.fonte === "ricettario" ? recipe.procedimento : generaPassaggi(recipe.pasto, ingredienti);
  const motivo = `Sostituito «${old.nome.toLowerCase()}» → «${chosen.nome.toLowerCase()}»: alternativa dello stesso gruppo a più basso carico renale (K/P/Na).`;
  return { ...recipe, ingredienti, nome, procedimento, motivo };
}

// Ricostruisce un nome leggibile dai 2-3 ingredienti principali
function rebuildName(pasto, ingredienti){
  syncData();
  const names = ingredienti
    .map(i=>FOOD_BY_ID[i.id])
    .filter(f=>f && f.categoria!=="Grassi")
    .map(f=>f.nome.toLowerCase());
  if(pasto==="colazione") return cap(names[0])+ (names[1]?` con ${names[1]}`:"") + (names[2]?` e ${names[2]}`:"");
  if(pasto==="cena") return cap(names[0]) + (names[1]?` con ${names[1]}`:"");
  return cap(names[0].split(" ")[0]) + (names[1]?` con ${names[1]}`:"") + (names[2]?` e ${names[2]}`:"");
}

// —— Parser comandi "vibe" in linguaggio naturale ——
const escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
// vero se "name" compare nel testo come parola intera ("riso" sì in
// "sostituisci il riso", no dentro "risotto")
function containsWord(t, name){
  return new RegExp(`(^|[^a-zà-ù])${escRe(name)}([^a-zà-ù]|$)`, "i").test(t);
}
function findFoodInText(text){
  const t = text.toLowerCase();
  // 1) il testo contiene il nome completo dell'alimento: vince il più lungo
  //    ("sostituisci il riso" → Riso; "niente riso integrale" → Riso integrale)
  let best = null;
  for(const f of FOODS){
    const n = f.nome.toLowerCase();
    if(containsWord(t, n) && (!best || n.length > best.nome.length)) best = f;
  }
  if(best) return best;
  // 2) fallback: prima parola del nome ("la pasta" → Pasta di semola)
  const sorted = [...FOODS].sort((a,b)=>a.nome.length-b.nome.length);
  for(const f of sorted){
    const w = f.nome.toLowerCase().split(" ")[0];
    if(w.length>3 && containsWord(t, w)) return f;
  }
  return null;
}

// parole → categoria del dataset
const CATEGORY_WORDS = [
  [/pesc/, "Pesce"], [/salum|affettat|prosciutt|insaccat/, "Carne lavorata"], [/carne|carni/, "Carne"],
  [/verdur|ortagg/, "Verdure"],
  [/frutta secca|noci|mandorl|nocciol/, "Frutta secca"], [/frutta|frutto/, "Frutta"],
  [/legum/, "Legumi"], [/formagg/, "Formaggi"], [/latticin|latte|yogurt/, "Latticini"],
  [/cereal|pasta|riso|pane|polenta/, "Cereali"], [/uov/, "Uova"], [/dolc|dessert/, "Dolci"],
];
function findCategoryInText(text){
  for(const [re,cat] of CATEGORY_WORDS) if(re.test(text)) return cat;
  return null;
}
// quali pasti coinvolgere
function mealsInText(text){
  const m = [];
  if(/colazione|mattin/.test(text)) m.push("colazione");
  if(/pranzo/.test(text)) m.push("pranzo");
  if(/cena|sera/.test(text)) m.push("cena");
  return m.length ? m : ["colazione","pranzo","cena"];
}

// sostituisce un ingrediente evitando un'intera categoria (per "niente <categoria>")
function substituteAvoidCat(recipe, oldId, patient, avoidCat, salt){
  const old = FOOD_BY_ID[oldId];
  let pool = categoriaSlot(old, patient).filter(f=>f.id!==oldId && f.categoria!==avoidCat);
  const ex = new Set((patient.preferenze?.esclusi)||[]);
  pool = pool.filter(f=>!ex.has(f.id));
  if(!pool.length) return recipe;
  const rand = (function(s){let a=s>>>0;return ()=>{a|=0;a=(a+0x6D2B79F5)|0;let x=Math.imul(a^(a>>>15),1|a);x=(x+Math.imul(x^(x>>>7),61|x))^x;return((x^(x>>>14))>>>0)/4294967296;};})(hashStr(recipe.id+oldId)+salt*1299709);
  const ranked = [...pool].sort((a,b)=>renalScore(a)-renalScore(b));
  const chosen = ranked[Math.floor(Math.pow(rand(),1.7)*ranked.length)] || ranked[0];
  const ingredienti = recipe.ingredienti.map(ing=> ing.id===oldId? {...ing, id:chosen.id}:ing);
  const procedimento = recipe.fonte === "ricettario" ? recipe.procedimento : generaPassaggi(recipe.pasto, ingredienti);
  const motivo = `Rimosso «${old.nome.toLowerCase()}» (categoria ${avoidCat}) → «${chosen.nome.toLowerCase()}», nei limiti renali.`;
  return {...recipe, ingredienti, nome:rebuildName(recipe.pasto, ingredienti), procedimento, motivo};
}

// A5: dopo ogni comando ri-valida l'intera settimana contro la prescrizione
// e, se un giorno sfora un tetto invalicabile, lo segnala nel report e nel
// messaggio (nessuna modifica "dichiarata conforme" senza verifica).
function withValidation(report, patient){
  report.validation = validateWeek(report.week, patient);
  const overDays = report.validation.filter(d => d.worst === "over");
  report.warnOver = overDays.length > 0;
  if(report.ok && overDays.length){
    const nomi = overDays.map(d => d.nome).join(", ");
    report.message += ` ⚠️ Attenzione: dopo la modifica ${overDays.length} giorn${overDays.length===1?"o":"i"} (${nomi}) supera un tetto prescritto — rigenera o correggi il pasto.`;
  }
  return report;
}

function applyCommand(text, week, patient){
  syncData();
  const t = text.toLowerCase().trim();
  const report = { ok:false, message:"", week, log:[] };
  const swap = /(sostitu|cambia|al posto|invece|un altro|un'altra|diverso|diversa|alterna|varia)/.test(t);
  const hardRemove = /(non voglio|niente|togli|togliere|senza|elimina|evita|\bno\b)/.test(t);
  const remove = swap || hardRemove;
  const more = /(pi[uù]|aumenta|aggiungi|di pi[uù]|extra)/.test(t);
  const meals = mealsInText(t);
  const mealLabel = meals.length===3 ? "" : " a "+meals.map(m=>m==="colazione"?"colazione":m).join(" e ");

  // 1) rigenerazione completa
  if(/(rigenera|nuova settimana|rifai|cambia tutto)/.test(t)){
    report.ok=true; report.week=generateWeek(patient, Date.now()%9999);
    report.log.push({azione:"rigenerazione", motivo:"Intera settimana rigenerata scegliendo alimenti nei limiti prescritti."});
    report.message="Ho rigenerato l'intera settimana con nuove proposte conformi ai limiti.";
    return withValidation(report, patient);
  }

  const food = findFoodInText(t);
  const cat = findCategoryInText(t);

  // 2) "più <categoria>" → aumenta/aggiunge quella categoria nei pasti indicati
  if(more && cat){
    const P = pools(patient);
    const poolForCat = FOODS.filter(f=>f.categoria===cat && !(patient.preferenze?.esclusi||[]).includes(f.id));
    let count=0;
    const rand=(function(s){let a=s>>>0;return ()=>{a|=0;a=(a+0x6D2B79F5)|0;let x=Math.imul(a^(a>>>15),1|a);x=(x+Math.imul(x^(x>>>7),61|x))^x;return((x^(x>>>14))>>>0)/4294967296;};})(hashStr(patient.id+cat)+Date.now()%9999);
    const newWeek = week.map(g=>{
      const ng={...g};
      for(const slot of meals){
        const r=g[slot]; const ing=[...r.ingredienti];
        const has = ing.filter(i=>FOOD_BY_ID[i.id]?.categoria===cat);
        if(has.length){ has.forEach(h=>{ const idx=ing.findIndex(i=>i.id===h.id); ing[idx]={...ing[idx], grammi:Math.round(ing[idx].grammi*1.4/5)*5}; }); count++; }
        else { const add=pick(poolForCat, rand, new Set(ing.map(i=>i.id))); if(add){ ing.push({id:add.id, grammi:cat==="Verdure"?120:60}); count++; } }
        ng[slot]={...r, ingredienti:ing, nome:rebuildName(r.pasto, ing),
          procedimento: r.fonte==="ricettario" ? r.procedimento : generaPassaggi(r.pasto, ing)};
      }
      return ng;
    });
    report.ok=true; report.week=newWeek;
    report.message=`Fatto. Ho aumentato la quota di ${cat.toLowerCase()}${mealLabel} in ${count} past${count===1?"o":"i"}, restando nei limiti di ${patient.nome}.`;
    return withValidation(report, patient);
  }

  // 3) singolo alimento
  if(food && remove){
    let count=0;
    const newWeek = week.map(g=>{
      const ng={...g};
      for(const slot of meals){
        const r=g[slot];
        if(r.ingredienti.some(i=>i.id===food.id)){ ng[slot]=substitute(r, food.id, patient, count+1); report.log.push({azione:"sostituzione", giorno:g.nome, pasto:slot, motivo:ng[slot].motivo}); count++; }
      }
      return ng;
    });
    report.ok=true; report.week=newWeek;
    report.message = count>0
      ? `Fatto. Ho sostituito «${food.nome.toLowerCase()}»${mealLabel} in ${count} ricett${count===1?"a":"e"}, mantenendo i limiti di sodio, potassio, fosforo e proteine.`
      : `«${food.nome}» non era presente nelle ricette${mealLabel||" di questa settimana"}.`;
    return withValidation(report, patient);
  }

  // 4) categoria (es. "sostituisci il pesce a cena", "niente latticini")
  if(cat && remove){
    let count=0;
    const newWeek = week.map(g=>{
      const ng={...g};
      for(const slot of meals){
        const r=g[slot]; let cur=r;
        cur.ingredienti.filter(i=>FOOD_BY_ID[i.id]?.categoria===cat).forEach(i=>{
          cur = hardRemove
            ? substituteAvoidCat(cur, i.id, patient, cat, count+1)   // elimina la categoria
            : substitute(cur, i.id, patient, count+1);               // cambia con altro dello stesso slot
          report.log.push({azione: hardRemove?"eliminazione":"sostituzione", giorno:g.nome, pasto:slot, motivo:cur.motivo});
          count++;
        });
        ng[slot]=cur;
      }
      return ng;
    });
    report.ok = count>0;
    report.week = newWeek;
    report.message = count>0
      ? (hardRemove
          ? `Fatto. Ho eliminato ${cat.toLowerCase()}${mealLabel} sostituendo ${count} aliment${count===1?"o":"i"} con alternative nei limiti renali.`
          : `Fatto. Ho cambiato ${cat.toLowerCase()}${mealLabel} in ${count} ricett${count===1?"a":"e"}, restando nei limiti di ${patient.nome}.`)
      : `Non c'erano alimenti della categoria «${cat}»${mealLabel||" nel piano"}.`;
    return withValidation(report, patient);
  }

  report.message = (food||cat)
    ? `Ho capito che parli di «${(food?food.nome:cat).toLowerCase()}». Prova ad esempio: «niente ${(food?food.nome:cat).toLowerCase()} a cena» oppure «più ${(cat||"verdure").toLowerCase()} a pranzo».`
    : "Non ho individuato un alimento. Prova con frasi tipo «niente zucchine», «sostituisci il pesce a cena» o «più verdure a pranzo».";
  return withValidation(report, patient);
}

window.NefroEngine = { totals, dayTotals, validateWeek, weekHasOver, generateWeek, substitute, applyCommand, GIORNI, categoriaSlot, rebuildName, generaPassaggi };
})();
