/* ───────────────────────────────────────────────
   Happy Guts — data model + verdict engine
   Food data is loaded at runtime from the per-compound
   CSV tables in data/foods/ (see data/foods/README.md).
   Exposed on window: COMPOUNDS, LEVELS, FOODS, RECIPES,
   SHOPPING_SEED, computeVerdict, levelMeta, loadFoodData
   ─────────────────────────────────────────────── */

// The four sensitivities. Order matters (used everywhere) and also defines
// which CSV files are loaded: data/foods/<key>.csv
const COMPOUNDS = [
  { key: 'gluten',      name: 'Gluten',      blurb: 'A protein in wheat, barley and rye.',                     cssVar: '--c-gluten',      glyph: 'wheat' },
  { key: 'salicylates', name: 'Salicylates', blurb: 'Natural chemicals in many fruits, veg, herbs and spices.', cssVar: '--c-salicylates', glyph: 'leaf'  },
  { key: 'oxalates',    name: 'Oxalates',    blurb: 'Compounds high in greens, nuts and some roots.',           cssVar: '--c-oxalates',    glyph: 'crystal' },
  { key: 'amines',      name: 'Amines',      blurb: 'Build up in aged, fermented and ripened foods.',           cssVar: '--c-amines',      glyph: 'flask' },
];

// 5-step level scale, plus 'Unknown' for foods with no researched value yet.
// Unknown is deliberately NOT in LEVELS / LEVEL_VALUE: it has no position on the ramp.
const LEVELS = ['Nil', 'Low', 'Med', 'High', 'V.High'];
const LEVEL_VALUE = { 'Nil': 0, 'Low': 1, 'Med': 2, 'High': 3, 'V.High': 4 };
const UNKNOWN = 'Unknown';
function isUnknown(level) { return LEVEL_VALUE[level] === undefined; }

const LEVEL_META = {
  'Nil':    { v: 0,    color: 'var(--lv-nil)',     label: 'Nil' },
  'Low':    { v: 1,    color: 'var(--lv-low)',     label: 'Low' },
  'Med':    { v: 2,    color: 'var(--lv-med)',     label: 'Med' },
  'High':   { v: 3,    color: 'var(--lv-high)',    label: 'High' },
  'V.High': { v: 4,    color: 'var(--lv-vhigh)',   label: 'V.High' },
  'Unknown':{ v: null, color: 'var(--lv-unknown)', label: 'Unknown' },
};
function levelMeta(level) {
  return LEVEL_META[level] || LEVEL_META[UNKNOWN];
}

// Some foods change compound levels with preparation (cooking).
// `cooked` is a PARTIAL override merged over `levels` when prep === 'cooked'.
// Boiling + draining is the big lever for oxalates.
function effectiveLevels(food, prep) {
  const state = prep || food.prepDefault || 'raw';
  if (state === 'cooked' && food.cooked) return { ...food.levels, ...food.cooked };
  return food.levels;
}
function hasPrep(food) { return !!food.cooked; }
// which compounds actually shift between raw & cooked
function prepDeltas(food) {
  if (!food.cooked) return [];
  return Object.keys(food.cooked).filter(k => food.cooked[k] !== food.levels[k]);
}

/* ── food database (populated by loadFoodData) ────
   Kept as a stable reference and mutated in place so every
   `window.FOODS` reader sees the rows once loading finishes. */
const FOODS = [];

