/* Nutri App — extended data for all 4 modules
   (Calories already covered in data.jsx — this adds Finance, Goals, Diary
    and a project registry for the sidebar.) */

/* ── PROJECT REGISTRY ─────────────────────────────────────────────── */
const NUTRI_PROJECTS = [
  { id: 'calories',  label: 'Calories',  type: 'diet',      color: '#1F3D2E', emoji: '🍽️' },
  { id: 'finance',   label: 'Finance',   type: 'finance',   color: '#5784D8', emoji: '💳' },
  { id: 'goals',     label: 'Goals',     type: 'goal',      color: '#E89B3C', emoji: '🎯' },
  { id: 'diary',     label: 'Diary',     type: 'diary',     color: '#A99CE0', emoji: '📓' },
  { id: 'movies',    label: 'Movie Night', type: 'movies',  color: '#C25A4E', emoji: '🎬' },
  { id: 'assistant', label: 'Assistant', type: 'assistant', color: '#4FA862', emoji: '✨' },
  { id: 'settings',  label: 'Settings',  type: 'settings',  color: '#7A766C', emoji: '⚙️' },
];

/* ── FINANCE DATA ─────────────────────────────────────────────────── */
const NF_CURRENCY = 'EGP';
let NF_ACCOUNTS = [
  { id: 'cash',    name: 'Cash',         startingBalance: 0, icon: '💵', kind: 'cash'   },
  { id: 'bank',    name: 'Bank Account', startingBalance: 0, icon: '🏦', kind: 'bank'   },
  { id: 'card',    name: 'Credit Card',  startingBalance: 0, icon: '💳', kind: 'card'   },
  { id: 'savings', name: 'Savings',      startingBalance: 0, icon: '🪙', kind: 'savings' },
];

/* Full default finance category seed (expense categories) + Income.
   Brand-new users get this list; existing users get it MERGED into their
   stored categories (see mergeFinanceCategories + the app migration). */
