// Saladizer Web — shared components, data, and i18n.

// ── Sample data ────────────────────────────────────────────
const CATEGORIES = ['Greens', 'Vegetables', 'Protein', 'Toppings', 'Dressing'];

// Nutrition is USDA per-100g (calories, protein, carbs, fat) so the
// builder's computeTotals can sum them honestly instead of estimating
// carbs/fat as a fraction of calories.
const INGREDIENTS = {
  Greens: [
    { id: 'spinach',   name: 'Spinach',          ar: 'سبانخ',         calories: 23,  protein: 2.9, carbs: 3.6,  fat: 0.4,  glyph: '🥬' },
    { id: 'romaine',   name: 'Romaine lettuce',  ar: 'خس روماني',     calories: 17,  protein: 1.2, carbs: 3.3,  fat: 0.3,  glyph: '🥗' },
    { id: 'kale',      name: 'Kale',             ar: 'كرنب',          calories: 33,  protein: 2.9, carbs: 6.7,  fat: 0.4,  glyph: '🌿' },
    { id: 'arugula',   name: 'Arugula',          ar: 'جرجير',         calories: 25,  protein: 2.6, carbs: 3.7,  fat: 0.7,  glyph: '🌱' },
  ],
  Vegetables: [
    { id: 'tomato',    name: 'Cherry tomatoes',  ar: 'طماطم كرزية',   calories: 18,  protein: 0.9, carbs: 3.9,  fat: 0.2,  glyph: '🍅' },
    { id: 'cucumber',  name: 'Cucumber',         ar: 'خيار',          calories: 16,  protein: 0.7, carbs: 3.6,  fat: 0.1,  glyph: '🥒' },
    { id: 'onion',     name: 'Red onion',        ar: 'بصل أحمر',      calories: 40,  protein: 1.1, carbs: 9.3,  fat: 0.1,  glyph: '🧅' },
    { id: 'pepper',    name: 'Bell pepper',      ar: 'فلفل رومي',     calories: 31,  protein: 1.0, carbs: 6.0,  fat: 0.3,  glyph: '🫑' },
    { id: 'avocado',   name: 'Avocado',          ar: 'أفوكادو',       calories: 160, protein: 2.0, carbs: 8.5,  fat: 14.7, glyph: '🥑' },
    { id: 'carrot',    name: 'Carrots',          ar: 'جزر',           calories: 41,  protein: 0.9, carbs: 9.6,  fat: 0.2,  glyph: '🥕' },
  ],
  Protein: [
    { id: 'chicken',   name: 'Grilled chicken',  ar: 'دجاج مشوي',     calories: 165, protein: 31,  carbs: 0,    fat: 3.6,  glyph: '🍗' },
    { id: 'halloumi',  name: 'Halloumi',         ar: 'حلومي',         calories: 316, protein: 21.4, carbs: 2.2, fat: 25.0, glyph: '🧀', custom: true },
    { id: 'chickpeas', name: 'Chickpeas',        ar: 'حمص',           calories: 164, protein: 8.9, carbs: 27.4, fat: 2.6,  glyph: '🫘' },
    { id: 'salmon',    name: 'Smoked salmon',    ar: 'سلمون مدخن',    calories: 117, protein: 18.3, carbs: 0,   fat: 4.3,  glyph: '🐟' },
    { id: 'tofu',      name: 'Tofu',             ar: 'توفو',          calories: 76,  protein: 8.0, carbs: 1.9,  fat: 4.8,  glyph: '⬜' },
    { id: 'egg',       name: 'Boiled egg',       ar: 'بيض مسلوق',     calories: 78,  protein: 6.3, carbs: 0.6,  fat: 5.3,  glyph: '🥚' },
  ],
  Toppings: [
    { id: 'feta',      name: 'Feta',             ar: 'فيتا',          calories: 264, protein: 14.2, carbs: 4.1, fat: 21.3, glyph: '🧀' },
    { id: 'almonds',   name: 'Toasted almonds',  ar: 'لوز محمص',      calories: 579, protein: 21.2, carbs: 21.6, fat: 49.9, glyph: '🥜' },
    { id: 'sumac',     name: 'Sumac',            ar: 'سمّاق',          calories: 5,   protein: 0.1, carbs: 1.0,  fat: 0.1,  glyph: '✨', custom: true },
    { id: 'olives',    name: 'Kalamata olives',  ar: 'زيتون كلاماتا', calories: 115, protein: 0.8, carbs: 6.3,  fat: 10.7, glyph: '🫒' },
    { id: 'pomegranate', name: 'Pomegranate',    ar: 'رمان',          calories: 83,  protein: 1.7, carbs: 18.7, fat: 1.2,  glyph: '🍎' },
  ],
  Dressing: [
    { id: 'olive-oil', name: 'Olive oil & lemon', ar: 'زيت وليمون',    calories: 90,  protein: 0,   carbs: 0.5,  fat: 10.0, glyph: '🫒' },
    { id: 'tahini',    name: 'Tahini',            ar: 'طحينة',         calories: 89,  protein: 2.5, carbs: 3.2,  fat: 8.1,  glyph: '🥣' },
    { id: 'balsamic',  name: 'Balsamic vinaigrette', ar: 'بلسميك',     calories: 60,  protein: 0.1, carbs: 14.0, fat: 0.4,  glyph: '🧄' },
    { id: 'yogurt',    name: 'Yogurt & herbs',    ar: 'زبادي بالأعشاب', calories: 50,  protein: 3.5, carbs: 4.0,  fat: 3.3,  glyph: '🥛' },
  ],
};