// Recipes reference food ids; verdict aggregates from ingredients.
// ids are the numeric food ids from data/foods/salicylates.csv
// (1364 Rice - White, 237 Chicken, 730 Egg (Chicken) - Fresh, 153 Pear - Ripe, Peeled, 7 Cabbage - Green,
//  37 Cucumber - Peeled, 284 Salmon, 20 White Potato - Peeled, 1450 Oats, 155 Banana - Ripe)
const RECIPES = [
  { id: 'r-congee',  name: 'Soothing Rice Congee',  time: '35 min', serves: 2, tag: 'Gentle on everything',
    ingredients: ['1364','237','730'],  blurb: 'A plain, warming bowl built from the lowest-trigger staples.' },
  { id: 'r-bowl',    name: 'Pear & Cabbage Slaw',   time: '15 min', serves: 2, tag: 'Crunchy + fresh',
    ingredients: ['153','7','37'],      blurb: 'Crisp, peeled produce that stays gentle for most sensitivities.' },
  { id: 'r-salmon',  name: 'Fresh Salmon & Potato', time: '30 min', serves: 2, tag: 'Protein-forward',
    ingredients: ['284','20','7'],      blurb: 'Use the freshest fish you can to keep amines low.' },
  { id: 'r-oats',    name: 'Morning Oats & Banana', time: '10 min', serves: 1, tag: 'Watch ripeness',
    ingredients: ['1450','155'],        blurb: 'Quick breakfast — riper bananas push amines up.' },
];

const SHOPPING_SEED = {
  id: 'list-week',
  name: 'This Week',
  items: [
    { id: 's1', foodId: '1364', qty: '1 bag',   done: false },
    { id: 's2', foodId: '153',  qty: '6',        done: false },
    { id: 's3', foodId: '237',  qty: '500 g',    done: true  },
    { id: 's4', foodId: '7',    qty: '1 head',   done: false },
    { id: 's5', foodId: '730',  qty: '1 dozen',  done: false },
    { id: 's6', foodId: '20',   qty: '1 kg',     done: true  },
  ],
};

/* ── CSV parsing + loader ─────────────────────────
   Splits one CSV row into fields, honouring double-quoted
   values (so notes may contain commas) and "" escapes. */
function splitCSVLine(line) {
  const out = [];
  let cur = '', inQuotes = false;
  for (let i = 0; i < line.length; i++) {
    const ch = line[i];
    if (inQuotes) {
      if (ch === '"') {
        if (line[i + 1] === '"') { cur += '"'; i++; }
        else inQuotes = false;
      } else cur += ch;
    } else if (ch === '"') {
      inQuotes = true;
    } else if (ch === ',') {
      out.push(cur); cur = '';
    } else cur += ch;
  }
  out.push(cur);
  return out;
}

function parseCSV(text) {
  const lines = text.replace(/\r\n?/g, '\n').split('\n').filter(l => l.trim().length > 0);
  if (lines.length === 0) return [];
  const headers = splitCSVLine(lines[0]).map(h => h.trim());
  return lines.slice(1).map(line => {
    const cells = splitCSVLine(line);
    const row = {};
    headers.forEach((h, i) => { row[h] = (cells[i] !== undefined ? cells[i] : '').trim(); });
    return row;
  });
}

// Coerce a CSV cell to a valid level. 'Unknown' (any case) and blank cells mean
// "not researched yet" and stay Unknown — they are never silently treated as Nil.
function normLevel(raw, ctx) {
  const v = (raw || '').trim();
  if (LEVEL_VALUE[v] !== undefined) return v;
  if (v.toLowerCase() === 'unknown' || v === '') return UNKNOWN;
  console.warn(`[Happy Guts] Unrecognised level "${v}" ${ctx || ''} — treating as Unknown.`);
  return UNKNOWN;
}

