/* ============================================================
   Glafia Dex — screens (box UI)
   Exports: BoxScreen, RecipeFull, SearchScreen, AddEditScreen, BoxListModal
============================================================ */
const EFFORT = ['', 'Easy', 'Easy', 'Medium', 'Involved', 'Project'];
const SPICE  = ['', 'Mild', 'Mild', 'Medium', 'Hot', 'Fiery'];
const FALLBACK_COLOR = '#c9a36a';
const BASICS = ['oil','salt','pepper','water','sugar','stock','flour'];
function isBasic(name){ const n = (name||'').toLowerCase(); return BASICS.some(b => n.includes(b)); }
function coverStyle(r, api){ return { background: hexTint(api.colorOf(r.id), 0.66) }; }
function coverFill(r, api){
  if (r.photo) return { style: { backgroundImage: `url("${(r.photo + '').replace(/"/g, '%22')}")`, backgroundSize: 'cover', backgroundPosition: 'center' }, content: '' };
  return { style: coverStyle(r, api), content: r.emoji };
}
const STOP_WORDS = new Set(['fresh','chopped','ground','large','small','dried','plain','whole','ripe','firm','silken','baby','cherry','extra','virgin','light','dark','self','raising','caster','granulated','brown','white','black','your','some','into','with','sauce','paste','powder','leaves','fillet']);
function sigWords(name){
  return (name || '').toLowerCase().replace(/[^a-z\s]/g, ' ').split(/\s+/).filter(w => w.length >= 4 && !STOP_WORDS.has(w));
}
function stepIngredients(step, ingredients){
  const s = ' ' + (step || '').toLowerCase().replace(/[^a-z\s]/g, ' ').replace(/\s+/g, ' ') + ' ';
  const out = [];
  ingredients.forEach(ing => {
    const words = sigWords(ing.name);
    if (!words.length) return;
    const hit = words.some(w => s.includes(' ' + w + ' ') || s.includes(' ' + w + 's ') || s.includes(' ' + w.replace(/ies$/, 'y') + ' ') || s.includes(' ' + w.replace(/s$/, '') + ' '));
    if (hit) out.push(ing);
  });
  return out;
}

/* ============ LEFT: party / favourites ============ */
function PartyList({ api }){
  const favs = [...api.favourites].map(api.recipeById).filter(Boolean);
  const [collapsed, setCollapsed] = React.useState(api.isMobile);
  React.useEffect(() => { setCollapsed(api.isMobile); }, [api.isMobile]);
  return (
    <div className={'party' + (collapsed ? ' collapsed' : '')}>
      <button className="partytab" onClick={() => setCollapsed(c => !c)}>
        <span>★ Favourites</span>
        <span className="ptcount">{favs.length}</span>
        <span className="ptchev">{collapsed ? '▾' : '▴'}</span>
      </button>
      {!collapsed && favs.length === 0 && (
        <div className="hearttip">Tap the <span className="heartstar">★</span> on any recipe to pin it to your party here.</div>
      )}
      {!collapsed && favs.map(r => (
        <div key={r.id} className={'pcard' + (api.selectedId === r.id ? ' sel' : '')} onClick={() => api.isMobile ? api.openRecipe(r.id) : api.setSelected(r.id)}>
          <span className="pcc" style={coverFill(r, api).style}>{coverFill(r, api).content}</span>
          <span className="pci">
            <span className="nm">{r.name}</span>
            <span className="lv">⏱ {r.time} min · serves {r.serves}</span>
          </span>
        </div>
      ))}
    </div>
  );
}