const ALL_INGREDIENTS = Object.entries(INGREDIENTS).flatMap(([cat, list]) =>
  list.map(i => ({ ...i, category: cat }))
);

const PRESETS = [
  { id: 'med',     name: 'Mediterranean bowl', ar: 'الطبق المتوسطي',  calories: 412, protein: 22, time: '8 min', tag: 'Salad of the day', image: true },
  { id: 'caesar',  name: 'Caesar crunch',      ar: 'سيزر مقرمش',      calories: 380, protein: 28, time: '6 min', tag: null,                image: false, hue: '#43A047' },
  { id: 'cobb',    name: 'Cobb classic',       ar: 'كوب كلاسيك',      calories: 540, protein: 34, time: '10 min', tag: 'High protein',     image: false, hue: '#1B5E20' },
  { id: 'asian',   name: 'Sesame ginger',      ar: 'سمسم بالزنجبيل',  calories: 320, protein: 14, time: '7 min', tag: 'Vegan',             image: false, hue: '#66BB6A' },
  { id: 'kale',    name: 'Kale & quinoa',      ar: 'كرنب وكينوا',     calories: 290, protein: 12, time: '9 min', tag: null,                image: false, hue: '#2D7A3E' },
  { id: 'beet',    name: 'Beet & goat cheese', ar: 'شمندر وجبن ماعز', calories: 360, protein: 11, time: '11 min', tag: null,               image: false, hue: '#7B1FA2' },
  { id: 'levant',  name: 'Levantine fattoush', ar: 'فتوش شامي',       calories: 280, protein: 6,  time: '6 min', tag: 'Local',             image: false, hue: '#F5A623' },
  { id: 'thai',    name: 'Thai mango crunch',  ar: 'مانجو تايلندي',   calories: 340, protein: 9,  time: '8 min', tag: 'Vegan',             image: false, hue: '#FF8A65' },
];

const FILTERS = [
  { id: 'high-protein', icon: 'zap',     label: 'High protein', ar: 'بروتين عالٍ' },
  { id: 'vegan',        icon: 'feather', label: 'Vegan',        ar: 'نباتي' },
  { id: 'keto',         icon: 'heart',   label: 'Keto',         ar: 'كيتو' },
  { id: 'quick',        icon: 'clock',   label: 'Quick prep',   ar: 'تحضير سريع' },
  { id: 'local',        icon: 'map-pin', label: 'Saudi & Arab', ar: 'سعودي وعربي' },
  { id: 'low-cal',      icon: 'minimize-2', label: 'Light',     ar: 'خفيف' },
];

