// data.jsx — realistic demo data for a beta-stage (hundreds of users) Quebec food-waste app
// All numbers are plausible placeholders. Deterministic so charts are stable.

// ── tiny seeded PRNG for stable "random" series ──────────────────────
function seeded(seed) {
  let s = seed % 2147483647;
  if (s <= 0) s += 2147483646;
  return () => (s = (s * 16807) % 2147483647) / 2147483647;
}

// month labels for last 12 months ending May 2026
const MONTHS_FR = ['juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc', 'janv', 'févr', 'mars', 'avr', 'mai'];
const MONTHS_EN = ['Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May'];

// ── headline KPIs ────────────────────────────────────────────────────
const ADMIN_KPIS = {
  users: { value: 842, delta: 14.2, spark: [410, 452, 498, 540, 587, 628, 664, 702, 737, 776, 808, 842] },
  mau: { value: 531, delta: 9.6, spark: [248, 276, 305, 330, 360, 388, 410, 438, 462, 488, 506, 531] },
  dau: { value: 187, delta: 6.1, spark: [92, 101, 110, 118, 126, 134, 141, 152, 160, 168, 176, 187] },
  households: { value: 408, delta: 11.4, spark: [196, 218, 240, 262, 286, 308, 326, 348, 366, 384, 396, 408] },
  items: { value: 6214, delta: 18.3, spark: [2980, 3340, 3680, 4010, 4360, 4700, 4980, 5320, 5610, 5860, 6040, 6214] },
  cooked: { value: 3821, delta: 12.9, spark: [1620, 1840, 2060, 2280, 2510, 2740, 2940, 3160, 3360, 3540, 3690, 3821] },
  scans: { value: 14860, delta: 21.7, spark: [6100, 7000, 7900, 8800, 9700, 10600, 11400, 12200, 13000, 13700, 14300, 14860] },
  savedKg: { value: 1284, delta: 16.4, spark: [540, 620, 700, 780, 866, 948, 1020, 1098, 1162, 1212, 1252, 1284] },
  savedMoney: { value: 9120, delta: 17.1, spark: [3820, 4380, 4960, 5520, 6120, 6700, 7220, 7760, 8240, 8620, 8900, 9120] },
  mrrProjected: { value: 0, projected: 1640, delta: 0, spark: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] },
  margin: { value: 71, delta: 0 },
  retention30: { value: 47, delta: 3.2 },
};

// ── overview: platform activity (area, 30 days) ──────────────────────
function genDaily(seed, base, drift, noise) {
  const r = seeded(seed); const out = [];
  for (let i = 0; i < 30; i++) {
    const weekend = (i % 7 === 5 || i % 7 === 6) ? 1.18 : 1;
    out.push(Math.round((base + drift * i + (r() - 0.5) * noise) * weekend));
  }
  return out;
}
const ACTIVITY_30D = {
  sessions: genDaily(7, 240, 3.2, 70),
  scans: genDaily(11, 78, 1.1, 28),
  cooked: genDaily(13, 36, 0.5, 16),
};

// activation funnel
const FUNNEL = [
  { key: 'signup', label: { fr: 'Inscription', en: 'Signed up' }, value: 842, pct: 100 },
  { key: 'onboard', label: { fr: 'Onboarding terminé', en: 'Finished onboarding' }, value: 731, pct: 87 },
  { key: 'first_item', label: { fr: 'Premier aliment ajouté', en: 'Added first item' }, value: 624, pct: 74 },
  { key: 'first_scan', label: { fr: 'Premier scan IA', en: 'First AI scan' }, value: 472, pct: 56 },
  { key: 'first_cook', label: { fr: 'Première recette cuisinée', en: 'First recipe cooked' }, value: 388, pct: 46 },
  { key: 'retained', label: { fr: 'Actif à 30 jours', en: 'Active at 30 days' }, value: 281, pct: 33 },
];

const INVENTORY_SPLIT = [
  { key: 'fridge', label: { fr: 'Frigo', en: 'Fridge' }, value: 3120, color: 'var(--chart-1)' },
  { key: 'pantry', label: { fr: 'Garde-manger', en: 'Pantry' }, value: 2080, color: 'var(--chart-2)' },
  { key: 'freezer', label: { fr: 'Congélateur', en: 'Freezer' }, value: 1014, color: 'var(--chart-3)' },
];

