/* === Meals screen: Recipes, weekly plan, shopping list, pantry === */

function MealsScreen({ state, setState, setOpenModal, setEditingRecipe }) {
  const { recipes = [], mealPlan = [], pantry = [] } = state;
  const [tab, setTab] = React.useState('recipes');
  const [planWeek, setPlanWeek] = React.useState(() => weekStartIso(isoDate(TODAY)));
  const [viewingRecipe, setViewingRecipe] = React.useState(null);

  const openView = (r) => setViewingRecipe(r);
  const editFromView = () => {
    setEditingRecipe(viewingRecipe);
    setViewingRecipe(null);
    setOpenModal('recipe');
  };

  return (
    <>
      <div className="page-head">
        <div>
          <div className="page-sub">{recipes.length} recipes · {mealPlan.length} planned · {pantry.length} pantry</div>
          <h1 className="page-title">Meals</h1>
        </div>
        {tab === 'recipes' && (
          <div className="row gap-sm">
            <button className="btn primary" onClick={() => { setEditingRecipe(null); setOpenModal('recipe'); }}>+ New recipe</button>
          </div>
        )}
      </div>

      <div className="meals-body">
        <div className="meals-tabs">
          {[
            ['recipes',  'Recipes'],
            ['plan',     'Plan'],
            ['pantry',   'Pantry'],
            ['shopping', 'Shopping list'],
          ].map(([id, label]) => (
            <button key={id}
              className={`meals-tab ${tab === id ? 'active' : ''}`}
              onClick={() => setTab(id)}>{label}</button>
          ))}
        </div>

        {tab === 'recipes'  && <RecipesTab  recipes={recipes} pantry={pantry} onView={openView} setOpenModal={setOpenModal} setEditingRecipe={setEditingRecipe} />}
        {tab === 'plan'     && <PlanTab     state={state} setState={setState} planWeek={planWeek} setPlanWeek={setPlanWeek} />}
        {tab === 'shopping' && <ShoppingTab state={state} setState={setState} planWeek={planWeek} setPlanWeek={setPlanWeek} />}
        {tab === 'pantry'   && <PantryTab   state={state} setState={setState} />}
      </div>

      {viewingRecipe && (
        <RecipeView
          recipe={viewingRecipe}
          pantry={pantry}
          onClose={() => setViewingRecipe(null)}
          onEdit={editFromView}
        />
      )}
    </>
  );
}

// ──────────────────────────────────────────────────
// RECIPES
// ──────────────────────────────────────────────────
function RecipesTab({ recipes, pantry, onView, setOpenModal, setEditingRecipe }) {
  const [filterMeal, setFilterMeal] = React.useState('all');
  const [filterTier, setFilterTier] = React.useState('all');
  const [filterTag,  setFilterTag]  = React.useState('all');

  const allTags = React.useMemo(() => {
    const set = new Set();
    recipes.forEach(r => (r.tags || []).forEach(t => set.add(t)));
    return [...set].sort();
  }, [recipes]);

  const visible = recipes.filter(r => {
    if (filterMeal !== 'all' && !(r.mealTags || []).includes(filterMeal)) return false;
    if (filterTier !== 'all' && costTierIndex(recipeCost(r)) !== parseInt(filterTier)) return false;
    if (filterTag !== 'all' && !(r.tags || []).includes(filterTag)) return false;
    return true;
  });

  return (
    <div>
      <div className="row gap-sm meals-filter-row">
        <span className="eyebrow">Meal</span>
        {[['all','All'], ...MEAL_TYPES.map(m => [m, MEAL_TYPE_LABELS[m]])].map(([v, l]) => (
          <button key={v} className={`seg ${filterMeal === v ? 'active' : ''}`} onClick={() => setFilterMeal(v)}>{l}</button>
        ))}
        <span style={{ flex: 1 }} />
        <span className="eyebrow">Price</span>
        {[['all','All'], ['0','I'], ['1','II'], ['2','III']].map(([v, l]) => (
          <button key={v} className={`seg ${filterTier === v ? 'active' : ''}`} onClick={() => setFilterTier(v)}>{l}</button>
        ))}
      </div>

      {allTags.length > 0 && (
        <div className="row gap-sm meals-filter-row" style={{ marginTop: -6 }}>
          <span className="eyebrow">Tag</span>
          <button className={`seg ${filterTag === 'all' ? 'active' : ''}`} onClick={() => setFilterTag('all')}>All</button>
          {allTags.map(t => (
            <button key={t} className={`seg ${filterTag === t ? 'active' : ''}`} onClick={() => setFilterTag(t)}>{t}</button>
          ))}
        </div>
      )}

      {visible.length === 0 ? (
        <div className="empty-state">
          <div className="eyebrow">No recipes yet</div>
          <p className="muted" style={{ marginTop: 6 }}>
            Add your first recipe — name it, list ingredients with rough costs, and tag it for breakfast, lunch, or dinner.
          </p>
          <button className="btn primary" onClick={() => { setEditingRecipe(null); setOpenModal('recipe'); }}>
            + Add a recipe
          </button>
        </div>
      ) : (
        <div className="recipe-grid">
          {visible.map(r => (
            <RecipeCard key={r.id} recipe={r} pantry={pantry}
              onView={() => onView(r)}
              onEdit={() => { setEditingRecipe(r); setOpenModal('recipe'); }} />
          ))}
        </div>
      )}
    </div>
  );
}