const INITIAL_SAVED = [
  { id: 's1', name: 'My weekday lunch',     ar: 'غدائي اليومي',   ingredients: ['romaine', 'tomato', 'cucumber', 'chicken', 'feta', 'olive-oil'], lastEaten: '2 days ago' },
  { id: 's2', name: 'Post-workout protein', ar: 'بعد التمرين',    ingredients: ['kale', 'chicken', 'chickpeas', 'almonds', 'tahini', 'avocado', 'egg'], lastEaten: 'Yesterday' },
  { id: 's3', name: 'Light & green',        ar: 'خفيف وأخضر',     ingredients: ['arugula', 'cucumber', 'tofu', 'olive-oil'], lastEaten: '5 days ago' },
  { id: 's4', name: 'Layla\'s fattoush',    ar: 'فتوش ليلى',      ingredients: ['romaine', 'tomato', 'cucumber', 'sumac', 'pomegranate', 'olive-oil'], lastEaten: '1 week ago' },
];

// ── i18n ──────────────────────────────────────────────────
const T = {
  en: {
    appName: 'Saladizer',
    tagline: 'Fresh. Smart. Yours.',
    nav: { home: 'Home', build: 'Build', explore: 'Explore', saved: 'Saved', profile: 'Profile' },
    navSection1: 'Main', navSection2: 'You',
    greetingMorn: 'Good morning', greetingAft: 'Good afternoon', greetingEve: 'Good evening',
    todayMacros: 'Today\u2019s macros', dailyGoal: 'Daily goal',
    cal: 'cal', g: 'g', protein: 'Protein', carbs: 'Carbs', fat: 'Fat', calories: 'Calories',
    saladOfTheDay: 'Salad of the day',
    presets: 'Preset salads', viewAll: 'View all',
    quickFilters: 'Quick filters',
    quickBuildTitle: 'Build your\nperfect salad.',
    quickBuildText: 'Pick a base, add what you love, watch the macros land in real time.',
    startBuilding: 'Start building', chefsChoice: 'Chef\u2019s choice',
    tipTitle: 'Tip of the day',
    tipBody: 'Adding a citrus dressing helps your body absorb iron from leafy greens. Drizzle lemon over your spinach for a small but mighty boost.',
    yourRecent: 'Your recent salads',
    saved: 'Saved salads', exploreTitle: 'Explore', exploreSub: 'Curated bowls, refined by what you love.',
    builderTitle: 'Build your salad', builderSub: 'Pick a category and tap the cards. Macros update live.',
    pickCategory: 'Pick a category',
    nutrition: 'Nutrition', ingredients: 'Ingredients',
    saveSalad: 'Save salad', askChef: 'Ask Chef Saladizer', reviewSalad: 'Review salad',
    emptyBowl: 'Your bowl is empty. Pick an ingredient to begin.',
    saladName: 'Untitled salad',
    profileTitle: 'Profile', profileSub: 'Your goals, your preferences, your data.',
    plusBadge: 'Plus member', upgrade: 'Upgrade to Saladizer+', upgradeSub: 'Unlock all AI features',
    upgradeBtn: 'Upgrade',
    settingsGoals: 'Nutrition goals', settingsDiet: 'Dietary preferences',
    settingsAllergens: 'Allergens', settingsLang: 'Language', settingsAppearance: 'Appearance',
    settingsPrivacy: 'Privacy', settingsNotif: 'Notifications',
    chefName: 'Chef Saladizer', chefSub: 'AI nutrition coach · online',
    chefIntro: 'Hi! I can suggest a salad for your day, swap an ingredient, or remix a saved one. What are you in the mood for?',
    chefPlaceholder: 'Ask anything about food, macros, ideas\u2026',
    chefSuggest1: 'Design a high-protein lunch',
    chefSuggest2: 'What can I make with halloumi?',
    chefSuggest3: 'Swap chicken for vegan protein',
    salads: 'salads', streak: 'day streak', calStat: 'calories',
    daysAgo: 'Last made', items: 'items',
    notFound: 'No saved salads yet', notFoundSub: 'Build one and tap Save to keep it here.',
    rebuild: 'Rebuild', editSalad: 'Edit', saveToast: 'Salad saved',
    share: 'Share', chefMake: 'Chef makes me a salad',
    apply: 'Apply', plusOnly: 'Plus only', forgotPassword: 'Forgot password?',
    continueWithGoogle: 'Continue with Google',
  },
  ar: {
    appName: 'سلادايزر',
    tagline: 'طازجة. ذكية. لك.',
    nav: { home: 'الرئيسية', build: 'اصنع', explore: 'استكشف', saved: 'المحفوظة', profile: 'حسابي' },
    navSection1: 'الرئيسي', navSection2: 'أنت',
    greetingMorn: 'صباح الخير', greetingAft: 'مساء الخير', greetingEve: 'مساء الخير',
    todayMacros: 'مغذيات اليوم', dailyGoal: 'هدف اليوم',
    cal: 'سعرة', g: 'غ', protein: 'بروتين', carbs: 'كربوهيدرات', fat: 'دهون', calories: 'السعرات',
    saladOfTheDay: 'سلطة اليوم',
    presets: 'سلطات جاهزة', viewAll: 'الكل',
    quickFilters: 'فلاتر سريعة',
    quickBuildTitle: 'اصنع سلطتك\nالمثالية.',
    quickBuildText: 'اختر القاعدة، أضف ما يحلو لك، وشاهد المغذيات تتحدث لحظياً.',
    startBuilding: 'ابدأ التحضير', chefsChoice: 'اختيار الشيف',
    tipTitle: 'نصيحة اليوم',
    tipBody: 'إضافة تتبيلة حمضية تساعد جسمك على امتصاص الحديد من الخضار الورقية. رشّة ليمون على السبانخ تصنع فرقاً.',
    yourRecent: 'سلطاتك الأخيرة',
    saved: 'السلطات المحفوظة', exploreTitle: 'استكشف', exploreSub: 'أطباق منتقاة، مصقولة بما تحب.',
    builderTitle: 'اصنع سلطتك', builderSub: 'اختر فئة وانقر على المكونات. المغذيات تتحدث لحظياً.',
    pickCategory: 'اختر فئة',
    nutrition: 'القيمة الغذائية', ingredients: 'المكونات',
    saveSalad: 'احفظ السلطة', askChef: 'اسأل الشيف', reviewSalad: 'مراجعة',
    emptyBowl: 'السلطة فارغة. اختر مكوناً للبدء.',
    saladName: 'سلطة بلا اسم',
    profileTitle: 'حسابي', profileSub: 'أهدافك، تفضيلاتك، بياناتك.',
    plusBadge: 'عضو بلَس', upgrade: 'اشترك في سلادايزر+', upgradeSub: 'افتح جميع مزايا الذكاء',
    upgradeBtn: 'اشترك',
    settingsGoals: 'الأهداف الغذائية', settingsDiet: 'التفضيلات الغذائية',
    settingsAllergens: 'الحساسية', settingsLang: 'اللغة', settingsAppearance: 'المظهر',
    settingsPrivacy: 'الخصوصية', settingsNotif: 'الإشعارات',
    chefName: 'شيف سلادايزر', chefSub: 'مدرّب تغذية ذكي · متصل',
    chefIntro: 'أهلاً! أقدر أقترح سلطة ليومك، أبدّل مكوناً، أو أحدّث وصفة محفوظة. وش بمزاجك اليوم؟',
    chefPlaceholder: 'اسأل عن أي طعام، مغذيات، أو فكرة\u2026',
    chefSuggest1: 'صمم غداء عالي البروتين',
    chefSuggest2: 'وش أعمل بالحلومي؟',
    chefSuggest3: 'استبدل الدجاج ببروتين نباتي',
    salads: 'سلطة', streak: 'يوم متتالي', calStat: 'سعرة',
    daysAgo: 'آخر تحضير', items: 'مكون',
    notFound: 'لا توجد سلطات محفوظة بعد', notFoundSub: 'اصنع واحدة واضغط احفظ.',
    rebuild: 'أعد التحضير', editSalad: 'تعديل', saveToast: 'تم حفظ السلطة',
    share: 'مشاركة', chefMake: 'الشيف يصنع لي سلطة',
    apply: 'أضف', plusOnly: 'لمشتركي Plus', forgotPassword: 'نسيت كلمة المرور؟',
    continueWithGoogle: 'المتابعة بحساب جوجل',
  },
};