// ── users page ───────────────────────────────────────────────────────
// cohort retention: rows = signup month, cols = week 0..7 (% retained)
const COHORTS = [
  { label: { fr: 'Déc', en: 'Dec' }, size: 88, vals: [100, 62, 51, 44, 39, 36, 33, 31] },
  { label: { fr: 'Janv', en: 'Jan' }, size: 102, vals: [100, 64, 53, 46, 41, 37, 34, null] },
  { label: { fr: 'Févr', en: 'Feb' }, size: 119, vals: [100, 66, 55, 48, 43, 39, null, null] },
  { label: { fr: 'Mars', en: 'Mar' }, size: 134, vals: [100, 68, 57, 50, 45, null, null, null] },
  { label: { fr: 'Avr', en: 'Apr' }, size: 141, vals: [100, 70, 59, 52, null, null, null, null] },
  { label: { fr: 'Mai', en: 'May' }, size: 156, vals: [100, 72, 61, null, null, null, null, null] },
];
const CHURN_TREND = [6.8, 6.4, 6.1, 5.9, 5.5, 5.2, 4.9, 4.7, 4.4, 4.2, 4.0, 3.8];
const ACQ_CHANNELS = [
  { key: 'organic', label: { fr: 'Bouche-à-oreille', en: 'Word of mouth' }, value: 312, color: 'var(--chart-1)' },
  { key: 'social', label: { fr: 'Réseaux sociaux', en: 'Social' }, value: 224, color: 'var(--chart-2)' },
  { key: 'search', label: { fr: 'Recherche', en: 'Search' }, value: 158, color: 'var(--chart-3)' },
  { key: 'referral', label: { fr: 'Parrainage', en: 'Referral' }, value: 96, color: 'var(--chart-4)' },
  { key: 'press', label: { fr: 'Presse / blogues', en: 'Press / blogs' }, value: 52, color: 'var(--chart-5)' },
];
const PLATFORM_SPLIT = [
  { key: 'ios', label: { fr: 'iOS (PWA)', en: 'iOS (PWA)' }, value: 461, color: 'var(--chart-1)' },
  { key: 'android', label: { fr: 'Android (PWA)', en: 'Android (PWA)' }, value: 308, color: 'var(--chart-2)' },
  { key: 'web', label: { fr: 'Web desktop', en: 'Web desktop' }, value: 73, color: 'var(--chart-3)' },
];
const DAU_MAU = [32, 33, 34, 33, 35, 34, 36, 35, 35, 36, 35, 35.2]; // %

// sample user table
const REGIONS = ['Montréal', 'Québec', 'Laval', 'Gatineau', 'Sherbrooke', 'Longueuil', 'Trois-Rivières', 'Ottawa', 'Toronto', 'Paris'];
const FIRST = ['Alex', 'Camille', 'Sam', 'Léa', 'Noah', 'Maya', 'Liam', 'Olivia', 'Émile', 'Rose', 'Jules', 'Florence', 'Thomas', 'Sophie', 'Marc', 'Élise', 'Hugo', 'Charlotte', 'Antoine', 'Béatrice'];
const LAST = ['Tremblay', 'Gagnon', 'Roy', 'Côté', 'Bouchard', 'Gauthier', 'Morin', 'Lavoie', 'Fortin', 'Bélanger', 'Pelletier', 'Lévesque', 'Bergeron', 'Leblanc', 'Paquette'];
function genUsers(n) {
  const r = seeded(99); const out = [];
  const plans = ['free', 'free', 'free', 'free', 'trial', 'premium'];
  const statuses = ['active', 'active', 'active', 'dormant', 'new', 'churned'];
  for (let i = 0; i < n; i++) {
    const f = FIRST[Math.floor(r() * FIRST.length)];
    const l = LAST[Math.floor(r() * LAST.length)];
    const seen = Math.floor(r() * 72);
    out.push({
      id: 'u' + (1000 + i),
      name: f + ' ' + l,
      email: (f + '.' + l).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '') + '@' + ['gmail.com', 'icloud.com', 'outlook.com', 'videotron.ca'][Math.floor(r() * 4)],
      region: REGIONS[Math.floor(r() * REGIONS.length)],
      plan: plans[Math.floor(r() * plans.length)],
      status: statuses[Math.floor(r() * statuses.length)],
      joinedDaysAgo: Math.floor(r() * 180),
      seenHrsAgo: seen,
      items: Math.floor(r() * 38) + 2,
      household: Math.floor(r() * 5) + 1,
      cooked: Math.floor(r() * 42),
      scans: Math.floor(r() * 120),
    });
  }
  return out;
}
const USERS = genUsers(36);