function _finSlug(s) {
  return String(s).toLowerCase().replace(/&/g, ' and ').replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
}
const _FIN_SEED_DEFS = [
  { name: 'Food & Drinks', icon: '🍽️', color: '#E89B3C', type: 'need', subs: ['Groceries','Restaurants','Cafes','Coffee','Snacks','Food Delivery','Healthy Food','Supplements','Water / Drinks','Other Food & Drinks'] },
  { name: 'Transportation', icon: '🚗', color: '#5784D8', type: 'need', subs: ['Fuel','Ride Hailing','Taxi','Public Transport','Metro','Bus','Parking','Car Maintenance','Car Wash','Travel Transportation','Other Transportation'] },
  { name: 'Travel', icon: '✈️', color: '#5BB874', type: 'want', subs: ['Accommodation','Flights','Transportation Travel','Food Travel','Activities','Tickets','Visa / Documents','Travel Shopping','Other Travel'] },
  { name: 'Home & Utilities', icon: '🏠', color: '#A06CC0', type: 'need', subs: ['Rent','Electricity','Water','Gas','Internet','Mobile Bill','Home Maintenance','Home Supplies','Furniture','Appliances','Cleaning','Other Home & Utilities'] },
  { name: 'Family', icon: '👨‍👩‍👧', color: '#4FA862', type: 'need', subs: ['Parents','Siblings','Family Support','Gifts','Family Events','Family Food','Family Medical','Other Family'] },
  { name: 'Health & Fitness', icon: '💊', color: '#C25A4E', type: 'need', subs: ['Doctor','Medication','Tests / Labs','Dental','Gym','Sports','Supplements','Healthy Food','Therapy','Medical Insurance','Other Health & Fitness'] },
  { name: 'Work & Tools', icon: '🛠️', color: '#7A766C', type: 'need', subs: ['Software','Subscriptions','Equipment','Devices','Courses','Books','Office Supplies','Freelance Tools','AI Tools','Design Tools','Hosting / Domains','Other Work & Tools'] },
  { name: 'Entertainment & Shopping', icon: '🛍️', color: '#D8745A', type: 'want', subs: ['Clothes','Shoes','Accessories','Electronics','Personal Shopping','Games','Movies','Events','Outings','Gifts','Hobbies','Other Entertainment & Shopping'] },
  { name: 'Financial', icon: '🏦', color: '#5784D8', type: 'need', subs: ['Bank Fees','ATM Fees','Loan Payment','Installment','Debt Payment','Savings','Investment','Transfer Fees','Currency Exchange','Gold Purchase','Other Financial'] },
  { name: 'Education', icon: '📚', color: '#A99CE0', type: 'need', subs: ['Courses','Books','Certificates','Workshops','University / School','Learning Tools','Other Education'] },
  { name: 'Subscriptions', icon: '🔁', color: '#A06CC0', type: 'want', subs: ['Streaming','Music','Apps','Cloud Storage','Software','AI Subscription','Gym Subscription','Internet Subscription','Other Subscriptions'] },
  { name: 'Projects', icon: '📁', color: '#E89B3C', type: 'want', subs: ['Personal Project','Client Project','Event Project','Equipment Project','Travel Project','Car Project','Home Project','Course Project','Other Project'] },
  { name: 'Fixed Expenses / Installments', icon: '🧾', color: '#C25A4E', type: 'need', subs: ['Rent','Loan Installment','Car Installment','Credit Card Installment','Course Installment','Subscription','Internet Bill','Mobile Bill','Insurance','Other Fixed Expense'] },
  { name: 'Other', icon: '📦', color: '#7A766C', type: null, subs: ['Miscellaneous','Unknown','Unsorted','Cash Adjustment','Manual Correction','Other'] },
];
function _buildFinanceSeed() {
  const cats = _FIN_SEED_DEFS.map(def => {
    const id = _finSlug(def.name);
    return {
      id, name: def.name, color: def.color, icon: def.icon,
      ...(def.type ? { spendingType: def.type } : {}),
      subcategories: def.subs.map(sn => ({ id: id + '__' + _finSlug(sn), name: sn })),
    };
  });
  // Income category preserved — income sources live here (income form + paste rely on id:'income').
  cats.push({ id: 'income', name: 'Income', color: '#4FA862', icon: '💰', subcategories: [
    { id: 'income_salary', name: 'Salary' },
    { id: 'income_freelance', name: 'Freelance' },
    { id: 'income_other', name: 'Other' },
  ]});
  return cats;
}
// Legacy defaults — kept so existing transactions referencing these ids
// (food/transport/bills/...) still resolve after the seed migration.
const NF_CATEGORIES_LEGACY = [
  { id: 'food', name: 'Food', color: '#E89B3C', icon: '🍽️', spendingType: 'need', subcategories: [
    { id: 'food_restaurants', name: 'Restaurants' }, { id: 'food_groceries', name: 'Groceries' },
    { id: 'food_coffee', name: 'Coffee & Drinks' }, { id: 'food_delivery', name: 'Delivery' } ]},
  { id: 'transport', name: 'Transport', color: '#5784D8', icon: '🚗', spendingType: 'need', subcategories: [
    { id: 'transport_fuel', name: 'Fuel' }, { id: 'transport_taxi', name: 'Taxi / Uber' },
    { id: 'transport_public', name: 'Public transit' }, { id: 'transport_parking', name: 'Parking' } ]},
  { id: 'bills', name: 'Bills', color: '#A06CC0', icon: '⚡', spendingType: 'need', subcategories: [
    { id: 'bills_electric', name: 'Electricity' }, { id: 'bills_internet', name: 'Internet' },
    { id: 'bills_phone', name: 'Phone' }, { id: 'bills_rent', name: 'Rent' } ]},
  { id: 'health', name: 'Health', color: '#4FA862', icon: '💊', spendingType: 'need', subcategories: [
    { id: 'health_medicine', name: 'Medicine' }, { id: 'health_doctor', name: 'Doctor' }, { id: 'health_gym', name: 'Gym' } ]},
  { id: 'leisure', name: 'Leisure', color: '#D8745A', icon: '🎮', spendingType: 'want', subcategories: [
    { id: 'leisure_dining', name: 'Dining out' }, { id: 'leisure_cinema', name: 'Cinema' }, { id: 'leisure_subs', name: 'Subscriptions' } ]},
  { id: 'shopping', name: 'Shopping', color: '#7A766C', icon: '🛍️', spendingType: 'want', subcategories: [
    { id: 'shopping_clothes', name: 'Clothes' }, { id: 'shopping_tech', name: 'Electronics' }, { id: 'shopping_home', name: 'Home & Decor' } ]},
];
let NF_CATEGORIES = _buildFinanceSeed();
// Pristine seed reference (NF_CATEGORIES gets reassigned to the live list at render).
const NF_CATEGORIES_SEED = _buildFinanceSeed();