function useT(lang) {
  return T[lang] || T.en;
}

function timeGreeting(t) {
  const h = new Date().getHours();
  if (h < 12) return t.greetingMorn;
  if (h < 18) return t.greetingAft;
  return t.greetingEve;
}

// ── Icon helper ───────────────────────────────────────────
function Icon({ name, size = 20, color = 'currentColor', stroke = 2 }) {
  const ref = React.useCallback((el) => {
    if (el && window.feather && window.feather.icons[name]) {
      el.innerHTML = window.feather.icons[name].toSvg({
        width: size, height: size, stroke: color, 'stroke-width': stroke,
      });
    }
  }, [name, size, color, stroke]);
  return <span ref={ref} style={{ display: 'inline-flex', lineHeight: 0 }} />;
}

// ── Sidebar ───────────────────────────────────────────────
function Sidebar({ view, onNav, lang, t, savedCount, user, onUpgrade }) {
  const main = [
    { key: 'home',    icon: 'home',     label: t.nav.home },
    { key: 'build',   icon: 'plus-circle', label: t.nav.build },
    { key: 'explore', icon: 'compass',  label: t.nav.explore },
  ];
  const you = [
    { key: 'saved',   icon: 'bookmark', label: t.nav.saved, badge: savedCount > 0 ? String(savedCount) : null },
    { key: 'profile', icon: 'user',     label: t.nav.profile },
  ];

  const Item = ({ k, icon, label, badge }) => (
    <button
      type="button"
      className={'nav-item' + (view === k ? ' active' : '')}
      onClick={() => onNav(k)}
    >
      <Icon name={icon} size={20} />
      <span className="nav-item-label">{label}</span>
      {badge ? <span className="badge">{badge}</span> : null}
    </button>
  );

  return (
    <aside className="sidebar">
      <div className="sidebar-brand" onClick={() => onNav('home')} style={{ cursor: 'pointer' }}>
        <img src="assets/app-icon.png" alt="" />
        <span>{t.appName}</span>
      </div>
      <div className="sidebar-section">{t.navSection1}</div>
      {main.map(m => <Item key={m.key} k={m.key} icon={m.icon} label={m.label} />)}
      <div className="sidebar-section">{t.navSection2}</div>
      {you.map(m => <Item key={m.key} k={m.key} icon={m.icon} label={m.label} badge={m.badge} />)}

      <div className="sidebar-foot">
        {user && user.subscriptionType === 'plus' ? null : (
          <div className="plus-promo">
            <div className="plus-promo-title">
              <Icon name="zap" size={14} /> {t.upgrade}
            </div>
            <div className="plus-promo-text">{t.upgradeSub}</div>
            <button
              type="button"
              className="plus-promo-btn"
              onClick={() => {
                if (onUpgrade) onUpgrade();
                else if (onNav) onNav('profile');
              }}
            >
              {t.upgradeBtn}
            </button>
          </div>
        )}
      </div>
    </aside>
  );
}