// ── households ───────────────────────────────────────────────────────
const HH_SIZE_DIST = [
  { size: 1, count: 142 }, { size: 2, count: 134 }, { size: 3, count: 78 }, { size: 4, count: 38 }, { size: 5, count: 12 }, { size: 6, count: 4 },
];
const HH_STATS = { total: 408, shared: 246, avgSize: 2.4, invites: 389, inviteAccept: 71 };
const HH_COLLAB = [ // weekly shared actions
  { key: 'shared_list', label: { fr: 'Liste partagée', en: 'Shared list' }, value: 1840 },
  { key: 'meal_votes', label: { fr: 'Votes de repas', en: 'Meal votes' }, value: 962 },
  { key: 'assigned_cook', label: { fr: 'Qui cuisine', en: 'Who cooks' }, value: 734 },
  { key: 'shared_inv', label: { fr: 'Inventaire partagé', en: 'Shared inventory' }, value: 2210 },
];

// ── engagement ───────────────────────────────────────────────────────
const FEATURE_ADOPTION = [
  { key: 'en_recipes', value: 82 },
  { key: 'en_scan_photo', value: 71 },
  { key: 'en_grocery', value: 68 },
  { key: 'en_scan_barcode', value: 59 },
  { key: 'en_calendar', value: 44 },
  { key: 'en_favorites', value: 38 },
  { key: 'en_leftovers', value: 26 },
  { key: 'en_privacy', value: 19 },
];
const SESSIONS_WEEKDAY = [
  { d: { fr: 'Lun', en: 'Mon' }, v: 268 }, { d: { fr: 'Mar', en: 'Tue' }, v: 252 },
  { d: { fr: 'Mer', en: 'Wed' }, v: 274 }, { d: { fr: 'Jeu', en: 'Thu' }, v: 261 },
  { d: { fr: 'Ven', en: 'Fri' }, v: 298 }, { d: { fr: 'Sam', en: 'Sat' }, v: 342 },
  { d: { fr: 'Dim', en: 'Sun' }, v: 388 },
];
const SCANS_DAILY = {
  photo: genDaily(21, 42, 0.6, 14),
  barcode: genDaily(23, 64, 0.9, 22),
};
const PRIVACY_SPLIT = { private: 162, shared: 246 }; // households keeping food data private vs shared
const STICKINESS = 35.2; // DAU/MAU %
const NOTIF_ENABLED = 64; // %