/* ============ MIDDLE: box grid / list + pager ============ */
function BoxGrid({ api }){
  const [mode, setMode] = React.useState('grid'); // grid | list
  const boxes = api.pagerBoxes;
  const idx = Math.min(api.collIndex, boxes.length - 1);
  const coll = boxes[idx];
  const members = coll.members.map(api.recipeById).filter(Boolean);
  const cells = Math.max(20, Math.ceil((members.length + 1) / 5) * 5);

  return (
    <div className="boxmid">
      <div className="pager">
        <button className="pagerbtn" onClick={() => api.setCollIndex((idx - 1 + boxes.length) % boxes.length)} aria-label="previous box">◀</button>
        <div className="pagername">
          <span className="pn-em">{coll.emoji}</span>
          {coll.virtual
            ? <span className="pn-txt" style={{ width: 'auto' }}>{coll.name}</span>
            : <input className="pn-txt" value={coll.name}
                onChange={e => api.renameCollection(coll.id, e.target.value)}
                onFocus={e => e.target.select()}
                style={{ width: Math.min(190, Math.max(70, coll.name.length * 11)) + 'px' }} />}
          {!coll.virtual && <span className="pn-edit">✎</span>}
        </div>
        <button className="pagerbtn" onClick={() => api.setCollIndex((idx + 1) % boxes.length)} aria-label="next box">▶</button>
      </div>
      <div className="boxcount">
        {members.length} {members.length === 1 ? 'recipe' : 'recipes'}{coll.virtual ? ' in your whole dex' : ' in this box'}
        <button className="viewtoggle" onClick={() => setMode(m => m === 'grid' ? 'list' : 'grid')}>
          {mode === 'grid' ? '☰ list' : '▦ grid'}
        </button>
      </div>

      <div className="boxgridwrap">
        {mode === 'grid' ? (
          <div className="boxgrid">
            {Array.from({ length: cells }, (_, i) => {
              const r = members[i];
              if (!r) return <div key={i} className="cell empty"></div>;
              const sel = api.selectedId === r.id;
              return (
                <div key={i} className={'cell full' + (sel ? ' sel' : '')} onClick={() => api.isMobile ? api.openRecipe(r.id) : api.setSelected(r.id)} title={r.name}>
                  {sel && <span className="heart">♥</span>}
                  {api.favourites.has(r.id) && <span className="minifav">⭐</span>}
                  <span className="tile" style={coverFill(r, api).style}>{!r.photo && <span className="tdots"></span>}{coverFill(r, api).content}</span>
                </div>
              );
            })}
          </div>
        ) : (
          <div className="boxlist">
            {members.length === 0 && <div className="emptyhint" style={{ paddingTop: 30 }}>This box is empty.</div>}
            {members.map(r => (
              <div key={r.id} className={'lrow' + (api.selectedId === r.id ? ' sel' : '')}
                onClick={() => api.isMobile ? api.openRecipe(r.id) : api.setSelected(r.id)} onDoubleClick={() => api.openRecipe(r.id)}>
                <span className="lmini" style={coverFill(r, api).style}>{coverFill(r, api).content}</span>
                <span className="linfo">
                  <span className="ln">{r.name}{api.favourites.has(r.id) && <span className="lstar"> ⭐</span>}</span>
                  <span className="lmeta">⏱ {r.time} min · serves {r.serves} · {r.steps.length} moves</span>
                </span>
                <button className="lopen" onClick={(e) => { e.stopPropagation(); api.openRecipe(r.id); }}>Open ▶</button>
              </div>
            ))}
          </div>
        )}
      </div>

      <div className="boxactions">
        <button className="boxbtn" onClick={api.openBoxList}><span className="bi">▦</span> Box List</button>
        <button className="boxbtn" onClick={() => api.startAdd()}><span className="bi">＋</span> Add recipe</button>
      </div>
    </div>
  );
}

/* ============ add-to-box popover ============ */
function BoxAdder({ recipe, api }){
  const [open, setOpen] = React.useState(false);
  const [text, setText] = React.useState('');
  const available = api.collections.filter(c => !c.members.includes(recipe.id));
  function pick(id){ api.toggleMember(id, recipe.id); setOpen(false); }
  function create(){ const t = text.trim(); if (!t) return; const id = api.addCollection(t); api.toggleMember(id, recipe.id); setText(''); setOpen(false); }
  return (
    <span style={{ position: 'relative' }}>
      <button className="addtag" onClick={() => setOpen(o => !o)}>＋ box</button>
      {open && (
        <div className="tagpop" onClick={e => e.stopPropagation()}>
          <div className="ti">
            <input autoFocus value={text} placeholder="new box, e.g. Winter nights"
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter') create(); }} />
            <button className="sendbtn" style={{ width: 36, height: 36 }} onClick={create}>＋</button>
          </div>
          <div className="opts">
            {available.length === 0 && <span style={{ color: 'var(--ink2)', fontWeight: 700, fontSize: 12 }}>In every box already!</span>}
            {available.map(c => (
              <button key={c.id} className="opt" style={{ background: c.color || FALLBACK_COLOR }} onClick={() => pick(c.id)}>{c.emoji} {c.name}</button>
            ))}
          </div>
        </div>
      )}
    </span>
  );
}