// ── Top bar ───────────────────────────────────────────────
function TopBar({ lang, setLang, t, onOpenChef, user, onNav, onOpenNotifications, unreadCount }) {
  const displayName = (user && (user.fullName || user.username || user.email)) || '';
  const initial = (displayName || '?').slice(0, 1).toUpperCase();
  const goProfile = () => onNav && onNav('profile');
  const hasUnread = unreadCount > 0;
  const isAr = lang === 'ar';

  return (
    <div className="topbar">
      <div className="topbar-actions">
        <div className="lang-pill" role="tablist" aria-label="Language">
          <button className={lang === 'en' ? 'on' : ''} onClick={() => setLang('en')}>EN</button>
          <button className={lang === 'ar' ? 'on' : ''} onClick={() => setLang('ar')}>ع</button>
        </div>
        <button className="icon-btn" title="Chef Saladizer" onClick={onOpenChef}>
          <Icon name="message-circle" size={20} />
          <span className="dot" />
        </button>
        <button
          className={'icon-btn bell-with-dot'}
          title={lang === 'ar' ? 'الإشعارات' : 'Notifications'}
          onClick={onOpenNotifications}
        >
          <Icon name="bell" size={20} />
          {hasUnread ? <span className="unread-dot" /> : null}
        </button>
        <button
          type="button"
          className="avatar"
          title={displayName || (lang === 'ar' ? 'الملف الشخصي' : 'Profile')}
          onClick={goProfile}
          style={{ cursor: 'pointer', border: 'none', padding: 0 }}
        >
          {user && user.profileImageUri ? (
            <img src={user.profileImageUri} alt="" style={{ width: '100%', height: '100%', borderRadius: '50%', objectFit: 'cover' }} />
          ) : initial}
        </button>
      </div>
    </div>
  );
}