const NF_BUDGETS = {
  food:     4500,
  transport:2000,
  bills:    1800,
  health:   1000,
  leisure:  1500,
  shopping: 2500,
};

const ND_MOOD_META = {
  great: { label: 'Great', color: '#4FA862', emoji: '😄' },
  good:  { label: 'Good',  color: '#5784D8', emoji: '🙂' },
  okay:  { label: 'Okay',  color: '#E89B3C', emoji: '😐' },
  low:   { label: 'Low',   color: '#C25A4E', emoji: '😕' },
};
const ND_TAG_COLORS = {
  work: '#5784D8', friends: '#A99CE0', food: '#E89B3C', family: '#4FA862',
  gym: '#C25A4E', morning: '#7A766C', weekend: '#A06CC0', travel: '#5BB874',
};

/* ── HELPERS ──────────────────────────────────────────────────────── */
function nfMoneyFmt(n) {
  const abs = Math.abs(n);
  const s = abs.toLocaleString(undefined, { maximumFractionDigits: 0 });
  return (n < 0 ? '-' : '') + s;
}
function nfTotalsForRange(txns, daysBack = 30) {
  const cutoff = daysAgo(daysBack);
  const inRange = txns.filter(t => t.date >= cutoff);
  const income = inRange.filter(t => t.kind === 'income').reduce((s,t) => s + (t.egpEquivalent != null ? t.egpEquivalent : t.amount), 0);
  const expenses = inRange.filter(t => t.kind === 'expense').reduce((s,t) => s + (t.egpEquivalent != null ? t.egpEquivalent : Math.abs(t.amount)), 0);
  return { income, expenses, net: income - expenses, count: inRange.length };
}
function nfByCategory(txns, daysBack = 30) {
  const cutoff = daysAgo(daysBack);
  const inRange = txns.filter(t => t.date >= cutoff && t.kind === 'expense');
  const grouped = {};
  inRange.forEach(t => {
    grouped[t.category] = (grouped[t.category] || 0) + (t.egpEquivalent != null ? t.egpEquivalent : Math.abs(t.amount));
  });
  return Object.entries(grouped)
    .map(([id, v]) => ({ ...NF_CATEGORIES.find(c => c.id === id), total: v }))
    .sort((a, b) => b.total - a.total);
}

function ngGoalCompletion(goal, logs, dateISO) {
  const log = logs[dateISO] || {};
  const v = log[goal.id];
  if (v === null || v === undefined) return null; // not scheduled
  return Math.min(1, v / goal.target);
}
function ngStreak(goal, logs, todayISO) {
  let streak = 0;
  for (let i = 0; i < 100; i++) {
    const iso = daysAgo(i);
    const c = ngGoalCompletion(goal, logs, iso);
    if (c === null) continue; // skip non-scheduled
    if (c >= 1) streak++;
    else break;
  }
  return streak;
}

/* ── GOLD ─────────────────────────────────────────────────────────── */
const GOLD_KARATS = [
  { id: '24k', label: '24K', purity: 1.0 },
  { id: '21k', label: '21K', purity: 21/24 },
  { id: '18k', label: '18K', purity: 18/24 },
  { id: '14k', label: '14K', purity: 14/24 },
];