/* ============ RIGHT: detail panel ============ */
function DetailPanel({ api }){
  const r = api.selectedId ? api.recipeById(api.selectedId) : null;
  if (!r) return (
    <div className="detail"><div className="dpanel"><div className="dempty">
      <div style={{ fontSize: 40 }}>🍽️</div>
      Pick a recipe to see its card here.
    </div></div></div>
  );
  const boxes = api.boxesOf(r.id);
  return (
    <div className="detail">
      <div className="dpanel">
        <div className="dscroll fadein" key={r.id}>
          <div className="dhead">
            <span className="dball" style={coverFill(r, api).style}>{coverFill(r, api).content}</span>
            <span className="dname">{r.name}</span>
            <span className="dlv">⏱ {r.time}m</span>
          </div>
          <div className="dspecies">{r.blurb}</div>

          <div className="dtypes">
            {boxes.map(c => <Badge key={c.id} emoji={c.emoji} label={c.name} color={c.color || FALLBACK_COLOR} onRemove={() => api.toggleMember(c.id, r.id)} />)}
            <BoxAdder recipe={r} api={api} />
          </div>

          <div className="stattable">
            <div className="strow"><span className="stk">Time</span><span className="stv">{r.time} min</span></div>
            <div className="strow"><span className="stk">Serves</span><span className="stv">{r.serves}</span></div>
            <div className="strow"><span className="stk">Effort</span><span className="stv">{EFFORT[r.effort]} <StatSeg value={r.effort} /></span></div>
            <div className="strow"><span className="stk">Spice</span><span className="stv">{SPICE[r.spice]} <StatSeg value={r.spice} spice /></span></div>
          </div>

          <div className="dmoves">
            <div className="ml">METHOD</div>
            {r.steps.slice(0, 4).map((s, i) => (
              <div key={i} className={'move m' + i}><span className="ic">{i + 1}</span>{s}</div>
            ))}
            {r.steps.length > 4 && <div className="more">＋{r.steps.length - 4} more moves in the full recipe…</div>}
          </div>
          <div className="markings">● ▲ ■ ♥ ★ ◆</div>
        </div>

        <div className="dfoot">
          <button className="openbtn" onClick={() => api.openRecipe(r.id)}>▶ View full recipe</button>
          <button className={'iconbtn' + (api.favourites.has(r.id) ? ' on' : '')} onClick={() => api.toggleFav(r.id)} aria-label="favourite" title="favourite">★</button>
          <button className="iconbtn" onClick={() => api.startEdit(r.id)} aria-label="edit" title="edit recipe">✎</button>
        </div>
      </div>
    </div>
  );
}

function BoxScreen({ api }){
  return (
    <div className="board">
      <div className="swoosh"></div><div className="swoosh two"></div>
      <div className={'boxlayout' + (api.isMobile ? ' mobile' : '')}>
        <PartyList api={api} />
        <BoxGrid api={api} />
        {!api.isMobile && <DetailPanel api={api} />}
      </div>
    </div>
  );
}

