/* data.jsx — shared scenarios + user profile */

const PROFILE = {
  name: 'Alex',
  age: 32,
  height: 178, // cm
  weight: 76,  // kg
  sex: 'male',
  activityLevel: 'moderately_active',
  bodyFatPercent: null,
  tdeeOverride: null,
};

const ACTIVITY_MULTIPLIERS = {
  sedentary: 1.2,
  lightly_active: 1.375,
  moderately_active: 1.55,
  very_active: 1.725,
  athlete: 1.9,
};

function normalizeSex(sex) {
  const s = String(sex || '').toLowerCase();
  if (s === 'm' || s === 'male') return 'male';
  if (s === 'f' || s === 'female') return 'female';
  return 'male';
}

function calculateMifflinStJeor(profile) {
  const sex = normalizeSex(profile.sex || profile.gender);
  const weightKg = profile.weightKg ?? profile.weight;
  const heightCm = profile.heightCm ?? profile.height;
  const base = 10 * weightKg + 6.25 * heightCm - 5 * profile.age;
  return Math.round(base + (sex === 'female' ? -161 : 5));
}

function calculateKatchMcArdle(profile) {
  if (!Number.isFinite(profile.bodyFatPercent) || profile.bodyFatPercent <= 0 || profile.bodyFatPercent >= 100) {
    return null;
  }
  const weightKg = profile.weightKg ?? profile.weight;
  const leanMassKg = weightKg * (1 - profile.bodyFatPercent / 100);
  return Math.round(370 + 21.6 * leanMassKg);
}

function getActivityMultiplier(activityLevel) {
  return ACTIVITY_MULTIPLIERS[activityLevel] || ACTIVITY_MULTIPLIERS.moderately_active;
}

function calculateBmr(profile) {
  return calculateKatchMcArdle(profile) || calculateMifflinStJeor(profile);
}

function calculateTdee(profile) {
  if (Number.isFinite(profile.tdeeOverride) && profile.tdeeOverride > 0) {
    return Math.round(profile.tdeeOverride);
  }
  return Math.round(calculateBmr(profile) * getActivityMultiplier(profile.activityLevel));
}

function getProfileEnergy(profile = PROFILE) {
  const bmr = calculateBmr(profile);
  const tdee = calculateTdee(profile);
  const threshold = Math.round(tdee * 0.10);
  return { bmr, tdee, threshold };
}

const PROFILE_ENERGY = getProfileEnergy(PROFILE);
const BMR = PROFILE_ENERGY.bmr;
const TDEE = PROFILE_ENERGY.tdee;
const BALANCE_THRESHOLD = PROFILE_ENERGY.threshold;

function normalizeDailyEntry(entry, tdee = TDEE, fallbackIntake) {
  const hasLoggedIntake = Number.isFinite(entry.intake);
  const hasFallback = Number.isFinite(fallbackIntake);
  const intake = hasLoggedIntake ? entry.intake : hasFallback ? fallbackIntake : tdee;
  return {
    ...entry,
    intake,
    balance: Math.round(intake - tdee),
    tdee,
    intakeSource: hasLoggedIntake ? (entry.intakeSource || 'logged') : hasFallback ? 'assumed_today' : 'assumed_tdee',
  };
}

function toDateKey(value = new Date()) {
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
  if (typeof value === 'string' && /^[A-Z][a-z]{2}\s+\d{1,2}$/.test(value)) {
    const parsedMonthDay = new Date(`${value}, ${new Date().getFullYear()}`);
    if (!Number.isNaN(parsedMonthDay.getTime())) return toDateKey(parsedMonthDay);
  }
  const parsed = value instanceof Date ? value : new Date(value);
  const date = Number.isNaN(parsed.getTime()) ? new Date() : parsed;
  const local = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  const y = local.getFullYear();
  const m = String(local.getMonth() + 1).padStart(2, '0');
  const d = String(local.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
}

function dateFromKey(key) {
  if (typeof key === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(key)) {
    const [y, m, d] = key.split('-').map(Number);
    return new Date(y, m - 1, d);
  }
  const parsed = new Date(key);
  return Number.isNaN(parsed.getTime()) ? new Date() : parsed;
}

function dateMetaFromKey(key) {
  const date = dateFromKey(key);
  return {
    date: key,
    dateKey: key,
    dateObj: date,
    label: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),
    dow: date.toLocaleDateString('en-US', { weekday: 'short' }),
    d: date.getDate(),
  };
}

function addDaysToKey(key, days) {
  const date = dateFromKey(key);
  date.setDate(date.getDate() + days);
  return toDateKey(date);
}