// ── content (recipes & food) ─────────────────────────────────────────
const RECIPE_TYPES = [
  { key: 'type_dinner', value: 1486, color: 'var(--chart-1)' },
  { key: 'type_lunch', value: 982, color: 'var(--chart-2)' },
  { key: 'type_breakfast', value: 724, color: 'var(--chart-3)' },
  { key: 'type_snack', value: 412, color: 'var(--chart-4)' },
  { key: 'type_dessert', value: 217, color: 'var(--chart-5)' },
];
const TOP_RECIPES = [
  { emoji: '🍝', name: { fr: 'Pâtes crémeuses aux tomates', en: 'Creamy tomato pasta' }, value: 482 },
  { emoji: '🥑', name: { fr: 'Avocado toast aux épinards', en: 'Avocado toast' }, value: 396 },
  { emoji: '🍳', name: { fr: 'Omelette grecque', en: 'Greek omelette' }, value: 341 },
  { emoji: '🍗', name: { fr: 'Bol poulet & légumes', en: 'Chicken & veg bowl' }, value: 318 },
  { emoji: '🥪', name: { fr: 'Sandwich grillé au fromage', en: 'Grilled cheese' }, value: 274 },
  { emoji: '🥣', name: { fr: 'Yogourt aux fruits', en: 'Yogurt parfait' }, value: 226 },
];
const TOP_FOODS = [
  { emoji: '🥛', name: { fr: 'Lait', en: 'Milk' }, value: 612 },
  { emoji: '🥚', name: { fr: 'Œufs', en: 'Eggs' }, value: 588 },
  { emoji: '🍞', name: { fr: 'Pain', en: 'Bread' }, value: 521 },
  { emoji: '🧀', name: { fr: 'Fromage', en: 'Cheese' }, value: 468 },
  { emoji: '🍅', name: { fr: 'Tomates', en: 'Tomatoes' }, value: 412 },
  { emoji: '🥬', name: { fr: 'Épinards', en: 'Spinach' }, value: 356 },
  { emoji: '🍗', name: { fr: 'Poulet', en: 'Chicken' }, value: 334 },
  { emoji: '🍌', name: { fr: 'Bananes', en: 'Bananas' }, value: 298 },
];
const MOST_SAVED = [
  { emoji: '🥬', name: { fr: 'Épinards', en: 'Spinach' }, value: 184 },
  { emoji: '🍌', name: { fr: 'Bananes', en: 'Bananas' }, value: 162 },
  { emoji: '🥛', name: { fr: 'Lait', en: 'Milk' }, value: 148 },
  { emoji: '🍓', name: { fr: 'Fraises', en: 'Strawberries' }, value: 121 },
  { emoji: '🥑', name: { fr: 'Avocats', en: 'Avocados' }, value: 98 },
];
const MOST_FAV = [
  { emoji: '🍝', name: { fr: 'Pâtes crémeuses aux tomates', en: 'Creamy tomato pasta' }, value: 268 },
  { emoji: '🍗', name: { fr: 'Bol poulet & légumes', en: 'Chicken & veg bowl' }, value: 214 },
  { emoji: '🥑', name: { fr: 'Avocado toast', en: 'Avocado toast' }, value: 187 },
  { emoji: '🍳', name: { fr: 'Omelette grecque', en: 'Greek omelette' }, value: 156 },
];

// ── impact (anti-waste) ──────────────────────────────────────────────
const IMPACT = {
  kg: 1284, money: 9120, co2: 3210, // co2 kg eq
  perUserKg: 2.4, perUserMoney: 17.2,
  trend: ACTIVITY_30D, // reuse shape; we build a monthly trend below
  monthlyKg: [62, 71, 80, 90, 102, 112, 121, 130, 138, 144, 149, 153],
  byCat: [
    { key: 'cat_produce', value: 486, color: 'var(--chart-1)' },
    { key: 'cat_dairy', value: 318, color: 'var(--chart-2)' },
    { key: 'cat_bakery', value: 224, color: 'var(--chart-3)' },
    { key: 'cat_meat', value: 158, color: 'var(--chart-4)' },
    { key: 'cat_pantry', value: 98, color: 'var(--chart-5)' },
  ],
  leaderboard: [
    { name: 'Foyer Tremblay', kg: 38.4, money: 274 },
    { name: 'Foyer Gagnon', kg: 31.2, money: 221 },
    { name: 'Foyer Côté', kg: 28.7, money: 198 },
    { name: 'Foyer Roy', kg: 24.1, money: 171 },
    { name: 'Foyer Lavoie', kg: 21.6, money: 154 },
  ],
};

