// Saladizer Web — feature components (share, preset detail, chef-makes-me-a-salad).

// ──────────────────────────────────────────────────────────
// Share-salad modal: POSTs to /api/share-salad, shows link
// ──────────────────────────────────────────────────────────
function ShareSaladModal({ open, onClose, lang, selections, saladName, totals }) {
  const isAr = lang === 'ar';
  const [busy, setBusy] = React.useState(false);
  const [link, setLink] = React.useState('');
  const [err, setErr] = React.useState('');
  const [copied, setCopied] = React.useState(false);

  React.useEffect(() => {
    if (!open) { setLink(''); setErr(''); setCopied(false); return; }
    let cancelled = false;
    (async () => {
      setBusy(true);
      try {
        const items = Object.entries(selections || {})
          .map(([id, qty]) => {
            const ing = window.ALL_INGREDIENTS.find(i => i.id === id);
            return ing && qty > 0 ? {
              nameEn: ing.name, nameAr: ing.ar,
              category: ing.category, quantity: qty,
            } : null;
          }).filter(Boolean);
        if (items.length === 0) {
          if (!cancelled) setErr(isAr ? 'سلطتك فارغة' : 'Your bowl is empty');
          return;
        }
        const name = (saladName || '').trim() || (isAr ? 'سلطتي' : 'My salad');
        const r = await window.api.shareSalad({
          name, nameEn: name, nameAr: name,
          ingredients: items,
          totalCalories: Math.round(totals.calories || 0),
          totalProtein: +(totals.protein || 0).toFixed(1),
          totalCarbs: +(totals.carbs || 0).toFixed(1),
          totalFat: +(totals.fat || 0).toFixed(1),
        });
        const code = r && (r.shareCode || r.code);
        if (cancelled) return;
        if (code) setLink(`${window.location.origin}/salad/${code}`);
        else setErr(isAr ? 'تعذّر إنشاء الرابط' : 'Could not create link');
      } catch (e) {
        if (!cancelled) setErr((e && e.data?.error) || (isAr ? 'تعذّر إنشاء الرابط' : 'Could not create link'));
      } finally {
        if (!cancelled) setBusy(false);
      }
    })();
    return () => { cancelled = true; };
  }, [open]);

  async function copy() {
    if (!link) return;
    try {
      await navigator.clipboard.writeText(link);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch (_) {}
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title={isAr ? 'مشاركة السلطة' : 'Share salad'}
      footer={
        <>
          <button className="btn btn-soft" onClick={onClose}>
            {isAr ? 'إغلاق' : 'Close'}
          </button>
          <button className="btn btn-primary" disabled={!link} onClick={copy}>
            <Icon name="copy" size={14} /> {copied ? (isAr ? 'تم النسخ ✓' : 'Copied ✓') : (isAr ? 'نسخ الرابط' : 'Copy link')}
          </button>
        </>
      }
    >
      {busy ? (
        <div style={{ padding: '12px 0', color: 'var(--fg-muted)' }}>
          {isAr ? 'جارٍ إنشاء الرابط...' : 'Creating link...'}
        </div>
      ) : err ? (
        <div className="auth-error">{err}</div>
      ) : link ? (
        <>
          <div className="info-note">
            {isAr ? 'انسخ الرابط وشاركه. أي شخص يقدر يفتحه.' : 'Copy the link and share. Anyone with the link can view this salad.'}
          </div>
          <label className="form-field">
            <span>{isAr ? 'الرابط' : 'Public link'}</span>
            <input type="text" readOnly value={link} onFocus={(e) => e.target.select()} />
          </label>
        </>
      ) : null}
    </Modal>
  );
}

// ──────────────────────────────────────────────────────────
// Preset salad detail modal — full info + rating + submit
// ──────────────────────────────────────────────────────────
function PresetDetailModal({ open, onClose, presetId, lang, t, user, onBuildFrom }) {
  const isAr = lang === 'ar';
  const [preset, setPreset] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [myRating, setMyRating] = React.useState(0);
  const [submitBusy, setSubmitBusy] = React.useState(false);

  const isPlus = !!(user && user.subscriptionType === 'plus');

  React.useEffect(() => {
    if (!open || !presetId) return;
    let cancelled = false;
    (async () => {
      setBusy(true); setErr(''); setPreset(null); setMyRating(0);
      try {
        const p = await window.api.getPresetSalad(presetId);
        if (!cancelled) setPreset(p);
        if (user) {
          try {
            const r = await window.api.getMyPresetRating(presetId);
            if (!cancelled && r && typeof r.rating === 'number') setMyRating(r.rating);
          } catch (_) {}
        }
      } catch (e) {
        if (!cancelled) setErr((isAr ? 'تعذّر تحميل التفاصيل' : 'Could not load details'));
      } finally {
        if (!cancelled) setBusy(false);
      }
    })();
    return () => { cancelled = true; };
  }, [open, presetId, user]);

  async function rate(stars) {
    if (!user) return;
    setMyRating(stars);
    try { await window.api.ratePreset(presetId, stars); } catch (_) {}
  }

  async function submitAsPreset() {
    if (!preset || !isPlus) return;
    setSubmitBusy(true);
    try {
      await window.api.submitPresetSalad({
        name: preset.name || preset.nameEn,
        nameEn: preset.nameEn || preset.name,
        nameAr: preset.nameAr || preset.name,
        ingredients: preset.ingredients || [],
        totalCalories: preset.totalCalories,
        totalProtein: preset.totalProtein,
        totalCarbs: preset.totalCarbs,
        totalFat: preset.totalFat,
        tags: preset.tags || [],
      });
      setErr(isAr ? 'تم الإرسال للمراجعة ✓' : 'Submitted for review ✓');
    } catch (e) {
      setErr((e && e.data?.error) || (isAr ? 'تعذّر الإرسال' : 'Could not submit'));
    } finally {
      setSubmitBusy(false);
    }
  }

  const name = preset ? (lang === 'ar' ? (preset.nameAr || preset.name) : (preset.nameEn || preset.name)) : '';
  const ings = preset?.ingredients || [];

  return (
    <Modal
      open={open}
      onClose={onClose}
      wide
      title={name || (isAr ? 'تفاصيل السلطة' : 'Salad details')}
      footer={preset ? (
        <>
          <button className="btn btn-soft" onClick={onClose}>{isAr ? 'إغلاق' : 'Close'}</button>
          <button className="btn btn-primary" onClick={() => { onBuildFrom && onBuildFrom(preset); onClose(); }}>
            <Icon name="play" size={14} /> {isAr ? 'حضّر هذا' : 'Make this'}
          </button>
        </>
      ) : null}
    >
      {busy ? (
        <div style={{ padding: '16px 0', color: 'var(--fg-muted)' }}>{isAr ? 'جارٍ التحميل...' : 'Loading...'}</div>
      ) : err && !preset ? (
        <div className="auth-error">{err}</div>
      ) : preset ? (
        <>
          {preset.imageUrl ? (
            <div style={{
              width: '100%', height: 220, borderRadius: 14, marginBottom: 14,
              backgroundImage: `url('${preset.imageUrl}')`,
              backgroundSize: 'cover', backgroundPosition: 'center',
            }} />
          ) : null}

          <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', marginBottom: 12 }}>
            <span><strong>{Math.round(preset.totalCalories || 0)}</strong> {t.cal}</span>
            <span><strong>{Math.round(preset.totalProtein || 0)}{t.g}</strong> {t.protein}</span>
            <span><strong>{Math.round(preset.totalCarbs || 0)}{t.g}</strong> {t.carbs}</span>
            <span><strong>{Math.round(preset.totalFat || 0)}{t.g}</strong> {t.fat}</span>
          </div>

          {preset.descriptionEn || preset.descriptionAr ? (
            <p style={{ color: 'var(--fg-muted)', marginBottom: 12 }}>
              {isAr ? (preset.descriptionAr || preset.descriptionEn) : (preset.descriptionEn || preset.descriptionAr)}
            </p>
          ) : null}

          <div style={{ fontSize: 12, fontWeight: 800, color: 'var(--fg-muted)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>
            {t.ingredients}
          </div>
          <ul className="ing-list" style={{ marginBottom: 14 }}>
            {ings.map((it, i) => {
              // Backend shape for preset ingredients: { ingredient: { nameEn, nameAr, ... }, quantity }.
              // Also accept the flat shape used elsewhere as a fallback.
              const ing = it.ingredient || it;
              const nameEn = ing.nameEn || ing.name || '';
              const nameAr = ing.nameAr || ing.ar || '';
              return (
                <li key={i}>
                  <span className="dot" style={{ background: 'var(--primary)' }} />
                  <span>{isAr ? (nameAr || nameEn) : (nameEn || nameAr)}</span>
                  {it.quantity ? <span className="qty">×{it.quantity}</span> : null}
                </li>
              );
            })}
          </ul>

          {user ? (
            <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12 }}>
              <div style={{ fontSize: 13, color: 'var(--fg-muted)', marginBottom: 6 }}>
                {isAr ? 'تقييمك' : 'Your rating'}
              </div>
              <div style={{ display: 'flex', gap: 4 }}>
                {[1, 2, 3, 4, 5].map(n => (
                  <button
                    key={n}
                    type="button"
                    onClick={() => rate(n)}
                    style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 2 }}
                    aria-label={`${n} star`}
                  >
                    <Icon name="star" size={22} color={n <= myRating ? 'var(--accent)' : 'var(--border-strong)'} />
                  </button>
                ))}
              </div>
            </div>
          ) : null}

          {isPlus ? (
            <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12, marginTop: 12 }}>
              <button className="btn btn-soft btn-sm" disabled={submitBusy} onClick={submitAsPreset}>
                <Icon name="upload" size={12} /> {isAr ? 'أرسِل سلطتي كنموذج' : 'Submit my salad as a preset'}
              </button>
              {err ? <div style={{ marginTop: 8, fontSize: 13, color: 'var(--primary)' }}>{err}</div> : null}
            </div>
          ) : null}
        </>
      ) : null}
    </Modal>
  );
}