// ── Macro ring (SVG) ──────────────────────────────────────
function MacroRing({ value, target, color = 'var(--primary)', label }) {
  const pct = Math.max(0, Math.min(1, value / target));
  const R = 26;
  const C = 2 * Math.PI * R;
  return (
    <div className="macro-ring">
      <svg viewBox="0 0 64 64">
        <circle cx="32" cy="32" r={R} className="track" fill="none" strokeWidth="6" />
        <circle cx="32" cy="32" r={R} className="fill" fill="none" stroke={color} strokeWidth="6"
          strokeDasharray={C} strokeDashoffset={C * (1 - pct)} />
      </svg>
      <div className="label">{label}</div>
    </div>
  );
}

// ── Filter chip ───────────────────────────────────────────
function Chip({ icon, label, on, onClick }) {
  return (
    <button className={'chip' + (on ? ' on' : '')} type="button" onClick={onClick}>
      <Icon name={icon} size={14} color={on ? '#fff' : 'var(--primary)'} />
      {label}
    </button>
  );
}

// ── Bowl preview SVG ──────────────────────────────────────
// Renders ingredient "dots" inside a bowl curve based on selections.
function BowlPreview({ items }) {
  // Positions: items get distributed inside an ellipse, with a deterministic seeded layout
  const dots = [];
  let idx = 0;
  items.forEach(({ ing, qty }) => {
    for (let i = 0; i < qty; i++) {
      const seed = (idx * 37 + (ing.id.charCodeAt(0) || 0) * 13) % 360;
      const r = 18 + ((idx * 7) % 40);
      const angle = (seed * Math.PI) / 180;
      const x = 100 + Math.cos(angle) * r;
      const y = 84 + Math.sin(angle) * r * 0.55;
      dots.push({ x, y, color: ingredientColor(ing), id: ing.id + '-' + i });
      idx++;
    }
  });

  return (
    <svg viewBox="0 0 200 180" aria-hidden="true">
      {/* Subtle drop shadow */}
      <ellipse cx="100" cy="160" rx="64" ry="8" fill="rgba(0,0,0,0.10)" />
      {/* Bowl outer rim */}
      <ellipse cx="100" cy="84" rx="74" ry="22" fill="var(--bg-mint)" />
      {/* Bowl body */}
      <path
        d="M 26,84 Q 26,160 100,160 Q 174,160 174,84 Z"
        fill="var(--bg-surface)"
        stroke="var(--border-strong)"
        strokeWidth="2"
      />
      {/* Bowl inner */}
      <ellipse cx="100" cy="84" rx="68" ry="18" fill="var(--bg-tinted)" />
      {/* Ingredient dots */}
      {dots.map(d => (
        <circle key={d.id} cx={d.x} cy={d.y} r="8.5" fill={d.color} stroke="#ffffff66" strokeWidth="1.5">
          <animate attributeName="r" from="0" to="8.5" dur="0.35s" fill="freeze" />
        </circle>
      ))}
      {/* Highlight */}
      <ellipse cx="86" cy="76" rx="20" ry="4" fill="rgba(255,255,255,0.30)" />
    </svg>
  );
}