// ── revenue (not live yet — projected) ───────────────────────────────
const REVENUE = {
  currentMRR: 0,
  projectedMRR: 1640,
  projectedARR: 19680,
  payingProjected: 71,
  arpu: 4.62,
  conversion: 8.4, // % free → paid (projected)
  ltv: 58,
  revChurn: 4.1,
  pricing: [
    { tier: 're_tier_free', price: 0, period: 'perMonth', users: 771, color: 'var(--ink-3)',
      feats: { fr: ['Inventaire illimité', '10 scans IA / mois', '1 personne'], en: ['Unlimited inventory', '10 AI scans / mo', '1 person'] } },
    { tier: 're_tier_premium', price: 4.99, period: 'perMonth', users: 52, color: 'var(--accent)', highlight: true,
      feats: { fr: ['Scans IA illimités', 'Plan de repas', 'Spéciaux d’épicerie'], en: ['Unlimited AI scans', 'Meal planning', 'Grocery deals'] } },
    { tier: 're_tier_family', price: 8.99, period: 'perMonth', users: 19, color: 'var(--accent-2)',
      feats: { fr: ['Tout Premium', 'Jusqu’à 6 membres', 'Partage avancé'], en: ['Everything in Premium', 'Up to 6 members', 'Advanced sharing'] } },
  ],
  scanPricePerScan: 0.15,
  scanRevenueProjected: 420,
  streams: [
    { key: 're_str_sub', value: 980, color: 'var(--chart-1)' },
    { key: 're_str_scan', value: 420, color: 'var(--chart-2)' },
    { key: 're_str_partner', value: 240, color: 'var(--chart-3)' },
  ],
  // projected MRR ramp over next 12 months
  mrrProjection: [0, 180, 360, 540, 720, 920, 1120, 1320, 1540, 1640, 1880, 2140],
};

// ── costs & infra ────────────────────────────────────────────────────
const COSTS = {
  totalMonthly: 487,
  perUser: 0.92,
  perScan: 0.018,
  marginProjected: 71, // % at projected revenue
  burn: 487,
  breakdown: [
    { key: 'cost_claude', value: 268, color: 'var(--chart-1)', note: { fr: '14 860 scans · vision', en: '14,860 scans · vision' } },
    { key: 'cost_cf_workers', value: 64, color: 'var(--chart-2)', note: { fr: '8.2 M requêtes', en: '8.2M requests' } },
    { key: 'cost_cf_r2', value: 48, color: 'var(--chart-3)', note: { fr: '212 Go photos', en: '212 GB photos' } },
    { key: 'cost_cf_d1', value: 31, color: 'var(--chart-4)', note: { fr: '1.4 M lignes', en: '1.4M rows' } },
    { key: 'cost_cf_pages', value: 22, color: 'var(--chart-5)', note: { fr: 'hébergement PWA', en: 'PWA hosting' } },
    { key: 'cost_cf_kv', value: 18, color: 'var(--chart-2)', note: { fr: 'sessions / cache', en: 'sessions / cache' } },
    { key: 'cost_email', value: 21, color: 'var(--chart-3)', note: { fr: '~9 200 courriels', en: '~9,200 emails' } },
    { key: 'cost_domain', value: 15, color: 'var(--chart-4)', note: { fr: 'pantry-wise.com', en: 'pantry-wise.com' } },
  ],
  trend: [210, 244, 278, 308, 342, 372, 398, 422, 446, 462, 476, 487],
  apiUsage: [
    { api: 'Claude Vision — scan photo', calls: '5 600', cost: 184, unit: '$0.0328' },
    { api: 'Claude — suggestions recettes', calls: '3 240', cost: 58, unit: '$0.0179' },
    { api: 'Claude Vision — code-barres', calls: '9 260', cost: 26, unit: '$0.0028' },
    { api: 'Cloudflare Workers AI', calls: '1.1 M', cost: 14, unit: '$0.000013' },
  ],
  // projected revenue vs costs (12 mo)
  revVsCost_rev: REVENUE.mrrProjection,
  revVsCost_cost: [210, 244, 278, 308, 342, 372, 398, 422, 446, 462, 476, 487],
};

// ── geography ────────────────────────────────────────────────────────
const GEO_REGIONS = [
  { region: 'Québec', users: 612, pct: 72.7, color: 'var(--chart-1)' },
  { region: 'Ontario', users: 118, pct: 14.0, color: 'var(--chart-2)' },
  { region: 'Nouveau-Brunswick', users: 38, pct: 4.5, color: 'var(--chart-3)' },
  { region: 'Colombie-Britannique', users: 27, pct: 3.2, color: 'var(--chart-4)' },
  { region: 'France', users: 31, pct: 3.7, color: 'var(--chart-5)' },
  { region: 'Autres', users: 16, pct: 1.9, color: 'var(--ink-3)' },
];
const GEO_CITIES = [
  { city: 'Montréal', users: 318, lat: 60, lng: 38 },
  { city: 'Québec', users: 142, lat: 44, lng: 58 },
  { city: 'Laval', users: 64, lat: 56, lng: 36 },
  { city: 'Gatineau', users: 48, lat: 70, lng: 18 },
  { city: 'Sherbrooke', users: 41, lat: 52, lng: 64 },
  { city: 'Longueuil', users: 37, lat: 62, lng: 42 },
  { city: 'Trois-Rivières', users: 28, lat: 50, lng: 50 },
  { city: 'Ottawa', users: 33, lat: 72, lng: 20 },
];