// Merge the per-compound tables into FOODS, joined by id.
// Row order follows the FIRST compound's CSV so the demo's
// "Good for you today" / "Worth avoiding" picks stay stable.
function buildFoods(parsed) {
  const map = new Map();
  const order = [];
  for (const c of COMPOUNDS) {
    for (const row of (parsed[c.key] || [])) {
      const id = row.id;
      if (!id) continue;
      let food = map.get(id);
      if (!food) {
        food = {
          id, name: row.name, cat: row.category, note: row.note, image: row.image_name,
          levels: { gluten: UNKNOWN, salicylates: UNKNOWN, oxalates: UNKNOWN, amines: UNKNOWN },
        };
        map.set(id, food);
        order.push(id);
      } else {
        // later tables fill any identity field the first table left blank
        food.name = food.name || row.name;
        food.cat = food.cat || row.category;
        food.note = food.note || row.note;
        food.image = food.image || row.image_name;
      }
      const lvl = normLevel(row.level, `for ${id}/${c.key}`);
      food.levels[c.key] = lvl;
      const cookedRaw = (row.cooked_level || '').trim();
      if (cookedRaw) {
        const cooked = normLevel(cookedRaw, `(cooked) for ${id}/${c.key}`);
        if (cooked !== lvl) {
          food.cooked = food.cooked || {};
          food.cooked[c.key] = cooked;
        }
      }
    }
  }
  return order.map(id => map.get(id));
}

// Fetch all four compound CSVs, build FOODS in place, return it.
async function loadFoodData(basePath = 'data/foods') {
  const parsed = {};
  await Promise.all(COMPOUNDS.map(async c => {
    const url = `${basePath}/${c.key}.csv`;
    const res = await fetch(url);
    if (!res.ok) throw new Error(`Failed to load ${url} (${res.status})`);
    parsed[c.key] = parseCSV(await res.text());
  }));
  const built = buildFoods(parsed);
  FOODS.length = 0;
  built.forEach(f => FOODS.push(f));
  return FOODS;
}

/* ── verdict engine ───────────────────────────────
   sens: { gluten:'yes'|'no'|'maybe', ... }
   returns { verdict:'safe'|'caution'|'avoid',
             flags:[{key,name,level,levelV,status}], worst } */
function computeVerdict(food, sens, prep) {
  const flags = [];
  let worst = 'safe';
  const levels = effectiveLevels(food, prep);
  for (const c of COMPOUNDS) {
    const setting = (sens && sens[c.key]) || 'no';
    const level = levels[c.key];
    const lv = isUnknown(level) ? null : LEVEL_VALUE[level];
    let status = 'ok';            // ok | watch | bad | unknown
    if (lv === null) {
      // No data: for a compound you react to, we can't call it safe — treat as "Go easy".
      if (setting === 'yes' || setting === 'maybe') status = 'unknown';
    } else if (setting === 'yes') {
      if (lv >= 3) status = 'bad';
      else if (lv === 2) status = 'watch';
    } else if (setting === 'maybe') {
      if (lv >= 4) status = 'bad';
      else if (lv >= 2) status = 'watch';
    }
    if (status === 'bad') worst = 'avoid';
    else if ((status === 'watch' || status === 'unknown') && worst !== 'avoid') worst = 'caution';
    flags.push({ key: c.key, name: c.name, level, levelV: lv, status, setting });
  }
  return { verdict: worst, flags };
}

// concerns = the compounds the user actually reacts to (yes/maybe)
function activeCompounds(sens) {
  return COMPOUNDS.filter(c => sens && (sens[c.key] === 'yes' || sens[c.key] === 'maybe'));
}

const VERDICT_META = {
  safe:    { label: 'Good to eat', color: 'var(--safe)',    tint: 'var(--safe-tint)',    ink: 'var(--safe-ink)' },
  caution: { label: 'Go easy',     color: 'var(--caution)', tint: 'var(--caution-tint)', ink: 'var(--caution-ink)' },
  avoid:   { label: 'Best avoided',color: 'var(--avoid)',   tint: 'var(--avoid-tint)',   ink: 'var(--avoid-ink)' },
};

Object.assign(window, {
  COMPOUNDS, LEVELS, LEVEL_VALUE, UNKNOWN, isUnknown, FOODS, RECIPES, SHOPPING_SEED,
  computeVerdict, levelMeta, activeCompounds, VERDICT_META,
  effectiveLevels, hasPrep, prepDeltas,
  loadFoodData, parseCSV,
});