function ingredientColor(ing) {
  switch (ing.category) {
    case 'Greens':     return '#43A047';
    case 'Vegetables': return '#FF8A65';
    case 'Protein':    return '#A0522D';
    case 'Toppings':   return '#F5A623';
    case 'Dressing':   return '#FBC02D';
    default:           return '#2D7A3E';
  }
}

// ── Ingredient search hook (server-backed, debounced) ────
// Returns { items, loading, error } where items is the normalized
// catalog shape used everywhere on the web: { id, name, ar, calories,
// protein, carbs, fat, glyph, category, isChef }.
function glyphForCategory(cat) {
  switch ((cat || '').toLowerCase()) {
    case 'greens':     return '🥬';
    case 'vegetables': return '🥒';
    case 'protein':    return '🍗';
    case 'toppings':   return '✨';
    case 'dressing':   return '🫒';
    case 'chef':       return '🌶️';
    default:           return '🥗';
  }
}

function normalizeServerIngredient(raw) {
  if (!raw || !raw.id) return null;
  return {
    id: String(raw.id),
    name: raw.nameEn || raw.name || '',
    ar: raw.nameAr || raw.name || '',
    calories: Number(raw.calories) || 0,
    protein: Number(raw.protein) || 0,
    carbs: Number(raw.carbs) || 0,
    fat: Number(raw.fat) || 0,
    category: raw.category || 'Vegetables',
    glyph: glyphForCategory(raw.category),
    isChef: !!raw.isChefIngredient,
  };
}

function useDebouncedIngredientSearch(query, opts = {}) {
  const minChars = opts.minChars || 2;
  const debounceMs = opts.debounceMs || 250;
  const limit = opts.limit || 30;
  const [state, setState] = React.useState({ items: [], loading: false, error: null });

  React.useEffect(() => {
    const q = (query || '').trim();
    if (q.length < minChars) {
      setState({ items: [], loading: false, error: null });
      return;
    }
    setState(s => ({ items: s.items, loading: true, error: null }));
    const ctrl = new AbortController();
    const timer = setTimeout(async () => {
      try {
        const params = new URLSearchParams({ search: q, limit: String(limit) });
        const res = await fetch('/api/ingredients?' + params.toString(), { signal: ctrl.signal });
        if (!res.ok) throw new Error('HTTP ' + res.status);
        const data = await res.json();
        const items = (Array.isArray(data) ? data : []).map(normalizeServerIngredient).filter(Boolean);
        setState({ items, loading: false, error: null });
      } catch (e) {
        if (e && (e.name === 'AbortError' || e.code === 20)) return;
        console.warn('[Saladizer] ingredient search failed:', e && e.message);
        setState({ items: [], loading: false, error: e });
      }
    }, debounceMs);
    return () => { clearTimeout(timer); ctrl.abort(); };
  }, [query, minChars, debounceMs, limit]);

  return state;
}

// ── Toast ─────────────────────────────────────────────────
function Toast({ text, show }) {
  return (
    <div className={'toast' + (show ? ' show' : '')}>
      <Icon name="check-circle" size={16} color="var(--color-leaf-green)" />
      {text}
    </div>
  );
}

Object.assign(window, {
  CATEGORIES, INGREDIENTS, ALL_INGREDIENTS, PRESETS, FILTERS, INITIAL_SAVED,
  T, useT, timeGreeting,
  Icon, Sidebar, TopBar, MacroRing, Chip, BowlPreview, ingredientColor, Toast,
  useDebouncedIngredientSearch, normalizeServerIngredient, glyphForCategory,
});