function RecipeCard({ recipe, pantry, onView, onEdit }) {
  const total = recipeCost(recipe);
  const withPantry = recipeCostWithPantry(recipe, pantry);
  const tier = costTierIndex(total);
  const perPortion = recipe.portions ? total / recipe.portions : 0;
  const pantrySaves = withPantry < total;
  // Card opens the read view; the read view's "Edit" button drops into the modal.
  // (If a parent only passes onEdit, fall back to that for backwards compatibility.)
  const handleClick = onView || onEdit;
  return (
    <button className="recipe-card" onClick={handleClick}>
      <div className="recipe-card-head">
        <div className="recipe-name">{recipe.name}</div>
        <span className={`tier-badge tier-${tier}`} title={COST_TIER_NAMES[tier]}>{COST_TIERS[tier]}</span>
      </div>
      <div className="recipe-meal-tags">
        {(recipe.mealTags || []).map(m => (
          <span key={m} className="meal-chip">{MEAL_TYPE_LABELS[m]}</span>
        ))}
        {(recipe.tags || []).map(t => (
          <span key={t} className="recipe-tag-chip">{t}</span>
        ))}
        {(recipe.mealTags || []).length === 0 && (recipe.tags || []).length === 0 && (
          <span className="muted" style={{ fontSize: 11 }}>· untagged</span>
        )}
      </div>
      <div className="recipe-meta">
        <div className="recipe-meta-cell">
          <div className="eyebrow">Total</div>
          <div className="num">{fmt(total, { cents: false })}</div>
          {pantrySaves && (
            <div className="recipe-pantry-cost num" title="With your pantry">
              {fmt(withPantry, { cents: false })} <span className="muted">w/ pantry</span>
            </div>
          )}
        </div>
        <div className="recipe-meta-cell">
          <div className="eyebrow">Per portion</div>
          <div className="num">{recipe.portions ? fmt(perPortion, { cents: false }) : '—'}</div>
        </div>
        <div className="recipe-meta-cell">
          <div className="eyebrow">Portions</div>
          <div className="num">{recipe.portions || '—'}{recipe.leftovers ? ` (+${recipe.leftovers})` : ''}</div>
        </div>
      </div>
      {recipe.note && <div className="muted recipe-note">{recipe.note}</div>}
    </button>
  );
}

// ──────────────────────────────────────────────────
// PLAN
// ──────────────────────────────────────────────────
function PlanTab({ state, setState, planWeek, setPlanWeek }) {
  const { recipes = [], mealPlan = [], pantry = [], family = [], lockedWeeks = {} } = state;
  const dates = weekDates(planWeek);
  const todayIso = isoDate(TODAY);
  const isLocked = !!lockedWeeks[planWeek];
  const [pickerSlot, setPickerSlot] = React.useState(null); // { date, mealType }

  // A slot may hold multiple entries (e.g., separate dishes for different family members).
  const slotEntries = (date, mealType) =>
    mealPlan.filter(p => p.date === date && p.mealType === mealType);
  const recipeOf = (id) => recipes.find(r => r.id === id);

  const addEntry = (date, mealType, payload) => {
    // payload is either { recipeId, memberId } or { diningOut: true, note, memberId }
    setState(s => ({
      ...s,
      mealPlan: [...(s.mealPlan || []), {
        id: 'mp' + Date.now().toString(36),
        date, mealType,
        recipeId: payload.recipeId || null,
        memberId: payload.memberId || null,
        diningOut: !!payload.diningOut,
        note: payload.note || '',
      }],
    }));
  };

  const removeEntry = (id) => {
    setState(s => ({ ...s, mealPlan: (s.mealPlan || []).filter(p => p.id !== id) }));
  };

  const toggleLock = () => {
    setState(s => {
      const next = { ...(s.lockedWeeks || {}) };
      if (next[planWeek]) delete next[planWeek];
      else next[planWeek] = true;
      return { ...s, lockedWeeks: next };
    });
  };

  // Pantry-aware totals — what you'll actually spend this week given what's already in stock.
  // Dining-out entries don't carry a recipe cost.
  const totalCost = mealPlan
    .filter(p => dates.includes(p.date) && !p.diningOut)
    .reduce((s, p) => {
      const r = recipeOf(p.recipeId);
      return s + (r ? recipeCostWithPantry(r, pantry) : 0);
    }, 0);

  const weekEntries = mealPlan.filter(p => dates.includes(p.date));
  const filledCount = weekEntries.length;
  const diningOutCount = weekEntries.filter(p => p.diningOut).length;

  if (isLocked) {
    return (
      <PlanReadView
        dates={dates}
        planWeek={planWeek}
        setPlanWeek={setPlanWeek}
        slotEntries={slotEntries}
        recipeOf={recipeOf}
        family={family}
        pantry={pantry}
        todayIso={todayIso}
        totalCost={totalCost}
        filledCount={filledCount}
        diningOutCount={diningOutCount}
        onUnlock={toggleLock}
      />
    );
  }

  return (
    <div>
      <div className="plan-week-bar">
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, -7))}>‹ Prev</button>
        <div className="plan-week-label">
          <div className="eyebrow">Week of</div>
          <div className="display">{formatWeekRange(planWeek)}</div>
        </div>
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, 7))}>Next ›</button>
        <span style={{ flex: 1 }} />
        <button className="btn ghost" onClick={() => setPlanWeek(weekStartIso(isoDate(TODAY)))}>Today</button>
        <button className="btn primary" onClick={toggleLock} disabled={filledCount === 0}
          title={filledCount === 0 ? 'Plan a meal first' : 'Lock this week into a presentation view'}>
          ✓ Lock week
        </button>
      </div>

      <div className="plan-summary">
        <span><b>{filledCount}</b> meals planned{diningOutCount > 0 ? ` · ${diningOutCount} out` : ''} · est. <b className="num">{fmt(totalCost, { cents: false })}</b></span>
      </div>

      <div className="plan-grid">
        {dates.map(date => (
          <div key={date} className={`plan-day ${date === todayIso ? 'today' : ''}`}>
            <div className="plan-day-head">
              <span className="display">{weekdayShort(date)}</span>
              <span className="muted mono">{monthDay(date)}</span>
            </div>
            {MEAL_TYPES.map(mealType => {
              const entries = slotEntries(date, mealType);
              return (
                <div key={mealType} className={`plan-slot ${entries.length ? 'filled' : 'empty'}`}>
                  <span className="plan-slot-label">{MEAL_TYPE_LABELS[mealType]}</span>
                  {entries.map(e => {
                    const r = e.diningOut ? null : recipeOf(e.recipeId);
                    const m = e.memberId ? family.find(f => f.id === e.memberId) : null;
                    return (
                      <div key={e.id} className={`plan-entry ${e.diningOut ? 'dining-out' : ''}`}>
                        {m && (
                          <span className="plan-entry-member"
                            style={{ background: TAG_COLORS[m.color] }}
                            title={m.name}>{nameInitials(m.name)}</span>
                        )}
                        {e.diningOut ? (
                          <span className="plan-entry-name">
                            <span className="plan-entry-glyph" title="Dining out">⌖</span>
                            {e.note ? e.note : 'Dining out'}
                          </span>
                        ) : (
                          <>
                            <span className="plan-entry-name">{r ? r.name : <em className="muted">missing</em>}</span>
                            {r && <span className="plan-entry-cost num muted">{fmt(recipeCostWithPantry(r, pantry), { cents: false })}</span>}
                          </>
                        )}
                        <button type="button" className="plan-entry-x"
                          onClick={() => removeEntry(e.id)}
                          title="Remove">×</button>
                      </div>
                    );
                  })}
                  <button type="button" className="plan-slot-add"
                    onClick={() => setPickerSlot({ date, mealType })}>
                    {entries.length ? '+ Add another' : '+ Add'}
                  </button>
                </div>
              );
            })}
          </div>
        ))}
      </div>

      {pickerSlot && (
        <RecipePicker
          recipes={recipes}
          family={family}
          pantry={pantry}
          mealType={pickerSlot.mealType}
          onAdd={(payload) => addEntry(pickerSlot.date, pickerSlot.mealType, payload)}
          onClose={() => setPickerSlot(null)}
        />
      )}
    </div>
  );
}