// ── system & bugs ────────────────────────────────────────────────────
const SYSTEM = {
  uptime: 99.94,
  latencyP95: 218, // ms
  errorRate: 0.42, // %
  crashFree: 99.7,
  openBugs: 11,
  latency24h: [196, 188, 182, 190, 205, 224, 248, 262, 241, 228, 216, 209, 214, 232, 256, 271, 258, 242, 230, 221, 214, 208, 202, 218],
  errorsByType: [
    { type: { fr: 'Timeout API IA', en: 'AI API timeout' }, value: 38, color: 'var(--chart-1)' },
    { type: { fr: 'Échec upload photo', en: 'Photo upload fail' }, value: 24, color: 'var(--chart-2)' },
    { type: { fr: 'Sync hors-ligne', en: 'Offline sync' }, value: 19, color: 'var(--chart-3)' },
    { type: { fr: 'Auth / session', en: 'Auth / session' }, value: 12, color: 'var(--chart-4)' },
    { type: { fr: 'Autre', en: 'Other' }, value: 9, color: 'var(--chart-5)' },
  ],
  bugs: [
    { id: 'PW-241', title: { fr: 'Scan IA expire sur photos > 8 Mo', en: 'AI scan times out on photos > 8MB' }, sev: 'critical', platform: 'iOS', status: 'st_progress', daysAgo: 1 },
    { id: 'PW-238', title: { fr: 'Dates d’expiration décalées d’un jour (fuseau)', en: 'Expiry dates off by one day (timezone)' }, sev: 'high', platform: 'Android', status: 'st_open', daysAgo: 2 },
    { id: 'PW-235', title: { fr: 'Liste d’épicerie ne sync pas entre membres', en: 'Grocery list not syncing between members' }, sev: 'high', platform: 'Web', status: 'st_progress', daysAgo: 3 },
    { id: 'PW-230', title: { fr: 'Code-barres non reconnu (produits IGA)', en: 'Barcode not recognized (IGA products)' }, sev: 'medium', platform: 'iOS', status: 'st_open', daysAgo: 4 },
    { id: 'PW-228', title: { fr: 'Notification d’expiration en double', en: 'Duplicate expiry notification' }, sev: 'medium', platform: 'Android', status: 'st_resolved', daysAgo: 6 },
    { id: 'PW-224', title: { fr: 'Crash à l’ajout d’un 7e membre au foyer', en: 'Crash when adding 7th household member' }, sev: 'low', platform: 'iOS', status: 'st_resolved', daysAgo: 8 },
    { id: 'PW-221', title: { fr: 'Switch FR/EN ne garde pas la page', en: 'FR/EN switch loses current page' }, sev: 'low', platform: 'Web', status: 'st_open', daysAgo: 9 },
  ],
};

const RECENT_SIGNUPS = USERS.slice(0, 6).map((u, i) => ({ ...u, joinedDaysAgo: i }));

Object.assign(window, {
  MONTHS_FR, MONTHS_EN, ADMIN_KPIS, ACTIVITY_30D, FUNNEL, INVENTORY_SPLIT,
  COHORTS, CHURN_TREND, ACQ_CHANNELS, PLATFORM_SPLIT, DAU_MAU, USERS, RECENT_SIGNUPS,
  HH_SIZE_DIST, HH_STATS, HH_COLLAB,
  FEATURE_ADOPTION, SESSIONS_WEEKDAY, SCANS_DAILY, PRIVACY_SPLIT, STICKINESS, NOTIF_ENABLED,
  RECIPE_TYPES, TOP_RECIPES, TOP_FOODS, MOST_SAVED, MOST_FAV,
  IMPACT, REVENUE, COSTS, GEO_REGIONS, GEO_CITIES, SYSTEM,
});