function kgToLb(value) {
  return value * 2.2046226218;
}

function lbToKg(value) {
  return value / 2.2046226218;
}

function cmToIn(value) {
  return value / 2.54;
}

function inToCm(value) {
  return value * 2.54;
}

function displayWeightValue(weightKg, unitSystem = 'metric', digits = 1) {
  const value = unitSystem === 'imperial' ? kgToLb(weightKg) : weightKg;
  return Number(value.toFixed(digits));
}

function displayHeightValue(heightCm, unitSystem = 'metric', digits = 1) {
  const value = unitSystem === 'imperial' ? cmToIn(heightCm) : heightCm;
  return Number(value.toFixed(digits));
}

function weightUnit(unitSystem = 'metric') {
  return unitSystem === 'imperial' ? 'lb' : 'kg';
}

function heightUnit(unitSystem = 'metric') {
  return unitSystem === 'imperial' ? 'in' : 'cm';
}

function buildBalanceWindow(entries, windowDays = 3, tdee = TDEE) {
  const minDays = Math.max(3, Math.round(windowDays || 3));
  const slice = entries.slice(-minDays);
  const today = slice[slice.length - 1] || entries[entries.length - 1] || {};
  const todayIntake = Number.isFinite(today.intake) ? today.intake : tdee;
  const missingCount = Math.max(0, minDays - slice.length);
  const assumed = Array.from({ length: missingCount }, (_, i) => ({
    date: `assumed ${i + 1}`,
    dow: '',
    d: '',
    intake: todayIntake,
    intakeSource: 'assumed_today',
  }));
  return [...assumed, ...slice].map((entry) => normalizeDailyEntry(entry, tdee, todayIntake));
}

function getBalanceReading(entries, windowDays = 3, tdee = TDEE) {
  const days = buildBalanceWindow(entries, windowDays, tdee);
  const avg = days.reduce((sum, day) => sum + day.balance, 0) / days.length;
  const threshold = tdee * 0.10;
  const state = avg > threshold ? 'gaining' : avg < -threshold ? 'losing' : 'keeping';
  const actualLoggedDays = days.filter((day) => day.intakeSource === 'logged').length;
  const confidence = actualLoggedDays < 3 ? 'rough' : days.length >= 7 ? 'higher' : 'moderate';
  return {
    days,
    averageBalance: avg,
    state,
    confidence,
    threshold,
    projectedKgPerWeek: avg * 7 / 7700,
    assumed: days.some((day) => day.intakeSource !== 'logged'),
  };
}

function displayDate(value) {
  if (typeof value === 'string') {
    if (/^[A-Z][a-z]{2}\\s+\\d{1,2}$/.test(value)) return value;
    const parsed = new Date(value);
    if (!Number.isNaN(parsed.getTime())) {
      return parsed.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
    }
    return value;
  }
  if (value instanceof Date && !Number.isNaN(value.getTime())) {
    return value.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
  }
  return String(value || '');
}

// Each scenario has a profile (mean kcal/day + day-to-day variance).
// getScenarioDays(key, count) returns `count` days ending today, oldest first.
const SCENARIO_PROFILES = {
  keeping:      { label: 'Holding steady', mean:   60, variance: 280 },
  gaining:      { label: 'Surplus stack',  mean:  580, variance: 260 },
  losing:       { label: 'Cut',            mean: -540, variance: 240 },
  rollercoaster:{ label: 'Swings',         mean:   80, variance: 760 },
};