// ──────────────────────────────────────────────────────────
// Chef Makes Me a Salad — goal picker → generate → result
// ──────────────────────────────────────────────────────────
function ChefMakesMeASaladView({ t, lang, user, onNav, setSelections, setSaladName, setExtraCatalog, setFromChef, setChefNote, setChefNutrition }) {
  const isAr = lang === 'ar';
  const isPlus = !!(user && user.subscriptionType === 'plus');
  const [goal, setGoal] = React.useState('balanced');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [result, setResult] = React.useState(null);
  // Index of the currently animated "thinking" step (0..steps.length-1).
  const [thinkStep, setThinkStep] = React.useState(0);

  const goals = isAr ? [
    { id: 'balanced',     label: 'متوازن',           icon: 'pie-chart' },
    { id: 'high_protein', label: 'عالي البروتين',     icon: 'zap' },
    { id: 'low_carb',     label: 'منخفض الكربوهيدرات', icon: 'minimize-2' },
    { id: 'weight_loss',  label: 'لخسارة الوزن',      icon: 'trending-down' },
    { id: 'muscle_gain',  label: 'لبناء العضلات',      icon: 'trending-up' },
  ] : [
    { id: 'balanced',     label: 'Balanced',     icon: 'pie-chart' },
    { id: 'high_protein', label: 'High protein', icon: 'zap' },
    { id: 'low_carb',     label: 'Low carb',     icon: 'minimize-2' },
    { id: 'weight_loss',  label: 'Weight loss',  icon: 'trending-down' },
    { id: 'muscle_gain',  label: 'Muscle gain',  icon: 'trending-up' },
  ];

  // Thinking steps shown while the backend is composing the bowl. They cycle
  // visually so users feel the chef is actually preparing the salad.
  const thinkingSteps = isAr ? [
    { icon: 'book-open',     label: 'يقرأ تفضيلاتك الغذائية…' },
    { icon: 'search',        label: 'يختار المكونات الأنسب…' },
    { icon: 'sliders',       label: 'يوازن البروتين والكربوهيدرات…' },
    { icon: 'droplet',       label: 'يضيف الصلصة ولمسة الشيف…' },
    { icon: 'check-circle',  label: 'يجهّز السلطة لك…' },
  ] : [
    { icon: 'book-open',     label: 'Reading your preferences…' },
    { icon: 'search',        label: 'Picking the best ingredients…' },
    { icon: 'sliders',       label: 'Balancing protein and carbs…' },
    { icon: 'droplet',       label: 'Adding the dressing and chef touch…' },
    { icon: 'check-circle',  label: 'Plating your salad…' },
  ];

  // Cycle through thinking steps while busy.
  React.useEffect(() => {
    if (!busy) { setThinkStep(0); return; }
    setThinkStep(0);
    const last = thinkingSteps.length - 1;
    const id = setInterval(() => {
      setThinkStep(s => {
        if (s >= last) { clearInterval(id); return s; }
        return s + 1;
      });
    }, 1400);
    return () => clearInterval(id);
  }, [busy, thinkingSteps.length]);

  // Resolve chef-generated ingredients against the backend catalog so the
  // Builder can render them (with proper nutrition + Arabic labels) after the
  // user taps "Open in builder". Falls back to a synthetic entry if nothing
  // matches, so totals are at least consistent with the AI estimate.
  async function resolveAndStoreSelections(r) {
    const items = (r && (r.ingredients || r.items)) || [];
    if (items.length === 0) return;
    const next = {};
    const extras = {};
    // Track which DB ids have already been assigned so two AI items that
    // resolve to the same catalog entry don't double-count.
    const usedIds = new Set();

    await Promise.all(items.map(async (it) => {
      const en = (it.nameEn || it.name || '').toString().trim();
      const ar = (it.nameAr || '').toString().trim();

      // Convert the AI's gram amount to a qty multiplier (USDA data is per 100g).
      // e.g. 120g salmon → qty=1.2 → builder shows 140 kcal instead of 117.
      // Fall back to qty=1 (one 100g serving) when unit is not grams or amount=0.
      const rawAmount = Number(it.amount) || 0;
      const unit = (it.unit || 'g').toLowerCase().trim();
      const qty = (unit === 'g' && rawAmount > 0)
        ? Math.round((rawAmount / 100) * 10) / 10   // round to 1 decimal
        : 1;

      // Helper: assign id only once per ingredient (prevents duplicates when two
      // AI items resolve to the same catalog entry).
      const assign = (id) => {
        if (usedIds.has(id)) return false;
        usedIds.add(id);
        next[id] = qty;
        return true;
      };

      // 1) Local catalog match (cheap, no network)
      const local = window.ALL_INGREDIENTS && window.ALL_INGREDIENTS.find(x =>
        x.name.toLowerCase() === en.toLowerCase() ||
        (ar && x.ar && x.ar.toLowerCase() === ar.toLowerCase())
      );
      if (local) { assign(local.id); return; }

      // 2) Backend / USDA search
      try {
        const results = await window.api.listIngredients({ search: en || ar, limit: 5 });
        const norm = (window.normalizeServerIngredient && Array.isArray(results))
          ? results.map(window.normalizeServerIngredient)
          : [];
        // Prefer exact name match; only accept norm[0] as a partial-match fallback
        // when the query has at least 3 chars to avoid random broad matches.
        const enLow = en.toLowerCase();
        const hit = norm.find(x =>
          x.name.toLowerCase() === enLow ||
          (ar && x.ar && x.ar === ar)
        ) || (enLow.length >= 3 ? norm[0] : null);
        if (hit) {
          extras[hit.id] = hit;
          assign(hit.id);
          return;
        }
      } catch (_) { /* fall through to synthetic */ }

      // 3) Synthetic placeholder — shows item in builder even if not in DB
      const synthId = `chef_ai_${en.toLowerCase().replace(/[^a-z0-9]+/g, '_') || Math.random().toString(36).slice(2, 8)}`;
      if (!usedIds.has(synthId)) {
        usedIds.add(synthId);
        extras[synthId] = {
          id: synthId,
          name: en || ar || 'Ingredient',
          ar: ar || en || 'مكوّن',
          calories: 0, protein: 0, carbs: 0, fat: 0,
          glyph: (window.glyphForCategory && window.glyphForCategory('')) || '🥗',
          category: 'Toppings',
          isChef: true,
        };
        next[synthId] = qty;
      }
    }));

    if (Object.keys(extras).length > 0 && setExtraCatalog) {
      setExtraCatalog(prev => ({ ...prev, ...extras }));
    }
    if (Object.keys(next).length > 0 && setSelections) {
      setSelections(next);
      if (setSaladName && (r.name || r.nameEn || r.nameAr)) {
        setSaladName(isAr ? (r.nameAr || r.name || r.nameEn) : (r.nameEn || r.name || r.nameAr));
      }
    }
  }

  async function generate() {
    if (!isPlus) return;
    setBusy(true); setErr(''); setResult(null);
    try {
      const resp = await window.api.generateChefSalad({ goal, language: isAr ? 'ar' : 'en' });
      // Backend returns { success, salad: {...} } on success, or { error, message }.
      if (resp && resp.error) {
        if (resp.error === 'plus_required') {
          setErr(isAr ? (resp.messageAr || 'هذه الميزة لمشتركي Plus') : (resp.message || 'Plus members only'));
        } else if (resp.error === 'limit_reached') {
          setErr(isAr ? (resp.messageAr || 'وصلت الحد اليومي') : (resp.message || 'Daily limit reached'));
        } else {
          setErr(isAr ? (resp.messageAr || 'تعذّر توليد السلطة') : (resp.message || 'Could not generate salad'));
        }
        return;
      }
      const r = (resp && resp.salad) ? resp.salad : resp;
      setResult(r);
      await resolveAndStoreSelections(r);
      // Store the chef's explanation so BuilderView can display it.
      if (setChefNote && (r.whyThisSalad || r.whyThisSaladAr)) {
        setChefNote({
          en: Array.isArray(r.whyThisSalad)  ? r.whyThisSalad  : (r.whyThisSalad  ? [r.whyThisSalad]  : []),
          ar: Array.isArray(r.whyThisSaladAr) ? r.whyThisSaladAr : (r.whyThisSaladAr ? [r.whyThisSaladAr] : []),
        });
      }
      // Capture chef's own estimatedNutrition so BuilderView can display the
      // exact same numbers the user saw on the chef result card. Without this
      // the builder recomputes via USDA-per-100g math which never matches.
      if (setChefNutrition) {
        const nut = r.estimatedNutrition || r.nutrition || null;
        if (nut) {
          setChefNutrition({
            calories: Number(nut.calories) || 0,
            protein:  Number(nut.protein)  || 0,
            carbs:    Number(nut.carbs)    || 0,
            fat:      Number(nut.fat)      || 0,
          });
        } else {
          setChefNutrition(null);
        }
      }
    } catch (e) {
      console.error('[Saladizer] generateChefSalad failed:', e);
      if (e && e.status === 403) {
        setErr(isAr ? 'هذه الميزة لمشتركي Plus' : 'Plus members only');
      } else if (e && e.status === 429) {
        setErr(isAr ? 'وصلت الحد اليومي' : 'Daily limit reached');
      } else {
        setErr(isAr ? 'تعذّر توليد السلطة' : 'Could not generate salad');
      }
    } finally {
      setBusy(false);
    }
  }

  function reset() {
    setResult(null);
    setErr('');
  }

  return (
    <div className="page">
      <div className="page-eyebrow">{isAr ? 'ذكاء اصطناعي' : 'AI'}</div>
      <h1 className="page-title">
        {isAr ? 'الشيف يصنع لي سلطة' : 'Chef makes me a salad'}
        <span style={{ display: 'block', color: 'var(--fg-muted)', fontSize: 18, fontWeight: 600, marginTop: 8 }}>
          {isAr ? 'اختر الهدف، وخلّي الشيف يجهّز لك سلطة كاملة.' : 'Pick a goal and let Chef Saladizer compose a complete bowl.'}
        </span>
      </h1>

      {!isPlus ? (
        <div className="info-note" style={{ marginTop: 18 }}>
          {isAr
            ? 'هذه الميزة متاحة لمشتركي Saladizer+ فقط.'
            : 'This feature is available for Saladizer+ members only.'}
        </div>
      ) : null}

      <div style={{
        marginTop: 22,
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',
        gap: 12,
      }}>
        {goals.map(g => (
          <button
            key={g.id}
            type="button"
            onClick={() => setGoal(g.id)}
            disabled={!isPlus}
            className={'preset-card' + (goal === g.id ? ' on' : '')}
            style={{
              padding: 18,
              border: goal === g.id ? '2px solid var(--primary)' : '1px solid var(--border)',
              background: 'var(--bg-surface)',
              cursor: isPlus ? 'pointer' : 'not-allowed',
              opacity: isPlus ? 1 : 0.55,
              borderRadius: 14,
              textAlign: 'start',
            }}
          >
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <Icon name={g.icon} size={20} color="var(--primary)" />
              <strong style={{ fontSize: 15, color: 'var(--fg)' }}>{g.label}</strong>
            </div>
          </button>
        ))}
      </div>

      <div style={{ marginTop: 22, display: 'flex', gap: 10, flexWrap: 'wrap' }}>
        {!result ? (
          <button className="btn btn-primary" disabled={!isPlus || busy} onClick={generate}>
            <Icon name="zap" size={14} /> {busy ? (isAr ? 'جارٍ التوليد...' : 'Generating...') : (isAr ? 'ولّد السلطة' : 'Generate salad')}
          </button>
        ) : (
          <>
            <button className="btn btn-primary" onClick={() => {
              if (setFromChef) setFromChef(true);
              onNav('build');
            }}>
              <Icon name="arrow-right" size={14} /> {isAr ? 'استخدم هذه السلطة' : 'Use this salad'}
            </button>
            <button className="btn btn-soft" onClick={reset}>
              <Icon name="refresh-cw" size={14} /> {isAr ? 'جرّب هدفاً آخر' : 'Try another goal'}
            </button>
          </>
        )}
      </div>

      {err ? <div className="auth-error" style={{ marginTop: 14 }}>{err}</div> : null}

      {busy ? (
        <div style={{
          marginTop: 22, padding: 18,
          background: 'var(--bg-tinted)', borderRadius: 14,
          border: '1px solid var(--border)',
        }}>
          <div style={{
            fontWeight: 800, fontSize: 16, marginBottom: 12,
            display: 'flex', alignItems: 'center', gap: 8, color: 'var(--fg)',
          }}>
            <span className="chef-pulse" style={{
              width: 10, height: 10, borderRadius: '50%',
              background: 'var(--primary)', display: 'inline-block',
            }} />
            {isAr ? 'الشيف يحضّر سلطتك…' : 'Chef is preparing your salad…'}
          </div>
          <ol style={{
            listStyle: 'none', padding: 0, margin: 0,
            display: 'flex', flexDirection: 'column', gap: 8,
          }}>
            {thinkingSteps.map((s, i) => {
              const done = i < thinkStep;
              const active = i === thinkStep;
              const color = active ? 'var(--primary)' : done ? 'var(--fg)' : 'var(--fg-muted)';
              return (
                <li key={i} style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  opacity: i > thinkStep ? 0.5 : 1,
                  transition: 'opacity 0.3s ease',
                }}>
                  <Icon name={done ? 'check' : s.icon} size={16} color={color} />
                  <span style={{
                    color, fontWeight: active ? 700 : 500, fontSize: 14,
                  }}>{s.label}</span>
                </li>
              );
            })}
          </ol>
        </div>
      ) : null}

      {result && !busy ? (
        <div style={{
          marginTop: 22, padding: 18,
          background: 'var(--bg-tinted)', borderRadius: 14,
        }}>
          <div style={{ fontWeight: 800, fontSize: 18, marginBottom: 6 }}>
            {isAr ? (result.nameAr || result.name) : (result.name || result.nameAr)}
          </div>
          {(result.descriptionAr || result.description) ? (
            <div style={{ color: 'var(--fg-muted)', fontSize: 14, marginBottom: 10 }}>
              {isAr ? (result.descriptionAr || result.description) : (result.description || result.descriptionAr)}
            </div>
          ) : null}
          {(() => {
            const nut = result.estimatedNutrition || result.nutrition || {
              calories: result.totalCalories, protein: result.totalProtein,
              carbs: result.totalCarbs, fat: result.totalFat,
            };
            return (
              <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', marginBottom: 10, color: 'var(--fg-muted)' }}>
                <span><strong>{Math.round(nut.calories || 0)}</strong> {t.cal}</span>
                <span><strong>{Math.round(nut.protein || 0)}{t.g}</strong> {t.protein}</span>
                <span><strong>{Math.round(nut.carbs || 0)}{t.g}</strong> {t.carbs}</span>
                <span><strong>{Math.round(nut.fat || 0)}{t.g}</strong> {t.fat}</span>
              </div>
            );
          })()}
          <ul className="ing-list">
            {(result.ingredients || []).map((it, i) => {
              const qty = it.amount || it.quantity;
              const unit = it.unit || 'g';
              return (
                <li key={i}>
                  <span className="dot" style={{ background: 'var(--primary)' }} />
                  <span>{isAr ? (it.nameAr || it.name) : (it.name || it.nameAr)}</span>
                  {qty ? <span className="qty">{qty}{unit}</span> : null}
                </li>
              );
            })}
          </ul>
          {Array.isArray(result.whyThisSalad || result.whyThisSaladAr) ? (
            <div style={{ marginTop: 14 }}>
              <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 6, color: 'var(--fg)' }}>
                {isAr ? 'لماذا هذه السلطة؟' : 'Why this salad?'}
              </div>
              <ul style={{ margin: 0, paddingInlineStart: 20, color: 'var(--fg-muted)', fontSize: 14 }}>
                {((isAr ? (result.whyThisSaladAr || result.whyThisSalad) : (result.whyThisSalad || result.whyThisSaladAr)) || []).map((w, i) => (
                  <li key={i} style={{ marginBottom: 4 }}>{w}</li>
                ))}
              </ul>
            </div>
          ) : null}
        </div>
      ) : null}
    </div>
  );
}

Object.assign(window, {
  ShareSaladModal, PresetDetailModal, ChefMakesMeASaladView,
});