/* ============ FULL RECIPE ============ */
function RecipeFull({ api }){
  const r = api.recipeById(api.selectedId);
  const [servings, setServings] = React.useState(r ? r.serves : 2);
  const [checked, setChecked] = React.useState(() => new Set());
  React.useEffect(() => { if (r) { setServings(r.serves); setChecked(new Set()); } }, [api.selectedId]);
  if (!r) return null;
  const toggle = i => setChecked(p => { const n = new Set(p); n.has(i) ? n.delete(i) : n.add(i); return n; });

  return (
    <div className="rfull board" style={{ display: 'block' }}>
      <button className="rfback" onClick={() => api.setView('box')}>← Back to boxes</button>
      <div className="rfgrid">
        <div>
          <div className="rfcard">
            <div className="rfportrait" style={coverFill(r, api).style}>{!r.photo && <span className="tdots"></span>}{coverFill(r, api).content}</div>
            <div className="rfst">
              <div className="strow"><span className="stk">Time</span><span className="stv">{r.time} min</span></div>
              <div className="strow"><span className="stk">Effort</span><span className="stv">{EFFORT[r.effort]} <StatSeg value={r.effort} /></span></div>
              <div className="strow"><span className="stk">Spice</span><span className="stv">{SPICE[r.spice]} <StatSeg value={r.spice} spice /></span></div>
            </div>
          </div>

          {r.link && (
            <a className="srclink" href={r.link} target="_blank" rel="noopener noreferrer">🔗 Original recipe<span className="srcdom">{srcDomain(r.link)}</span></a>
          )}

          <div className="panelw" style={{ marginTop: 14 }}>
            <h3>🗂️ In boxes</h3>
            <p className="hint">Toggle which boxes this recipe lives in.</p>
            <div className="tagedit">
              {api.collections.map(c => {
                const inIt = c.members.includes(r.id);
                return (
                  <button key={c.id} className="typebadge" style={{ background: inIt ? (c.color || FALLBACK_COLOR) : '#cdc4b0' }}
                    onClick={() => api.toggleMember(c.id, r.id)}>
                    <span className="tb-ic">{c.emoji}</span>{c.name}{inIt ? ' ✓' : ''}
                  </button>
                );
              })}
            </div>
          </div>
          <button className="editlink" onClick={() => api.startEdit(r.id)}>✎ Edit this recipe</button>
        </div>

        <div className="rfright">
          <div className="rftitle">
            <div style={{ flex: 1, minWidth: 0 }}>
              <h1>{r.name}</h1>
              <p className="blurb">{r.blurb}</p>
            </div>
            <Scaler servings={servings} onChange={setServings} />
          </div>

          <div className="panelw">
            <h3>🧺 Ingredients</h3>
            <p className="hint">Metric · scales live with servings · tap to tick off</p>
            {r.ingredients.length === 0 && <p style={{ color: 'var(--ink2)', fontWeight: 700 }}>No ingredients listed for this one.</p>}
            {r.ingredients.map((ing, i) => (
              <div key={i} className={'ing' + (checked.has(i) ? ' on' : '')} onClick={() => toggle(i)}>
                <span className="box">{checked.has(i) ? '✓' : ''}</span>
                <span className="nm">{ing.name}</span>
                <span className="qty">{formatQty(ing, servings, r.serves)}</span>
              </div>
            ))}
          </div>

          <div className="panelw">
            <h3>✨ Method</h3>
            <p className="hint">{r.steps.length} {r.steps.length === 1 ? 'move' : 'moves'}</p>
            {r.steps.map((s, i) => {
              const used = stepIngredients(s, r.ingredients);
              return (
                <div key={i} className={'move m' + (i % 6)} style={{ marginBottom: 8, alignItems: 'flex-start' }}>
                  <span className="ic">{i + 1}</span>
                  <div className="movebody">
                    <div className="movetx">{s}</div>
                    {used.length > 0 && (
                      <div className="stepings">
                        {used.map((ing, j) => <span key={j} className="steping">{ing.name} <b>{formatQty(ing, servings, r.serves)}</b></span>)}
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}
function srcDomain(url){ try { return ' · ' + new URL(url).hostname.replace(/^www\./, ''); } catch { return ''; } }

/* ============ SEARCH (pantry) ============ */
const CATS = [
  { key: 'veg', label: 'VEG & FRESH' }, { key: 'protein', label: 'PROTEIN' },
  { key: 'dry', label: 'DRY & GRAINS' }, { key: 'dairy', label: 'DAIRY' },
  { key: 'sauce', label: 'SAUCES & TINS' }, { key: 'pantry', label: 'PANTRY' },
  { key: 'freezer', label: 'FREEZER' }, { key: 'custom', label: 'OTHER' },
];
function SearchScreen({ api }){
  const [edit, setEdit] = React.useState(false);
  const [adding, setAdding] = React.useState({});
  const [boxFilter, setBoxFilter] = React.useState(() => new Set());
  const have = api.have;
  const pantry = api.pantry;
  function toggle(k){ if (edit) return; api.setHave(p => { const n = new Set(p); n.has(k) ? n.delete(k) : n.add(k); return n; }); }
  function toggleBox(id){ setBoxFilter(p => { const n = new Set(p); n.has(id) ? n.delete(id) : n.add(id); return n; }); }

  const haveCustomLabels = pantry.filter(p => p.custom && have.has(p.key)).map(p => p.label.toLowerCase()).filter(Boolean);
  const haveKeys = have;
  function ingHave(ing){
    if (ing.key && haveKeys.has(ing.key)) return true;
    const n = (ing.name || '').toLowerCase();
    return haveCustomLabels.some(l => l && (n.includes(l) || l.includes(n)));
  }

  let recipes = api.recipes;
  if (boxFilter.size) {
    const ids = new Set();
    api.collections.forEach(c => { if (boxFilter.has(c.id)) c.members.forEach(m => ids.add(m)); });
    recipes = recipes.filter(r => ids.has(r.id));
  }

  const ranked = recipes.map(r => {
    const needed = r.ingredients.filter(i => !isBasic(i.name));
    const haveCount = needed.filter(ingHave).length;
    const missing = needed.filter(i => !ingHave(i));
    const pct = needed.length ? haveCount / needed.length * 100 : 0;
    return { r, total: needed.length, haveCount, missing, pct };
  }).sort((a, b) => {
    if ((a.missing.length === 0) !== (b.missing.length === 0)) return a.missing.length === 0 ? -1 : 1;
    if (b.pct !== a.pct) return b.pct - a.pct;
    return a.missing.length - b.missing.length;
  });
  const readyCount = ranked.filter(x => x.haveCount > 0 && x.missing.length === 0).length;
  const any = have.size > 0 || boxFilter.size > 0;

  return (
    <div className="searchscreen board" style={{ display: 'block' }}>
      <button className="rfback" onClick={() => api.setView('box')}>← Back to boxes</button>
      <div className="searchhead"><h2>🧺 What can I make?</h2></div>

      <div className="boxfilter">
        <span className="bflbl">Filter by box:</span>
        {api.collections.map(c => (
          <button key={c.id} className={'bfchip' + (boxFilter.has(c.id) ? ' on' : '')}
            style={boxFilter.has(c.id) ? { background: c.color || FALLBACK_COLOR, color: '#fff' } : null}
            onClick={() => toggleBox(c.id)}>{c.emoji} {c.name}</button>
        ))}
        {boxFilter.size > 0 && <button className="bfclear" onClick={() => setBoxFilter(new Set())}>clear ✕</button>}
      </div>

      <div className="searchgrid">
        <div className="pantrycard">
          <div className="pantrytop">
            <p className="hint" style={{ margin: 0 }}>Tap what's in your kitchen. (Salt, oil, flour &amp; basics are assumed.)</p>
            <div style={{ display: 'flex', gap: 8, flex: 'none' }}>
              {have.size > 0 && <button className="editpantry" onClick={() => api.setHave(new Set())}>Clear</button>}
              <button className={'editpantry' + (edit ? ' on' : '')} onClick={() => setEdit(e => !e)}>{edit ? '✓ Done' : '✎ Edit'}</button>
            </div>
          </div>
          {CATS.map(cat => {
            const items = pantry.filter(p => p.cat === cat.key);
            return (
              <div key={cat.key}>
                <div className="catlbl">{cat.label}</div>
                <div className="jargrid">
                  {items.map(p => edit ? (
                    <div key={p.key} className="jar editing">
                      <input className="emojinp" value={p.emoji || ''} placeholder="🙂" maxLength={2}
                        onChange={e => api.editPantry(p.key, { emoji: e.target.value })} />
                      <input className="namenp" value={p.label}
                        onChange={e => api.editPantry(p.key, { label: e.target.value })} />
                      {p.custom && <button className="jardel" onClick={() => api.removePantry(p.key)}>✕</button>}
                    </div>
                  ) : (
                    <button key={p.key} className={'jar' + (have.has(p.key) ? ' have' : '')} onClick={() => toggle(p.key)}>
                      <span className="av">{p.emoji ? p.emoji : <span className="lt">{(p.label[0] || '?').toUpperCase()}</span>}</span>
                      {p.label}{have.has(p.key) ? ' ✓' : ''}
                    </button>
                  ))}
                  {!edit && (
                    <form className="addjar" onSubmit={e => { e.preventDefault(); const t = (adding[cat.key] || '').trim(); if (t) { api.addPantry(t, cat.key); setAdding(a => ({ ...a, [cat.key]: '' })); } }}>
                      <input value={adding[cat.key] || ''} placeholder="＋ add…" onChange={e => setAdding(a => ({ ...a, [cat.key]: e.target.value }))} />
                    </form>
                  )}
                </div>
                {cat.key === 'custom' && !items.length && (
                  <p className="customhint">For anything that doesn't fit a section, or a niche ingredient — type it in any box above. New items match your recipes by name.</p>
                )}
              </div>
            );
          })}
        </div>

        <div className="resultscard">
          <h3>🍳 You can make…</h3>
          <p className="meta">{any ? `${readyCount} ready now · ${ranked.length} shown` : 'pick ingredients or a box to start'}</p>
          {!any ? (
            <div className="emptyhint"><span className="big">🐾</span>Tap a few jars and I'll show you what's cookable!</div>
          ) : ranked.length === 0 ? (
            <div className="emptyhint">Nothing in those boxes yet.</div>
          ) : ranked.map(({ r, total, haveCount, missing, pct }) => (
            <div key={r.id} className="result" onClick={() => api.openRecipe(r.id)}>
              <span className="mini" style={coverFill(r, api).style}>{coverFill(r, api).content}</span>
              <div className="info">
                <div className="nm">{r.name}</div>
                <div className="mb"><i style={{ width: Math.max(6, pct) + '%', background: pct >= 100 ? 'var(--good)' : pct >= 60 ? 'var(--warn)' : 'var(--miss)' }}></i></div>
                <div className="st">{missing.length === 0
                  ? <span className="ready">✓ you have everything!</span>
                  : `have ${haveCount}/${total} · missing ${missing.slice(0,2).map(i => i.name).join(', ')}${missing.length > 2 ? ` +${missing.length-2}` : ''}`}</div>
              </div>
              {missing.length === 0 && <span className="cook">COOK!</span>}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ============ ADD / EDIT recipe ============ */
const UNITS = ['g','kg','ml','l','tbsp','tsp','cup','cups','clove','cloves','stalk','stalks','stalks','head','heads','pinch','can','cans','tin','tins','pcs','piece','pieces','slice','slices','handful','bunch'];
function parseQty(s){
  if (!s) return null;
  s = s.replace('½','.5').replace('¼','.25').replace('¾','.75').replace('⅓','.333').replace('⅔','.667').replace(',', '.');
  const mixed = s.match(/(\d+)\s+(\d+)\s*\/\s*(\d+)/); if (mixed) return +mixed[1] + (+mixed[2]/+mixed[3]);
  const frac = s.match(/(\d+)\s*\/\s*(\d+)/); if (frac) return +frac[1]/+frac[2];
  const dec = s.match(/-?\d+(?:\.\d+)?/); return dec ? parseFloat(dec[0]) : null;
}
function parseIngredientLine(line){
  let s = line.replace(/^[-*•·\u2022]\s*/, '').trim();
  const m = s.match(/^((?:\d+\s+)?\d*[¼½¾⅓⅔]|\d+(?:[.,]\d+)?(?:\s*\/\s*\d+)?)\s*/);
  let qty = null, rest = s;
  if (m && m[1]) { qty = parseQty(m[1]); rest = s.slice(m[0].length).trim(); }
  let unit = '';
  const um = rest.match(/^([a-zA-Z.]+)\b/);
  if (um) { const u = um[1].toLowerCase().replace(/\.$/, ''); if (UNITS.includes(u)) { unit = u === 'cups' ? 'cup' : u; rest = rest.slice(um[0].length).trim(); } }
  rest = rest.replace(/^(of)\s+/i, '');
  return { name: rest || s, qty, unit };
}
function looksLikeIngredient(line){
  const s = line.trim();
  if (/^\d+[.)]\s/.test(s)) return false;               // "1. step"
  const hasQty = /^[\d¼½¾⅓⅔]/.test(s);
  const hasUnit = new RegExp('\\b(' + UNITS.join('|') + ')\\b', 'i').test(s);
  const words = s.split(/\s+/).length;
  const sentence = /[.!?]$/.test(s) || words > 9;
  if (hasQty || (hasUnit && words <= 8)) return true;
  return !sentence && words <= 6;
}
function autoSplit(text){
  const lines = text.split(/\n+/).map(l => l.trim()).filter(Boolean);
  const ingredients = [], steps = [];
  let mode = null;
  for (let line of lines){
    const low = line.toLowerCase().replace(/[:：]/g,'').trim();
    if (/^(ingredients|you will need|shopping list)$/.test(low)) { mode = 'ing'; continue; }
    if (/^(method|steps|directions|instructions|preparation|how to)$/.test(low)) { mode = 'step'; continue; }
    const isIng = mode === 'ing' ? true : mode === 'step' ? false : looksLikeIngredient(line);
    if (isIng) ingredients.push(parseIngredientLine(line));
    else steps.push(line.replace(/^(step\s*)?\d+[.):]?\s*/i, '').trim());
  }
  return { ingredients, steps };
}

const FOOD_EMOJIS = ['🍜','🍝','🍲','🍛','🥗','🥟','🥞','🍕','🍚','🍳','🌮','🍔','🥘','🍰','🍪','🥧','🍞','🫕','🍣','🥪','🌯','🧆'];

function AddEditScreen({ api }){
  const editing = api.editId ? api.recipeById(api.editId) : null;
  const blank = { id: '', name: '', emoji: '🍳', link: '', blurb: '', serves: 2, time: 20, effort: 1, spice: 1, ingredients: [], steps: [] };
  const [d, setD] = React.useState(() => editing ? JSON.parse(JSON.stringify(editing)) : blank);
  const [boxIds, setBoxIds] = React.useState(() => new Set(editing ? api.boxesOf(editing.id).map(c => c.id) : []));
  const [paste, setPaste] = React.useState('');
  const [showPaste, setShowPaste] = React.useState(!editing);
  const [coverMode, setCoverMode] = React.useState(() => editing && editing.photo ? 'image' : 'emoji');
  const [hasIngredients, setHasIngredients] = React.useState(() => editing ? !!(editing.ingredients && editing.ingredients.length) : true);
  const set = (k, v) => setD(p => ({ ...p, [k]: v }));

  function onUpload(e){
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = ev => {
      const img = new Image();
      img.onload = () => {
        const max = 480, scale = Math.min(1, max / Math.max(img.width, img.height));
        const cw = Math.round(img.width * scale), ch = Math.round(img.height * scale);
        const cv = document.createElement('canvas'); cv.width = cw; cv.height = ch;
        cv.getContext('2d').drawImage(img, 0, 0, cw, ch);
        set('photo', cv.toDataURL('image/jpeg', 0.82));
        setCoverMode('image');
      };
      img.src = ev.target.result;
    };
    reader.readAsDataURL(file);
  }

  function applyPaste(replace){
    const { ingredients, steps } = autoSplit(paste);
    if (ingredients.length) setHasIngredients(true);
    setD(p => ({
      ...p,
      ingredients: replace ? ingredients : [...p.ingredients, ...ingredients],
      steps: replace ? steps : [...p.steps, ...steps],
    }));
    setShowPaste(false);
  }
  const setIng = (i, k, v) => setD(p => ({ ...p, ingredients: p.ingredients.map((x, j) => j === i ? { ...x, [k]: v } : x) }));
  const addIng = () => setD(p => ({ ...p, ingredients: [...p.ingredients, { name: '', qty: null, unit: '' }] }));
  const delIng = (i) => setD(p => ({ ...p, ingredients: p.ingredients.filter((_, j) => j !== i) }));
  const setStep = (i, v) => setD(p => ({ ...p, steps: p.steps.map((x, j) => j === i ? v : x) }));
  const addStep = () => setD(p => ({ ...p, steps: [...p.steps, ''] }));
  const delStep = (i) => setD(p => ({ ...p, steps: p.steps.filter((_, j) => j !== i) }));
  const toggleBox = (id) => setBoxIds(p => { const n = new Set(p); n.has(id) ? n.delete(id) : n.add(id); return n; });

  function save(){
    if (!d.name.trim()) { api.flash('Give your recipe a name first 🐾'); return; }
    const clean = {
      ...d, name: d.name.trim(),
      ingredients: hasIngredients ? d.ingredients.filter(i => (i.name || '').trim()) : [],
      steps: d.steps.map(s => s.trim()).filter(Boolean),
    };
    api.saveRecipe(clean, [...boxIds]);
  }

  return (
    <div className="rfull board" style={{ display: 'block' }}>
      <button className="rfback" onClick={() => api.setView('box')}>← {editing ? 'Cancel edit' : 'Cancel'}</button>
      <div className="editwrap">
        <div className="edithead">
          <h1>{editing ? 'Edit recipe' : 'Catch a new recipe'}</h1>
          <p>{editing ? 'Tweak anything and save.' : 'Paste a recipe to auto-fill, or type it in by hand.'}</p>
        </div>

        {showPaste && (
          <div className="panelw pastebox">
            <h3>📋 Paste a recipe</h3>
            <p className="hint">Copy a recipe from any website and drop it here</p>
            <textarea value={paste} onChange={e => setPaste(e.target.value)} placeholder={"Ingredients\n200g rice noodles\n2 cloves garlic\n3 tbsp soy sauce\n\nMethod\nSoak the noodles...\nFry the garlic..."} />
            <div className="pasterow">
              <button className="gbtn" onClick={() => applyPaste(true)} disabled={!paste.trim()}>✨ Auto-split</button>
              <button className="lbtn" onClick={() => setShowPaste(false)}>Skip</button>
            </div>
          </div>
        )}

        <div className="editgrid">
          <div className="panelw">
            <h3>The basics</h3>
            <label className="flbl">Name</label>
            <input className="finp" value={d.name} onChange={e => set('name', e.target.value)} placeholder="e.g. Grandma's dumplings" />
            <label className="flbl">One-line description</label>
            <input className="finp" value={d.blurb} onChange={e => set('blurb', e.target.value)} placeholder="A cosy bowl for cold nights" />
            <label className="flbl">Cover</label>
            <div className="coverpick">
              <div className="coverprev" style={d.photo ? { backgroundImage: `url("${(d.photo + '').replace(/"/g, '%22')}")`, backgroundSize: 'cover', backgroundPosition: 'center' } : { background: hexTint(api.colorOf(d.id) || '#c9a36a', 0.66) }}>
                {!d.photo && <span>{d.emoji}</span>}
              </div>
              <div className="covertabs">
                <button className={'ctab' + (coverMode === 'emoji' ? ' on' : '')} onClick={() => setCoverMode('emoji')}>😀 Emoji</button>
                <button className={'ctab' + (coverMode === 'image' ? ' on' : '')} onClick={() => setCoverMode('image')}>🖼️ Image</button>
              </div>
            </div>
            {coverMode === 'emoji' ? (
              <div className="emojipick">
                <input className="finp emojibig" value={d.emoji} maxLength={2} onChange={e => { set('emoji', e.target.value); set('photo', undefined); }} />
                <div className="emojiopts">
                  {FOOD_EMOJIS.map(e => <button key={e} className="eopt" onClick={() => { set('emoji', e); set('photo', undefined); }}>{e}</button>)}
                </div>
              </div>
            ) : (
              <div className="imagepick">
                <input className="finp" value={d.photo && !(d.photo + '').startsWith('data:') ? d.photo : ''} placeholder="Paste an image URL…" onChange={e => set('photo', e.target.value || undefined)} />
                <div className="imagerow">
                  <label className="uploadbtn">⬆ Upload photo<input type="file" accept="image/*" onChange={onUpload} style={{ display: 'none' }} /></label>
                  {d.photo && <button className="lbtn" onClick={() => set('photo', undefined)}>Remove</button>}
                </div>
              </div>
            )}
            <label className="flbl">🔗 Link to original (optional)</label>
            <input className="finp" value={d.link} onChange={e => set('link', e.target.value)} placeholder="https://…  (if you grabbed it from a site)" />
          </div>

          <div className="panelw">
            <h3>Stats</h3>
            <div className="statedit">
              <label className="flbl">Serves</label>
              <NumStepper value={d.serves} min={1} max={24} onChange={v => set('serves', v)} />
            </div>
            <div className="statedit">
              <label className="flbl">Time (min)</label>
              <NumStepper value={d.time} min={1} max={600} step={5} onChange={v => set('time', v)} />
            </div>
            <div className="statedit"><label className="flbl">Effort</label><SegPick value={d.effort} labels={EFFORT} onChange={v => set('effort', v)} /></div>
            <div className="statedit"><label className="flbl">Spice</label><SegPick value={d.spice} labels={SPICE} spice onChange={v => set('spice', v)} /></div>
            <label className="flbl" style={{ marginTop: 12 }}>In boxes</label>
            <div className="tagedit">
              {api.collections.map(c => (
                <button key={c.id} className="typebadge" style={{ background: boxIds.has(c.id) ? (c.color || FALLBACK_COLOR) : '#cdc4b0' }} onClick={() => toggleBox(c.id)}>
                  <span className="tb-ic">{c.emoji}</span>{c.name}{boxIds.has(c.id) ? ' ✓' : ''}
                </button>
              ))}
            </div>
          </div>
        </div>

        <div className="panelw">
          <div className="ingedithead">
            <h3>🧺 Ingredients</h3>
            <label className="optoggle"><input type="checkbox" checked={!hasIngredients} onChange={e => setHasIngredients(!e.target.checked)} /> No ingredient list</label>
          </div>
          {!hasIngredients ? (
            <p className="hint">This recipe has no ingredient list (handy for a quick note or a technique).</p>
          ) : (
          <React.Fragment>
          <p className="hint">Metric amounts. Leave quantity blank for "to taste" items.</p>
          {d.ingredients.map((ing, i) => (
            <div key={i} className="ingedit">
              <input className="qy" value={ing.qty == null ? '' : ing.qty} placeholder="qty" onChange={e => setIng(i, 'qty', e.target.value === '' ? null : parseQty(e.target.value))} />
              <select className="un" value={ing.unit || ''} onChange={e => setIng(i, 'unit', e.target.value)}>
                <option value="">unit</option>
                {['g','kg','ml','l','tbsp','tsp','cup','clove','cloves','stalk','stalks','head','pinch','can','pcs','slice','handful','bunch'].map(u => <option key={u} value={u}>{u}</option>)}
              </select>
              <input className="nm" value={ing.name} placeholder="ingredient" onChange={e => setIng(i, 'name', e.target.value)} />
              <button className="rmrow" onClick={() => delIng(i)}>✕</button>
            </div>
          ))}
          <button className="addrow" onClick={addIng}>＋ Add ingredient</button>
          </React.Fragment>
          )}
        </div>

        <div className="panelw">
          <h3>✨ Method</h3>
          <p className="hint">One move per line.</p>
          {d.steps.map((s, i) => (
            <div key={i} className="stepedit">
              <span className="sn">{i + 1}</span>
              <textarea value={s} placeholder="Describe this move…" onChange={e => setStep(i, e.target.value)} rows={2} />
              <button className="rmrow" onClick={() => delStep(i)}>✕</button>
            </div>
          ))}
          <button className="addrow" onClick={addStep}>＋ Add move</button>
        </div>

        <div className="editfoot">
          {editing && <button className="delbtn" onClick={() => { if (confirm('Delete this recipe from your dex? This cannot be undone.')) api.deleteRecipe(editing.id); }}>🗑 Delete recipe</button>}
          {!showPaste && <button className="lbtn" onClick={() => setShowPaste(true)}>📋 Paste more</button>}
          <span style={{ flex: 1 }}></span>
          <button className="gbtn big" onClick={save}>{editing ? '✓ Save changes' : '✓ Catch it!'}</button>
        </div>
      </div>
    </div>
  );
}

function NumStepper({ value, onChange, min = 0, max = 999, step = 1 }){
  return (
    <span className="numstep">
      <button onClick={() => onChange(Math.max(min, value - step))}>–</button>
      <b>{value}</b>
      <button onClick={() => onChange(Math.min(max, value + step))}>+</button>
    </span>
  );
}
function SegPick({ value, onChange, labels, spice }){
  return (
    <span className="segpick">
      <span className="pips">
        {[1,2,3,4,5].map(i => (
          <button key={i} type="button" className={'pip' + (i <= value ? (spice ? ' spiceon' : ' on') : '')} onClick={() => onChange(i)}>{i}</button>
        ))}
      </span>
      <small>{labels[value]}</small>
    </span>
  );
}

/* ============ BOX LIST modal ============ */
function BoxListModal({ api, onClose }){
  const [newName, setNewName] = React.useState('');
  return (
    <div className="modalwrap" onClick={onClose}>
      <div className="modalcard" onClick={e => e.stopPropagation()}>
        <div className="modalhead">▦ Your boxes <button className="mx" onClick={onClose}>✕</button></div>
        <div className="modalbody">
          {api.collections.map(c => (
            <div key={c.id} className="boxrow">
              <input className="bememoji" value={c.emoji} maxLength={2} onChange={e => api.editCollection(c.id, { emoji: e.target.value })} style={{ background: hexTint(c.color || FALLBACK_COLOR, 0.55) }} />
              <input value={c.name} onChange={e => api.renameCollection(c.id, e.target.value)} />
              <span className="bcount">{c.members.length}</span>
              <button className="bdel" onClick={() => api.deleteCollection(c.id)} aria-label="delete box">🗑</button>
            </div>
          ))}
          <div className="addbox">
            <input value={newName} placeholder="New box, e.g. Winter nights"
              onChange={e => setNewName(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter' && newName.trim()) { api.addCollection(newName); setNewName(''); } }} />
            <button onClick={() => { if (newName.trim()) { api.addCollection(newName); setNewName(''); } }}>＋ Add</button>
          </div>
          <div className="modalnote">Edit the emoji or name inline · 🗑 to delete · open a recipe to choose its boxes.</div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { BoxScreen, RecipeFull, SearchScreen, AddEditScreen, BoxListModal });