function goldPriceForKarat(goldPrices, karat) {
  if (goldPrices && goldPrices[karat] != null) return +goldPrices[karat];
  return null;
}

function goldHoldingCurrentValue(holding, goldPrices) {
  const pricePerGram = goldPriceForKarat(goldPrices, holding.karat);
  if (!pricePerGram) return null;
  return +(pricePerGram * (holding.weightGrams || 0)).toFixed(2);
}

function goldHoldingPurchaseValue(holding) {
  if (holding.totalPurchaseValue != null && +holding.totalPurchaseValue > 0) return +holding.totalPurchaseValue;
  if (holding.purchasePricePerGram != null && +holding.purchasePricePerGram > 0) {
    return +(+holding.purchasePricePerGram * (holding.weightGrams || 0)).toFixed(2);
  }
  return null;
}

function goldTotalStats(holdings, goldPrices) {
  let totalWeight = 0, totalCurrentValue = 0, totalPurchaseValue = 0, hasPurchase = false, hasPrices = false;
  (holdings || []).forEach(h => {
    totalWeight += +(h.weightGrams) || 0;
    const cv = goldHoldingCurrentValue(h, goldPrices);
    if (cv != null) { totalCurrentValue += cv; hasPrices = true; }
    const pv = goldHoldingPurchaseValue(h);
    if (pv != null) { totalPurchaseValue += pv; hasPurchase = true; }
  });
  return {
    totalWeight,
    totalCurrentValue: hasPrices ? totalCurrentValue : null,
    totalPurchaseValue: hasPurchase ? totalPurchaseValue : null,
    profitLoss: (hasPrices && hasPurchase) ? totalCurrentValue - totalPurchaseValue : null,
  };
}

/* ── FINANCE: category merge + projects + fixed expenses ──────────────── */
// Union seed into base, preserving custom cats/subs; never deletes. Matches by
// id then case-insensitive name; merges missing subcategories by name.
function mergeFinanceCategories(base, seed) {
  const out = (base || []).map(c => ({ ...c, subcategories: (c.subcategories || []).map(s => ({ ...s })) }));
  const byId = {}, byName = {};
  out.forEach(c => { byId[c.id] = c; byName[(c.name || '').trim().toLowerCase()] = c; });
  (seed || []).forEach(sc => {
    let target = byId[sc.id] || byName[(sc.name || '').trim().toLowerCase()];
    if (!target) {
      target = { ...sc, subcategories: (sc.subcategories || []).map(s => ({ ...s })) };
      out.push(target); byId[target.id] = target; byName[(target.name || '').trim().toLowerCase()] = target;
      return;
    }
    const have = new Set((target.subcategories || []).map(s => (s.name || '').trim().toLowerCase()));
    (sc.subcategories || []).forEach(s => {
      const k = (s.name || '').trim().toLowerCase();
      if (!have.has(k)) { target.subcategories = (target.subcategories || []).concat([{ ...s }]); have.add(k); }
    });
  });
  return out;
}

/* A transaction can be linked to multiple projects. Read links uniformly via this helper:
   prefers projectIds[] and falls back to the legacy single `project` id (safe migration). */
function txnProjectIds(t) {
  if (t && Array.isArray(t.projectIds)) return t.projectIds.filter(Boolean);
  if (t && t.project) return [t.project];
  return [];
}

/* Projects — spending/income/budget per project (uses EGP-equivalent amounts).
   A multi-project expense counts its full amount toward EACH linked project. */
function financeProjectStats(project, txns) {
  let spent = 0, income = 0, count = 0;
  (txns || []).forEach(t => {
    if (txnProjectIds(t).indexOf(project.id) < 0) return;
    const egp = t.egpEquivalent != null ? t.egpEquivalent : Math.abs(t.amount);
    if (t.kind === 'income') income += egp;
    else if (t.kind === 'expense') { spent += egp; count++; }
  });
  const budget = project.budget != null && +project.budget > 0 ? +project.budget : null;
  return {
    spent: +spent.toFixed(2), income: +income.toFixed(2), count,
    budget, remaining: budget != null ? +(budget - spent).toFixed(2) : null,
    pct: budget ? Math.min(1, spent / budget) : null,
  };
}