// ──────────────────────────────────────────────────
// PLAN — read view (presentation, locked)
// ──────────────────────────────────────────────────
function PlanReadView({ dates, planWeek, setPlanWeek, slotEntries, recipeOf, family, pantry, todayIso, totalCost, filledCount, diningOutCount, onUnlock }) {
  return (
    <div className="plan-read">
      <div className="plan-week-bar">
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, -7))}>‹ Prev</button>
        <div className="plan-week-label">
          <div className="eyebrow">Locked week of</div>
          <div className="display">{formatWeekRange(planWeek)}</div>
        </div>
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, 7))}>Next ›</button>
        <span style={{ flex: 1 }} />
        <button className="btn ghost" onClick={onUnlock}>✎ Unlock to edit</button>
      </div>

      <div className="plan-read-hero">
        <div className="plan-read-hero-stat">
          <div className="eyebrow">Meals</div>
          <div className="display num">{filledCount}</div>
        </div>
        <div className="plan-read-hero-stat">
          <div className="eyebrow">Est. spend</div>
          <div className="display num">{fmt(totalCost, { cents: false })}</div>
        </div>
        {diningOutCount > 0 && (
          <div className="plan-read-hero-stat">
            <div className="eyebrow">Dining out</div>
            <div className="display num">{diningOutCount}</div>
          </div>
        )}
      </div>

      <div className="plan-read-days">
        {dates.map(date => {
          const dayEntries = MEAL_TYPES.flatMap(mt => slotEntries(date, mt).map(e => ({ ...e, mealType: mt })));
          if (dayEntries.length === 0) {
            return (
              <div key={date} className={`plan-read-day empty ${date === todayIso ? 'today' : ''}`}>
                <div className="plan-read-day-head">
                  <div className="display">{weekdayLong(date)}</div>
                  <div className="muted mono">{monthDay(date)}</div>
                </div>
                <div className="plan-read-empty muted">Nothing planned</div>
              </div>
            );
          }
          return (
            <div key={date} className={`plan-read-day ${date === todayIso ? 'today' : ''}`}>
              <div className="plan-read-day-head">
                <div className="display">{weekdayLong(date)}</div>
                <div className="muted mono">{monthDay(date)}</div>
              </div>
              <div className="plan-read-meals">
                {dayEntries.map(e => {
                  const r = e.diningOut ? null : recipeOf(e.recipeId);
                  const m = e.memberId ? family.find(f => f.id === e.memberId) : null;
                  return (
                    <div key={e.id} className={`plan-read-meal ${e.diningOut ? 'dining-out' : ''}`}>
                      <div className="plan-read-meal-type eyebrow">{MEAL_TYPE_LABELS[e.mealType]}</div>
                      <div className="plan-read-meal-body">
                        <div className="plan-read-meal-title">
                          {e.diningOut ? (
                            <>
                              <span className="plan-read-glyph">⌖</span>
                              <span className="plan-read-dining-label">Dining out</span>
                              {e.note && <span className="plan-read-dining-note">· {e.note}</span>}
                            </>
                          ) : (
                            r ? r.name : <em className="muted">missing recipe</em>
                          )}
                        </div>
                        <div className="plan-read-meal-meta">
                          {m && (
                            <span className="plan-read-member">
                              <span className="member-chip" style={{ background: TAG_COLORS[m.color] }}>{nameInitials(m.name)}</span>
                              {m.name}
                            </span>
                          )}
                          {!e.diningOut && r && (
                            <span className="num muted">{fmt(recipeCostWithPantry(r, pantry), { cents: false })}</span>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function RecipePicker({ recipes, family = [], pantry, mealType, onAdd, onClose }) {
  const [requestClose, sheetClass] = useSheet(onClose);
  const [search, setSearch] = React.useState('');
  const [memberId, setMemberId] = React.useState('');
  const [diningOut, setDiningOut] = React.useState(false);
  const [diningNote, setDiningNote] = React.useState('');
  const handleAdd = (payload) => { onAdd(payload); requestClose(); };
  const allTags = React.useMemo(() => {
    const set = new Set();
    recipes.forEach(r => (r.tags || []).forEach(t => set.add(t)));
    return [...set].sort();
  }, [recipes]);

  // Search matches against name + custom tags + meal-type tags.
  const matchesSearch = (r) => {
    const q = search.trim().toLowerCase();
    if (!q) return true;
    if (r.name.toLowerCase().includes(q)) return true;
    if ((r.tags || []).some(t => t.toLowerCase().includes(q))) return true;
    if ((r.mealTags || []).some(t => t.toLowerCase().includes(q))) return true;
    return false;
  };

  // Strict match on the slot's meal type, so "Breakfast recipes" only contains
  // recipes explicitly tagged for breakfast — untagged recipes fall through to
  // the catch-all "Other recipes" section below.
  const matching = recipes
    .filter(r => (r.mealTags || []).includes(mealType))
    .filter(matchesSearch);
  const others = recipes
    .filter(r => !(r.mealTags || []).includes(mealType))
    .filter(matchesSearch);

  // Tier reflects the recipe's evergreen cost; the line price reflects what *this week* costs.
  const renderRow = (r) => (
    <button key={r.id} className="picker-row"
      onClick={() => handleAdd({ recipeId: r.id, memberId })}>
      <span className="picker-name">
        {r.name}
        {(r.tags || []).length > 0 && (
          <span className="picker-tags">
            {(r.tags || []).slice(0, 3).map(t => <span key={t} className="picker-tag">{t}</span>)}
          </span>
        )}
      </span>
      <span className={`tier-badge tier-${costTierIndex(recipeCost(r))}`}>{COST_TIERS[costTierIndex(recipeCost(r))]}</span>
      <span className="num muted">{fmt(recipeCostWithPantry(r, pantry), { cents: false })}</span>
    </button>
  );

  const submitDiningOut = () => {
    handleAdd({ diningOut: true, note: diningNote.trim(), memberId });
  };

  return (
    <div className={`modal-backdrop ${sheetClass}`} onClick={requestClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-header">
          <div className="modal-sub">{MEAL_TYPE_LABELS[mealType]}</div>
          <div className="modal-title">Add a meal</div>
        </div>
        <div className="modal-body">
          <div className="picker-mode-toggle">
            <button type="button"
              className={`picker-mode ${!diningOut ? 'active' : ''}`}
              onClick={() => setDiningOut(false)}>
              <span className="picker-mode-glyph">◫</span>
              <span>Cook a recipe</span>
            </button>
            <button type="button"
              className={`picker-mode ${diningOut ? 'active' : ''}`}
              onClick={() => setDiningOut(true)}>
              <span className="picker-mode-glyph">⌖</span>
              <span>Dining out</span>
            </button>
          </div>

          {family.length > 0 && (
            <div className="field" style={{ marginTop: 14 }}>
              <span className="field-label">{diningOut ? 'Who · optional' : 'Cooking for · optional'}</span>
              <div className="member-picker">
                <button type="button"
                  className={`member-pick ${!memberId ? 'selected' : ''}`}
                  onClick={() => setMemberId('')}>
                  <span className="member-chip" style={{ background: 'var(--paper-3)', color: 'var(--ink-3)' }}>—</span>
                  <span>Everyone</span>
                </button>
                {family.map(m => (
                  <button key={m.id} type="button"
                    className={`member-pick ${memberId === m.id ? 'selected' : ''}`}
                    onClick={() => setMemberId(m.id)}>
                    <span className="member-chip" style={{ background: TAG_COLORS[m.color] }}>{nameInitials(m.name)}</span>
                    <span>{m.name}</span>
                  </button>
                ))}
              </div>
            </div>
          )}

          {diningOut ? (
            <div className="field" style={{ marginTop: 14 }}>
              <span className="field-label">Restaurant or note · optional</span>
              <input className="input" autoFocus
                placeholder="e.g. Sushi spot, Mom's place"
                value={diningNote}
                onChange={e => setDiningNote(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && submitDiningOut()} />
            </div>
          ) : (
            <>
              <input className="input" autoFocus style={{ marginTop: 14 }}
                placeholder="Search by name, tag, or meal type…"
                value={search}
                onChange={e => setSearch(e.target.value)} />

              {allTags.length > 0 && (
                <div className="row gap-sm" style={{ marginTop: 8, flexWrap: 'wrap' }}>
                  {allTags.slice(0, 8).map(t => (
                    <button key={t} type="button"
                      className={`seg ${search.toLowerCase() === t.toLowerCase() ? 'active' : ''}`}
                      onClick={() => setSearch(search.toLowerCase() === t.toLowerCase() ? '' : t)}>
                      {t}
                    </button>
                  ))}
                </div>
              )}

              <div className="picker-list" style={{ marginTop: 14 }}>
                {matching.length > 0 && (
                  <>
                    <div className="eyebrow" style={{ padding: '0 4px 4px' }}>
                      {MEAL_TYPE_LABELS[mealType]} recipes
                    </div>
                    {matching.map(renderRow)}
                  </>
                )}
                {others.length > 0 && (
                  <>
                    <div className="eyebrow" style={{ marginTop: matching.length ? 12 : 0, padding: '0 4px 4px' }}>
                      {matching.length > 0 ? 'Other recipes' : 'All recipes'}
                    </div>
                    {others.map(renderRow)}
                  </>
                )}
                {recipes.length === 0 && (
                  <div className="muted" style={{ padding: 24, textAlign: 'center' }}>
                    No recipes yet. Add one from the Recipes tab.
                  </div>
                )}
                {recipes.length > 0 && matching.length + others.length === 0 && (
                  <div className="muted" style={{ padding: 24, textAlign: 'center' }}>
                    No recipes match that search.
                  </div>
                )}
              </div>
            </>
          )}
        </div>
        <div className="modal-footer">
          <button className="btn ghost" onClick={requestClose}>Close</button>
          {diningOut && <button className="btn primary" onClick={submitDiningOut}>Add dining out</button>}
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────
// SHOPPING LIST
// ──────────────────────────────────────────────────
function ShoppingTab({ state, setState, planWeek, setPlanWeek }) {
  const { recipes = [], mealPlan = [], pantry = [], shopping = {} } = state;
  const dates = weekDates(planWeek);
  const weekPlan = mealPlan.filter(p => dates.includes(p.date));
  const weekShop = shopping[planWeek] || { checked: {}, cleared: {}, extras: [] };
  const checked = weekShop.checked || {};
  const cleared = weekShop.cleared || {};
  const extras = weekShop.extras || [];

  const derivedItems = buildShoppingList(weekPlan, recipes, pantry);
  const extraItems = extras.map(e => ({
    name: e.name,
    qty: e.qty || '',
    unit: e.unit || '',
    estCost: parseFloat(e.estCost) || 0,
    hasQty: !!e.qty,
    isExtra: true,
  }));
  const visibleItems = [...derivedItems, ...extraItems]
    .filter(i => !cleared[i.name.toLowerCase()]);

  const total = visibleItems.reduce((s, i) => s + i.estCost, 0);
  const checkedCount = visibleItems.filter(i => checked[i.name.toLowerCase()]).length;

  // Toggle is purely visual — checking off a pantry restock or bulk-purchase
  // line used to mutate the pantry immediately, which made the row vanish from
  // the list mid-shop. Pantry side effects now defer to "Clear checked" once
  // the user is home.
  const toggle = (key) => {
    setState(s => {
      const prev = s.shopping?.[planWeek] || { checked: {}, cleared: {}, extras: [] };
      const nowChecked = !prev.checked[key];
      return {
        ...s,
        shopping: {
          ...(s.shopping || {}),
          [planWeek]: { ...prev, checked: { ...prev.checked, [key]: nowChecked } },
        },
      };
    });
  };

  const clearChecked = () => {
    const itemByKey = new Map(visibleItems.map(i => [i.name.toLowerCase(), i]));
    setState(s => {
      const prev = s.shopping?.[planWeek] || { checked: {}, cleared: {}, extras: [] };
      const checkedKeys = Object.keys(prev.checked).filter(k => prev.checked[k]);
      if (!checkedKeys.length) return s;

      let nextPantry = s.pantry || [];
      checkedKeys.forEach(key => {
        const item = itemByKey.get(key);
        if (!item) return;
        if (item.fromPantry) {
          // Restock — flip the pantry's low flag back to in-stock.
          nextPantry = nextPantry.map(p =>
            (p.name || '').toLowerCase().trim() === key ? { ...p, lowOnStock: false } : p);
        } else if (item.leftoverToPantry) {
          // Bulk purchase — stock the pantry (or refresh it).
          const exists = nextPantry.some(p => (p.name || '').toLowerCase().trim() === key);
          nextPantry = exists
            ? nextPantry.map(p => (p.name || '').toLowerCase().trim() === key
                ? { ...p, lowOnStock: false } : p)
            : [...nextPantry, { id: 'pn' + Date.now().toString(36), name: item.name, lowOnStock: false, category: 'other' }];
        }
      });

      const newCleared = { ...prev.cleared };
      checkedKeys.forEach(k => { newCleared[k] = true; });
      const newExtras = (prev.extras || []).filter(e => !prev.checked[e.name.toLowerCase()]);

      return {
        ...s,
        pantry: nextPantry,
        shopping: {
          ...(s.shopping || {}),
          [planWeek]: { checked: {}, cleared: newCleared, extras: newExtras },
        },
      };
    });
  };

  const [extraDraft, setExtraDraft] = React.useState('');
  const addExtra = () => {
    const name = extraDraft.trim();
    if (!name) return;
    const key = name.toLowerCase();
    setState(s => {
      const prev = s.shopping?.[planWeek] || { checked: {}, cleared: {}, extras: [] };
      const dup = (prev.extras || []).some(e => e.name.toLowerCase() === key)
        || derivedItems.some(d => d.name.toLowerCase() === key);
      const newCleared = { ...prev.cleared };
      delete newCleared[key]; // un-dismiss if previously cleared
      if (dup) {
        return { ...s, shopping: { ...(s.shopping || {}), [planWeek]: { ...prev, cleared: newCleared } } };
      }
      return {
        ...s,
        shopping: {
          ...(s.shopping || {}),
          [planWeek]: {
            ...prev,
            cleared: newCleared,
            extras: [...(prev.extras || []), { id: 'sx' + Date.now().toString(36), name }],
          },
        },
      };
    });
    setExtraDraft('');
  };

  return (
    <div>
      <div className="plan-week-bar">
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, -7))}>‹ Prev</button>
        <div className="plan-week-label">
          <div className="eyebrow">Shopping for</div>
          <div className="display">{formatWeekRange(planWeek)}</div>
        </div>
        <button className="btn ghost" onClick={() => setPlanWeek(addDaysIso(planWeek, 7))}>Next ›</button>
        <span style={{ flex: 1 }} />
        <button className="btn ghost" onClick={() => setPlanWeek(weekStartIso(isoDate(TODAY)))}>Today</button>
      </div>

      <div className="shopping-summary">
        <div>
          <div className="eyebrow">Items</div>
          <div className="display num">{visibleItems.length}</div>
        </div>
        <div>
          <div className="eyebrow">Estimated total</div>
          <div className="display num">{fmt(total, { cents: false })}</div>
        </div>
        <div>
          <div className="eyebrow">Checked off</div>
          <div className="display num">{checkedCount}/{visibleItems.length}</div>
        </div>
      </div>

      <div className="shop-add-row">
        <input className="input" style={{ flex: 1 }}
          placeholder="Add an item — e.g. Paper towels"
          value={extraDraft}
          onChange={e => setExtraDraft(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && addExtra()} />
        <button className="btn primary" onClick={addExtra} disabled={!extraDraft.trim()}>Add</button>
        <button className="btn ghost" onClick={clearChecked} disabled={checkedCount === 0}
          title="Remove all checked items from this list">
          Clear checked
        </button>
      </div>

      {visibleItems.length === 0 ? (
        <div className="empty-state">
          <div className="eyebrow">Nothing to buy</div>
          <p className="muted" style={{ marginTop: 6 }}>
            Plan some meals for this week — or add items above — and they'll collect here, minus anything in your pantry.
          </p>
        </div>
      ) : (
        <div className="card list-card" style={{ marginTop: 18 }}>
          <div className="card-section">
            Ingredients
            <span className="eyebrow num">{fmt(total, { cents: false })}</span>
          </div>
          {visibleItems.map(i => {
            const key = i.name.toLowerCase();
            const isChecked = !!checked[key];
            return (
              <button key={key}
                className={`shop-row ${isChecked ? 'checked' : ''} ${i.fromPantry ? 'from-pantry' : ''} ${i.leftoverToPantry ? 'has-bulk' : ''} ${i.isExtra ? 'is-extra' : ''}`}
                onClick={() => toggle(key)}>
                <span className="shop-check">{isChecked ? '✓' : ''}</span>
                <span className="shop-name">
                  {i.name}
                  {i.fromPantry && <span className="shop-pantry-tag">pantry</span>}
                  {i.leftoverToPantry && !i.fromPantry && <span className="shop-bulk-tag">bulk · stocks pantry</span>}
                  {i.isExtra && <span className="shop-extra-tag">added</span>}
                </span>
                <span className="shop-qty muted">
                  {i.hasQty && i.qty ? `${i.qty}${i.unit ? ' ' + i.unit : ''}` : (i.unit || '')}
                </span>
                <span className="shop-cost num muted">{i.estCost > 0 ? fmt(i.estCost, { cents: false }) : ''}</span>
              </button>
            );
          })}
        </div>
      )}

      {pantry.length > 0 && (() => {
        const inStock = pantry.filter(p => !p.lowOnStock).length;
        const low = pantry.length - inStock;
        return (
          <div className="muted mono" style={{ fontSize: 11, marginTop: 14, textAlign: 'center' }}>
            {inStock} pantry staple{inStock === 1 ? '' : 's'} filtered out
            {low > 0 && ` · ${low} flagged for restock`}
            {' · edit on Pantry tab'}
          </div>
        );
      })()}
    </div>
  );
}

// ──────────────────────────────────────────────────
// PANTRY
// ──────────────────────────────────────────────────
function PantryTab({ state, setState }) {
  const { pantry = [] } = state;
  const [name, setName] = React.useState('');
  const [newCategory, setNewCategory] = React.useState('dry-goods');
  const [search, setSearch] = React.useState('');
  const [activeFilter, setActiveFilter] = React.useState('all'); // 'all' | 'low' | category id
  // picker state: null | { kind: 'item', id } | { kind: 'new' }
  const [picker, setPicker] = React.useState(null);

  // Anything on file before category was a thing falls into "Other".
  const categoryOf = (p) => p.category || 'other';

  const add = () => {
    const v = name.trim();
    if (!v) return;
    if (pantry.some(p => p.name.toLowerCase() === v.toLowerCase())) {
      setName('');
      return;
    }
    setState(s => ({
      ...s,
      pantry: [...(s.pantry || []), {
        id: 'pn' + Date.now().toString(36),
        name: v, lowOnStock: false, category: newCategory,
      }],
    }));
    setName('');
  };

  const remove = (id) => setState(s => ({ ...s, pantry: (s.pantry || []).filter(p => p.id !== id) }));
  const toggleLow = (id) => setState(s => ({
    ...s,
    pantry: (s.pantry || []).map(p => p.id === id ? { ...p, lowOnStock: !p.lowOnStock } : p),
  }));
  const setCategory = (id, category) => setState(s => ({
    ...s,
    pantry: (s.pantry || []).map(p => p.id === id ? { ...p, category } : p),
  }));

  const lowCount = pantry.filter(p => p.lowOnStock).length;

  // Apply search + filter, then group by category in PANTRY_CATEGORIES order.
  const q = search.trim().toLowerCase();
  const filtered = pantry.filter(p => {
    if (q && !p.name.toLowerCase().includes(q)) return false;
    if (activeFilter === 'low') return p.lowOnStock;
    if (activeFilter !== 'all' && categoryOf(p) !== activeFilter) return false;
    return true;
  });

  const grouped = PANTRY_CATEGORIES.map(c => ({
    ...c,
    items: filtered
      .filter(p => categoryOf(p) === c.id)
      .sort((a, b) => a.name.localeCompare(b.name)),
  })).filter(g => g.items.length > 0);

  // For the filter pill row, only show categories that actually have items.
  const presentCategories = PANTRY_CATEGORIES.filter(c => pantry.some(p => categoryOf(p) === c.id));

  return (
    <div>
      <div className="muted mono" style={{ fontSize: 11, marginBottom: 12, lineHeight: 1.6 }}>
        Staples you usually keep on hand. Pantry items are filtered out of shopping lists. Tap the mark to flag one as <b>low / out</b> and it'll reappear on the list.
      </div>

      <div className="pantry-add-row">
        <input className="input" style={{ flex: 1, minWidth: 160 }}
          placeholder="e.g. Olive oil"
          value={name}
          onChange={e => setName(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && add()} />
        <button type="button" className="pantry-add-cat-btn"
          onClick={() => setPicker({ kind: 'new' })}
          title="Pick category">
          {PANTRY_CATEGORY_LABELS[newCategory] || 'Other'}
          <span className="pantry-add-cat-caret" aria-hidden="true">▾</span>
        </button>
        <button className="btn primary" onClick={add} disabled={!name.trim()}>Add</button>
      </div>

      {pantry.length === 0 ? (
        <div className="empty-state">
          <div className="eyebrow">Pantry is empty</div>
          <p className="muted" style={{ marginTop: 6 }}>Add staple ingredients you always have on hand.</p>
        </div>
      ) : (
        <>
          {lowCount > 0 && (
            <div className="pantry-low-banner">
              <span className="eyebrow">Restock</span>
              <span>{lowCount} item{lowCount === 1 ? '' : 's'} flagged — added to your shopping list.</span>
            </div>
          )}

          <div className="pantry-toolbar">
            <input className="input pantry-search"
              placeholder="Search pantry…"
              value={search} onChange={e => setSearch(e.target.value)} />
            <div className="pantry-filter-row">
              <button type="button"
                className={`pantry-filter ${activeFilter === 'all' ? 'active' : ''}`}
                onClick={() => setActiveFilter('all')}>
                All <span className="muted num">{pantry.length}</span>
              </button>
              {lowCount > 0 && (
                <button type="button"
                  className={`pantry-filter low ${activeFilter === 'low' ? 'active' : ''}`}
                  onClick={() => setActiveFilter('low')}>
                  Low <span className="muted num">{lowCount}</span>
                </button>
              )}
              {presentCategories.map(c => {
                const count = pantry.filter(p => categoryOf(p) === c.id).length;
                return (
                  <button key={c.id} type="button"
                    className={`pantry-filter ${activeFilter === c.id ? 'active' : ''}`}
                    onClick={() => setActiveFilter(c.id)}>
                    {c.label} <span className="muted num">{count}</span>
                  </button>
                );
              })}
            </div>
          </div>

          {grouped.length === 0 ? (
            <div className="empty-state">
              <div className="eyebrow">No matches</div>
              <p className="muted" style={{ marginTop: 6 }}>Try a different search or filter.</p>
            </div>
          ) : grouped.map(group => (
            <section key={group.id} className="pantry-section">
              <div className="pantry-section-head">
                <span className="pantry-section-title">{group.label}</span>
                <span className="muted num">{group.items.length}</span>
              </div>
              <div className="pantry-grid">
                {group.items.map(p => (
                  <div key={p.id}
                    role="button"
                    tabIndex={0}
                    className={`pantry-chip ${p.lowOnStock ? 'low' : ''}`}
                    onClick={() => toggleLow(p.id)}
                    onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleLow(p.id); } }}
                    title={p.lowOnStock ? 'Tap to mark in stock' : 'Tap to mark low / out'}>
                    <span className="pantry-low-toggle" aria-hidden="true">
                      {p.lowOnStock ? '!' : '✓'}
                    </span>
                    <span className="pantry-name">{p.name}</span>
                    <button type="button" className="pantry-cat-tag"
                      onClick={e => { e.stopPropagation(); setPicker({ kind: 'item', id: p.id }); }}
                      title="Change category">
                      {PANTRY_CATEGORY_LABELS[categoryOf(p)] || 'Other'}
                    </button>
                    <button type="button" className="pantry-x"
                      onClick={e => { e.stopPropagation(); remove(p.id); }}
                      title="Remove">×</button>
                  </div>
                ))}
              </div>
            </section>
          ))}
        </>
      )}

      {picker && (() => {
        let sub, title, current, apply;
        if (picker.kind === 'item') {
          const editing = pantry.find(p => p.id === picker.id);
          if (!editing) return null;
          sub = editing.name;
          title = 'Choose a category';
          current = categoryOf(editing);
          apply = (id) => setCategory(editing.id, id);
        } else {
          sub = name.trim() ? `New · ${name.trim()}` : 'New pantry item';
          title = 'Choose a category';
          current = newCategory;
          apply = (id) => setNewCategory(id);
        }
        return (
          <Sheet onClose={() => setPicker(null)} modalStyle={{ width: 380 }}>
            {(closeSheet) => (
              <>
                <div className="modal-header">
                  <div className="modal-sub">{sub}</div>
                  <div className="modal-title">{title}</div>
                </div>
                <div className="modal-body">
                  <div className="pantry-cat-picker">
                    {PANTRY_CATEGORIES.map(c => (
                      <button key={c.id} type="button"
                        className={`pantry-cat-option ${current === c.id ? 'selected' : ''}`}
                        onClick={() => { apply(c.id); closeSheet(); }}>
                        <span className="pantry-cat-option-label">{c.label}</span>
                        {current === c.id && <span className="pantry-cat-option-check">✓</span>}
                      </button>
                    ))}
                  </div>
                </div>
                <div className="modal-footer">
                  <button className="btn ghost" onClick={closeSheet}>Cancel</button>
                </div>
              </>
            )}
          </Sheet>
        );
      })()}
    </div>
  );
}

// ──────────────────────────────────────────────────
// RECIPE VIEW (read-only) — for referring back to a recipe's instructions
// ──────────────────────────────────────────────────
function RecipeView({ recipe, pantry, onClose, onEdit }) {
  const [requestClose, sheetClass] = useSheet(onClose);
  const total = recipeCost(recipe);
  const withPantry = recipeCostWithPantry(recipe, pantry);
  const tier = costTierIndex(total);
  const perPortion = recipe.portions ? total / recipe.portions : 0;
  const pantrySaves = withPantry < total;

  // Esc closes the view.
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') requestClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [requestClose]);

  return (
    <div className={`modal-backdrop ${sheetClass}`} onClick={requestClose}>
      <div className="modal recipe-view" onClick={e => e.stopPropagation()}>
        <div className="modal-header">
          <div className="modal-sub">Recipe</div>
          <div className="modal-title">{recipe.name}</div>
          <div className="recipe-view-tagrow">
            {(recipe.mealTags || []).map(m => (
              <span key={m} className="meal-chip">{MEAL_TYPE_LABELS[m]}</span>
            ))}
            {(recipe.tags || []).map(t => (
              <span key={t} className="recipe-tag-chip">{t}</span>
            ))}
            <span className={`tier-badge tier-${tier}`} title={COST_TIER_NAMES[tier]}>{COST_TIERS[tier]}</span>
          </div>
        </div>

        <div className="modal-body">
          <div className="recipe-view-meta">
            <div>
              <div className="eyebrow">Total</div>
              <div className="num display">{fmt(total, { cents: false })}</div>
              {pantrySaves && (
                <div className="recipe-pantry-cost num">
                  {fmt(withPantry, { cents: false })} <span className="muted">w/ pantry</span>
                </div>
              )}
            </div>
            <div>
              <div className="eyebrow">Per portion</div>
              <div className="num display">{recipe.portions ? fmt(perPortion, { cents: false }) : '—'}</div>
            </div>
            <div>
              <div className="eyebrow">Portions</div>
              <div className="num display">{recipe.portions || '—'}{recipe.leftovers ? ` (+${recipe.leftovers})` : ''}</div>
            </div>
          </div>

          {(recipe.ingredients || []).length > 0 && (
            <div className="recipe-view-section">
              <div className="recipe-view-section-head">
                <span className="eyebrow">Ingredients</span>
                <span className="muted mono" style={{ fontSize: 10.5 }}>{recipe.ingredients.length}</span>
              </div>
              <ul className="recipe-view-ingredients">
                {recipe.ingredients.map((ing, idx) => {
                  const qty = ing.qty ? `${ing.qty}${ing.unit ? ' ' + ing.unit : ''}` : (ing.unit || '');
                  const cost = parseFloat(ing.estCost) || 0;
                  return (
                    <li key={idx} className="recipe-view-ingredient">
                      <span className="recipe-view-ing-qty num muted">{qty || '—'}</span>
                      <span className="recipe-view-ing-name">
                        {ing.name}
                        {ing.leftoverToPantry && <span className="shop-bulk-tag" style={{ marginLeft: 8 }}>bulk</span>}
                      </span>
                      <span className="recipe-view-ing-cost num muted">{cost > 0 ? fmt(cost, { cents: false }) : ''}</span>
                    </li>
                  );
                })}
              </ul>
            </div>
          )}

          {recipe.note && recipe.note.trim() && (
            <div className="recipe-view-section">
              <div className="recipe-view-section-head">
                <span className="eyebrow">Instructions</span>
              </div>
              <div className="recipe-view-instructions">{recipe.note}</div>
            </div>
          )}

          {!recipe.note && (recipe.ingredients || []).length === 0 && (
            <div className="muted" style={{ padding: '24px 0', textAlign: 'center', fontSize: 12 }}>
              No ingredients or instructions yet — tap Edit to add some.
            </div>
          )}
        </div>

        <div className="modal-footer">
          <button className="btn ghost" onClick={requestClose}>Close</button>
          <button className="btn primary" onClick={onEdit}>Edit recipe</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { MealsScreen, RecipeView });