// Stable seeded RNG so the same (key, count) always produces the same days.
function _mulberry(seed) {
  let a = seed | 0;
  return function () {
    a |= 0; a = a + 0x6D2B79F5 | 0;
    let t = a;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
function _scenarioSeed(key) {
  let h = 5381;
  for (let i = 0; i < key.length; i++) h = (h * 33) ^ key.charCodeAt(i);
  return h >>> 0;
}

function getScenarioDays(key, count) {
  const profile = SCENARIO_PROFILES[key] || SCENARIO_PROFILES.keeping;
  const rand = _mulberry(_scenarioSeed(key));
  const today = new Date(2026, 4, 16); // May 16
  // Generate `count` days ending today, oldest first.
  const days = [];
  for (let i = count - 1; i >= 0; i--) {
    const date = new Date(today.getTime() - i * 86400000);
    const dow = date.getDay();
    const weekend = (dow === 5 || dow === 6) ? 180 : 0;
    const variance = (rand() - 0.5) * 2 * profile.variance;
    const balance = Math.round(profile.mean + variance + weekend);
    days.push(normalizeDailyEntry({
      date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),
      dow:  date.toLocaleDateString('en-US', { weekday: 'short' }),
      d:    date.getDate(),
      intake:  TDEE + balance,
      intakeSource: 'logged',
    }));
  }
  return days;
}

// Pre-built "default" scenarios (3 days) for back-compat where callers expect .days
const SCENARIOS = Object.fromEntries(
  Object.entries(SCENARIO_PROFILES).map(([k, p]) => [k, { label: p.label, days: getScenarioDays(k, 3) }])
);

// Today's meal log for the active scenario. Macros in grams.
const TODAY_MEALS = [
  { time: '07:24', slot: 'breakfast', name: 'Greek yogurt, honey, walnuts',   kcal: 380, p: 22, c: 38, f: 14 },
  { time: '09:50', slot: 'breakfast', name: 'Flat white',                      kcal: 110, p: 6,  c: 10, f: 5  },
  { time: '13:10', slot: 'lunch',     name: 'Chicken bowl, brown rice, kimchi',kcal: 720, p: 48, c: 78, f: 18 },
  { time: '16:30', slot: 'snack',     name: 'Apple + almonds',                 kcal: 240, p: 6,  c: 28, f: 14 },
  { time: '19:45', slot: 'dinner',    name: 'Pasta, tomato, parmesan',         kcal: 560, p: 22, c: 84, f: 16 },
];

// Library of foods (quick-add)
const FOOD_LIBRARY = [
  { name: 'Eggs, two scrambled',     kcal: 220, p: 14, c: 2,  f: 16, tag: 'breakfast' },
  { name: 'Avocado toast',           kcal: 320, p: 9,  c: 30, f: 18, tag: 'breakfast' },
  { name: 'Oat porridge, banana',    kcal: 380, p: 12, c: 64, f: 8,  tag: 'breakfast' },
  { name: 'Chicken Caesar wrap',     kcal: 560, p: 32, c: 48, f: 22, tag: 'lunch' },
  { name: 'Tuna poke bowl',          kcal: 620, p: 36, c: 70, f: 16, tag: 'lunch' },
  { name: 'Cold-pressed juice',      kcal: 140, p: 2,  c: 32, f: 0,  tag: 'snack' },
  { name: 'Dark chocolate, square',  kcal: 60,  p: 1,  c: 6,  f: 4,  tag: 'snack' },
  { name: 'Salmon, asparagus, rice', kcal: 640, p: 42, c: 60, f: 18, tag: 'dinner' },
];

// Generate 28 days of mock history balances (recent → past).
// Days are weighted around the current scenario for visual coherence but
// have realistic day-to-day variance.
function generateHistory(scenarioKey = 'keeping') {
  const seed = { keeping: 60, gaining: 500, losing: -480, rollercoaster: 80 }[scenarioKey] ?? 0;
  // simple seeded rng
  let s = 1234;
  const rand = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
  const days = [];
  const today = new Date(2026, 4, 16); // May 16
  for (let i = 27; i >= 0; i--) {
    const date = new Date(today.getTime() - i * 86400000);
    const variance = (rand() - 0.5) * 700;
    const dayOfWeek = date.getDay();
    const weekend = (dayOfWeek === 5 || dayOfWeek === 6) ? 220 : 0;
    const balance = Math.round(seed + variance + weekend);
    days.push(normalizeDailyEntry({
      date,
      label: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),
      dow: date.toLocaleDateString('en-US', { weekday: 'short' }),
      d: date.getDate(),
      intake: TDEE + balance,
      intakeSource: 'logged',
    }));
  }
  return days;
}

const HISTORY = generateHistory('keeping');

Object.assign(window, {
  PROFILE,
  PROFILE_ENERGY,
  BMR,
  TDEE,
  BALANCE_THRESHOLD,
  ACTIVITY_MULTIPLIERS,
  SCENARIO_PROFILES,
  SCENARIOS,
  TODAY_MEALS,
  FOOD_LIBRARY,
  HISTORY,
  calculateMifflinStJeor,
  calculateKatchMcArdle,
  getActivityMultiplier,
  calculateBmr,
  calculateTdee,
  getProfileEnergy,
  normalizeDailyEntry,
  buildBalanceWindow,
  getBalanceReading,
  displayDate,
  toDateKey,
  dateFromKey,
  dateMetaFromKey,
  addDaysToKey,
  kgToLb,
  lbToKg,
  cmToIn,
  inToCm,
  displayWeightValue,
  displayHeightValue,
  weightUnit,
  heightUnit,
  generateHistory,
  getScenarioDays,
});