/* Fixed expenses / installments — schedule, status, stats */
function _finIso(d) {
  return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
}
function _finAddByFreq(startISO, freq, intervalDays, n) {
  const d = new Date(startISO + 'T00:00:00');
  const iv = +intervalDays || 30;
  if (freq === 'daily') d.setDate(d.getDate() + n);
  else if (freq === 'weekly') d.setDate(d.getDate() + 7 * n);
  else if (freq === 'yearly') d.setFullYear(d.getFullYear() + n);
  else if (freq === 'everyX' || freq === 'custom') d.setDate(d.getDate() + iv * n);
  else d.setMonth(d.getMonth() + n); // monthly (default)
  return _finIso(d);
}
function fixedToEgp(amount, currency, usdRate) {
  const a = Math.abs(+amount || 0);
  if (!currency || currency === 'EGP') return a;
  if (currency === 'USD' && usdRate) return +(a * usdRate).toFixed(2);
  return a;
}
function fixedPaidCount(fx) { return ((fx && fx.paidDates) || []).length; }
function fixedRemainingCount(fx) {
  if (!fx || !fx.installmentsTotal) return null;
  return Math.max(0, (+fx.installmentsTotal) - fixedPaidCount(fx));
}
function fixedOccurrences(fx, fromISO, toISO) {
  const out = [];
  if (!fx || !fx.startDate) return out;
  const maxN = fx.installmentsTotal ? +fx.installmentsTotal : 240;
  for (let n = 0; n < maxN; n++) {
    const date = _finAddByFreq(fx.startDate, fx.frequency, fx.intervalDays, n);
    if (fx.endDate && date > fx.endDate) break;
    if (toISO && date > toISO) break;
    if (!fromISO || date >= fromISO) out.push({ date, index: n });
    if (out.length > 500) break;
  }
  return out;
}
function fixedOccurrenceStatus(fx, dateISO, todayISO) {
  if (((fx.paidDates) || []).indexOf(dateISO) >= 0) return 'paid';
  if (dateISO < todayISO) return 'overdue';
  if (dateISO === todayISO) return 'due';
  return 'upcoming';
}
function fixedNextDue(fx, todayISO) {
  const occ = fixedOccurrences(fx, null, _finAddByFreq(todayISO, 'yearly', null, 2));
  const unpaid = occ.filter(o => ((fx.paidDates) || []).indexOf(o.date) < 0);
  if (!unpaid.length) return null;
  const overdue = unpaid.filter(o => o.date < todayISO);
  if (overdue.length) return { date: overdue[overdue.length - 1].date, status: 'overdue', overdueCount: overdue.length };
  const next = unpaid.find(o => o.date >= todayISO);
  return next ? { date: next.date, status: next.date === todayISO ? 'due' : 'upcoming', overdueCount: 0 } : null;
}
function fixedMonthlyEgp(fx, usdRate) {
  const amt = fixedToEgp(fx.amount, fx.currency, usdRate);
  const f = fx.frequency || 'monthly';
  if (f === 'daily') return +(amt * 30).toFixed(2);
  if (f === 'weekly') return +(amt * 52 / 12).toFixed(2);
  if (f === 'yearly') return +(amt / 12).toFixed(2);
  if (f === 'everyX' || f === 'custom') { const iv = +fx.intervalDays || 30; return +(amt * 30 / iv).toFixed(2); }
  return amt;
}
function fixedExpenseStats(list, opts) {
  const o = opts || {};
  const todayISO = o.todayISO || _finIso(new Date());
  const usdRate = o.usdRate || null;
  const ym = todayISO.slice(0, 7);
  const monthStart = ym + '-01', monthEnd = ym + '-31';
  const active = (list || []).filter(fx => (fx.status || 'active') === 'active');
  let monthTotal = 0, monthPaid = 0, monthUnpaid = 0, overdue = 0, upcoming = 0, remainingInstallments = 0, monthlyCommitment = 0;
  let overdueCount = 0, upcomingCount = 0;
  const byCategory = {}, byProject = {};
  active.forEach(fx => {
    const egp = fixedToEgp(fx.amount, fx.currency, usdRate);
    monthlyCommitment += fixedMonthlyEgp(fx, usdRate);
    const rc = fixedRemainingCount(fx);
    if (rc != null) remainingInstallments += rc * egp;
    fixedOccurrences(fx, monthStart, monthEnd).forEach(occ => {
      const st = fixedOccurrenceStatus(fx, occ.date, todayISO);
      monthTotal += egp;
      if (st === 'paid') monthPaid += egp; else monthUnpaid += egp;
      byCategory[fx.category || 'other'] = (byCategory[fx.category || 'other'] || 0) + egp;
      if (fx.project) byProject[fx.project] = (byProject[fx.project] || 0) + egp;
    });
    const nd = fixedNextDue(fx, todayISO);
    if (nd) {
      if (nd.status === 'overdue') { overdue += egp * (nd.overdueCount || 1); overdueCount += (nd.overdueCount || 1); }
      else { upcoming += egp; upcomingCount++; }
    }
  });
  return {
    todayISO, month: ym,
    monthTotal: +monthTotal.toFixed(2), monthPaid: +monthPaid.toFixed(2), monthUnpaid: +monthUnpaid.toFixed(2),
    overdue: +overdue.toFixed(2), overdueCount, upcoming: +upcoming.toFixed(2), upcomingCount,
    remainingInstallments: +remainingInstallments.toFixed(2), monthlyCommitment: +monthlyCommitment.toFixed(2),
    byCategory, byProject, activeCount: active.length,
  };
}
function fixedScheduleList(list, opts) {
  const o = opts || {};
  const todayISO = o.todayISO || _finIso(new Date());
  const fromISO = o.fromISO || _finAddByFreq(todayISO, 'monthly', null, -2);
  const toISO = o.toISO || _finAddByFreq(todayISO, 'monthly', null, 3);
  const rows = [];
  (list || []).forEach(fx => {
    if ((fx.status || 'active') !== 'active') return;
    fixedOccurrences(fx, fromISO, toISO).forEach(occ => {
      rows.push({ fixedId: fx.id, name: fx.name, amount: fx.amount, currency: fx.currency,
        category: fx.category, subcategory: fx.subcategory, account: fx.account, project: fx.project,
        date: occ.date, index: occ.index, status: fixedOccurrenceStatus(fx, occ.date, todayISO) });
    });
  });
  rows.sort((a, b) => a.date.localeCompare(b.date));
  return rows;
}

Object.assign(window, {
  NUTRI_PROJECTS, NF_CURRENCY, NF_BUDGETS,
  ND_MOOD_META, ND_TAG_COLORS,
  nfMoneyFmt, nfTotalsForRange, nfByCategory, ngGoalCompletion, ngStreak,
  GOLD_KARATS, goldPriceForKarat, goldHoldingCurrentValue, goldHoldingPurchaseValue, goldTotalStats,
  NF_CATEGORIES_LEGACY, NF_CATEGORIES_SEED, mergeFinanceCategories, financeProjectStats, txnProjectIds,
  fixedToEgp, fixedPaidCount, fixedRemainingCount, fixedOccurrences, fixedOccurrenceStatus,
  fixedNextDue, fixedMonthlyEgp, fixedExpenseStats, fixedScheduleList, _finIso, _finAddByFreq,
});
// NF_ACCOUNTS and NF_CATEGORIES are module-level `let` variables.
// nutri-app.jsx overrides them at render time with store-backed live data.
// Access via window.NF_ACCOUNTS / window.NF_CATEGORIES after first render.
window.NF_ACCOUNTS = NF_ACCOUNTS;
window.NF_CATEGORIES = NF_CATEGORIES;
