// Recipe detail view, cook mode, shopping list panel. Depends on icons + home.

// Same-number-preserving amount scaler (fractions + unicode handled via simpler pass)
const UNICODE_FRAC = {
  '½':0.5,'⅓':1/3,'⅔':2/3,'¼':0.25,'¾':0.75,
  '⅕':0.2,'⅖':0.4,'⅗':0.6,'⅘':0.8,
  '⅙':1/6,'⅚':5/6,'⅛':0.125,'⅜':0.375,'⅝':0.625,'⅞':0.875
};
const FRAC_CHARS = Object.keys(UNICODE_FRAC).join('');
function parseQty(tok) {
  tok = tok.trim();
  let m = tok.match(/^(\d+)\s+(\d+)\/(\d+)$/); if (m) return (+m[1]) + (+m[2])/(+m[3]);
  m = tok.match(new RegExp(`^(\\d+)([${FRAC_CHARS}])$`)); if (m) return (+m[1]) + UNICODE_FRAC[m[2]];
  m = tok.match(/^(\d+)\/(\d+)$/); if (m) return (+m[1]) / (+m[2]);
  if (UNICODE_FRAC[tok] != null) return UNICODE_FRAC[tok];
  m = tok.match(/^\d+(?:\.\d+)?$/); if (m) return +tok;
  return null;
}
function fmtQty(v) {
  if (!isFinite(v) || v <= 0) return '';
  const r = Math.round(v * 100) / 100;
  const whole = Math.floor(r + 1e-9);
  const frac = r - whole;
  const pairs = [[0.125,'⅛'],[0.25,'¼'],[1/3,'⅓'],[0.375,'⅜'],[0.5,'½'],[0.625,'⅝'],[2/3,'⅔'],[0.75,'¾'],[0.875,'⅞']];
  for (const [f, g] of pairs) if (Math.abs(frac - f) < 0.02) return whole === 0 ? g : whole + g;
  if (frac < 0.02) return String(whole);
  return r.toFixed(2).replace(/\.?0+$/, '') || '0';
}
function scaleAmtStr(amt, factor) {
  if (!amt || typeof amt !== 'string' || factor === 1) return amt;
  const pat = new RegExp(
    `(\\d+\\s+\\d+\\/\\d+)|(\\d+[${FRAC_CHARS}])|(\\d+\\/\\d+)|([${FRAC_CHARS}])|(\\d+(?:\\.\\d+)?)`,
    'g'
  );
  return amt.replace(pat, (match) => {
    const v = parseQty(match);
    if (v == null) return match;
    return fmtQty(v * factor);
  });
}
function parseBaseServings(s) {
  if (!s) return 0;
  const m = String(s).match(/(\d+(?:\.\d+)?)/);
  return m ? +m[1] : 0;
}

// Turn mentions of other recipe names (optionally "(page NN)") into clickable refs.
function LinkedText({ text, refs, onOpenRef }) {
  if (!text) return null;
  const valid = (refs || []).filter(r => r && r.name && r.name.trim().length > 2);
  if (valid.length === 0) return text;
  const sorted = [...valid].sort((a, b) => b.name.length - a.name.length);
  const pattern = sorted.map(r => r.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
  const regex = new RegExp(`\\b(${pattern})\\b(\\s*\\(\\s*page\\s+\\d+\\s*\\))?`, 'gi');
  const parts = [];
  let lastIdx = 0, m, keyN = 0;
  while ((m = regex.exec(text)) !== null) {
    if (m.index > lastIdx) parts.push(text.slice(lastIdx, m.index));
    const name = m[1];
    const ref = sorted.find(r => r.name.toLowerCase() === name.toLowerCase());
    if (ref) {
      parts.push(
        <a key={`r${keyN++}`} className="recipe-ref"
           onClick={(e) => { e.preventDefault(); e.stopPropagation(); onOpenRef(ref.id); }}>
          {m[0]}
        </a>
      );
    } else {
      parts.push(m[0]);
    }
    lastIdx = m.index + m[0].length;
  }
  if (lastIdx < text.length) parts.push(text.slice(lastIdx));
  return <>{parts}</>;
}

// Slide-in side panel showing a referenced recipe without leaving current context
function RecipePeek({ recipe, allRecipes, onClose, onOpenRef }) {
  if (!recipe) return null;
  const img = recipe.image || '';
  const refsExcludingSelf = (allRecipes || []).filter(r => r.id !== recipe.id);
  const rating = avgRating(recipe);
  return (
    <div className="recipe-peek">
      <button className="recipe-peek-close" onClick={onClose} aria-label="Close">
        <IconX size={18} />
      </button>
      <div className="peek-scroll">
        <div className="peek-eyebrow">Referenced recipe</div>
        {img && <div className="peek-image" style={{ backgroundImage: `url(${img})` }} />}
        <div className="peek-category">
          {recipe.category || 'Recipe'}
          {recipe.author && <> · by <span className="recipe-eyebrow-author">{recipe.author}</span></>}
        </div>
        <h2 className="peek-title">{recipe.name}</h2>
        {recipe.addedBy && (
          <div className="recipe-author peek-author">
            <IconBook size={13} stroke={1.7} />
            <span>Added by <strong>{recipe.addedBy}</strong></span>
          </div>
        )}
        {rating > 0 && (
          <div className="peek-rating">
            <Stars value={Math.round(rating)} size={14} />
            <span>{rating.toFixed(1)}</span>
          </div>
        )}
        {recipe.description && <p className="peek-desc">{recipe.description}</p>}

        <div className="peek-meta">
          {recipe.prep     && <span><IconClock size={12} /> Prep {recipe.prep}</span>}
          {recipe.cook     && <span><IconFlame size={12} /> Cook {recipe.cook}</span>}
          {recipe.servings && <span><IconUsers size={12} /> {recipe.servings}</span>}
        </div>

        <div className="peek-section">
          <h3>Ingredients</h3>
          <ul className="peek-ing-list">
            {(recipe.ingredients || []).map((i, idx) => (
              <li key={idx}>
                <span className="ing-amt">{i.amt || '•'}</span>
                <span className="ing-name"><LinkedText text={i.name || ''} refs={refsExcludingSelf} onOpenRef={onOpenRef} /></span>
              </li>
            ))}
          </ul>
        </div>

        <div className="peek-section">
          <h3>Method</h3>
          <ol className="peek-steps">
            {(recipe.steps || []).map((s, idx) => (
              <li key={idx}>
                <span className="peek-step-num">{idx + 1}</span>
                <p><LinkedText text={s} refs={refsExcludingSelf} onOpenRef={onOpenRef} /></p>
              </li>
            ))}
          </ol>
        </div>
      </div>
    </div>
  );
}

function NotesSection({ recipe, onAdd, onDelete, members = [] }) {
  const { user } = useAuthState();
  const userName = user?.fullName || user?.firstName || '';
  const [adding, setAdding]     = React.useState(false);
  const [author, setAuthor]     = React.useState('');
  const [text, setText]         = React.useState('');
  const [saving, setSaving]     = React.useState(false);
  const comments = Array.isArray(recipe.comments) ? recipe.comments : [];

  async function handleSubmit(e) {
    e.preventDefault();
    if (!text.trim()) return;
    setSaving(true);
    try {
      await onAdd({ author: (userName || author).trim(), text: text.trim() });
      setAuthor(''); setText(''); setAdding(false);
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="notes-panel">
      <div className="panel-head">
        <h2>Family notes</h2>
        <span className="panel-sub">{comments.length} {comments.length === 1 ? 'note' : 'notes'}</span>
      </div>

      {comments.slice().reverse().map(c => {
        const when = c.date ? new Date(c.date).toLocaleDateString(undefined, { month:'short', day:'numeric' }) : '';
        return (
          <div key={c.id} className="note">
            <div className="note-head">
              {(() => { const av = memberAvatarFor(members, c.author); return av ? <AvatarMark avatar={av} name={c.author} size={20} className="note-mark" /> : null; })()}
              <strong>{c.author || 'Anonymous'}</strong>
              {when && <span className="muted">· {when}</span>}
              <button
                type="button"
                className="note-delete"
                title="Delete note"
                onClick={() => { if (confirm('Delete this note?')) onDelete(c.id); }}
              >
                <IconX size={12} />
              </button>
            </div>
            <p>{c.text}</p>
          </div>
        );
      })}

      {adding ? (
        <form className="note-form" onSubmit={handleSubmit}>
          <input
            type="text"
            value={userName || author}
            onChange={e => !userName && setAuthor(e.target.value)}
            placeholder="Your name (optional)"
            readOnly={!!userName}
            maxLength={40}
          />
          <textarea
            rows={3}
            placeholder="What did you think? Any tweaks for next time?"
            value={text}
            onChange={e => setText(e.target.value)}
            maxLength={2000}
            required
            autoFocus
          />
          <div className="note-form-actions">
            <button type="button" className="ghost-btn" onClick={() => { setAdding(false); setAuthor(''); setText(''); }}>
              Cancel
            </button>
            <button type="submit" className="primary-btn" disabled={saving || !text.trim()}>
              {saving ? 'Adding…' : 'Add note'}
            </button>
          </div>
        </form>
      ) : (
        <button type="button" className="btn-note" onClick={() => setAdding(true)}>
          <IconPlus size={14} stroke={2} /> Add a note
        </button>
      )}
    </div>
  );
}

function FamilyVerdict({ recipe, onSet, onRemove, currentUserId }) {
  const verdicts = (recipe.verdicts || []).filter(v => v.again);
  const mine = (recipe.verdicts || []).find(v => v.userId === currentUserId);
  const [noteOpen, setNoteOpen] = React.useState(false);
  const [note, setNote] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  async function vote(extraNote) {
    setSaving(true);
    try { await onSet({ again: true, note: (extraNote ?? note).trim() }); setNoteOpen(false); setNote(''); }
    finally { setSaving(false); }
  }
  async function unvote() {
    setSaving(true);
    try { await onRemove(); } finally { setSaving(false); }
  }

  return (
    <div className="notes-panel verdict-section">
      <div className="panel-head">
        <h2>The family verdict</h2>
        <span className="panel-sub">
          {verdicts.length > 0
            ? `${verdicts.length} would make this again`
            : 'no votes yet'}
        </span>
      </div>

      {verdicts.length > 0 && (
        <div className="verdict-row">
          <div className="verdict-marks">
            {verdicts.map(v => <AvatarMark key={v.userId} avatar={v.avatar} name={v.name} size={38} />)}
          </div>
          <span className="verdict-hint">
            {verdicts.map(v => (v.name || '').split(' ')[0]).join(', ')} would make this again
          </span>
        </div>
      )}

      {verdicts.filter(v => v.note).map(v => (
        <p key={`n-${v.userId}`} className="verdict-note">
          <span className="who">{(v.name || '').split(' ')[0]}:</span> {v.note}
        </p>
      ))}

      {currentUserId && (
        mine ? (
          <div className="verdict-mine">
            <span className="verdict-you">you're in ♥</span>
            <button type="button" className="btn-note" disabled={saving}
                    onClick={() => { setNote(mine.note || ''); setNoteOpen(o => !o); }}>
              {mine.note ? 'edit my note' : 'add a note'}
            </button>
            <button type="button" className="btn-note" disabled={saving} onClick={unvote}>
              changed my mind
            </button>
          </div>
        ) : !noteOpen && (
          <button type="button" className="primary-btn verdict-vote" disabled={saving} onClick={() => vote('')}>
            <IconHeart size={15} filled /> I'd make this again
          </button>
        )
      )}

      {noteOpen && (
        <form className="note-form" onSubmit={e => { e.preventDefault(); vote(); }}>
          <textarea rows={2} placeholder="a quick scribble — 'double the garlic', 'kids loved it'…"
                    value={note} onChange={e => setNote(e.target.value)} maxLength={500} autoFocus />
          <div className="note-form-actions">
            <button type="button" className="ghost-btn" onClick={() => setNoteOpen(false)}>Cancel</button>
            <button type="submit" className="primary-btn" disabled={saving}>{saving ? 'Saving…' : 'Save note'}</button>
          </div>
        </form>
      )}
    </div>
  );
}

function RecipeView({ recipe, allRecipes, onBack, onToggleFav, myUserId, onAddToShopping, onCook, onEdit, onDelete, onOpenReference, onAddComment, onDeleteComment, onSetVerdict, onRemoveVerdict, currentUserId, onEnhancePhoto, members = [] }) {
  const base = parseBaseServings(recipe.servings) || 1;
  const [servings, setServings]       = React.useState(base);
  const [enhancing, setEnhancing]         = React.useState(false);
  const [enhancedUrl, setEnhancedUrl]     = React.useState(null);
  const [enhancedBase64, setEnhancedBase64] = React.useState(null);
  const [enhancedMime, setEnhancedMime]   = React.useState(null);
  const [enhanceErr, setEnhanceErr]       = React.useState('');
  const [stylePickerOpen, setStylePickerOpen] = React.useState(false);

  React.useEffect(() => {
    setServings(parseBaseServings(recipe.servings) || 1);
    setEnhancedUrl(null);
    setEnhancedBase64(null);
    setEnhancedMime(null);
    setEnhancing(false);
    setEnhanceErr('');
    setStylePickerOpen(false);
  }, [recipe.id]);

  const factor = base ? servings / base : 1;

  async function handleEnhanceClick(style) {
    if (!img || enhancing) return;
    setStylePickerOpen(false);
    setEnhancing(true);
    setEnhancedUrl(null);
    setEnhancedBase64(null);
    setEnhancedMime(null);
    setEnhanceErr('');
    try {
      console.log('[enhance] img value:', img);
      console.log('[enhance] recipe.image:', recipe.image);
      const res = await fetch('/api/enhance-photo', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json' },
        body:    JSON.stringify({ imageUrl: img, style }),
      });
      console.log('[enhance] response status:', res.status);
      const data = await res.json();
      console.log('[enhance] response keys:', Object.keys(data));
      console.log('[enhance] enhancedImageBase64 length:', data.enhancedImageBase64?.length);
      if (!res.ok || data.error) throw new Error(data.error || 'Enhancement failed');

      const dataUrl = `data:${data.mimeType || 'image/jpeg'};base64,${data.enhancedImageBase64}`;
      console.log('[enhance] setting img src to data URL, length:', dataUrl.length);
      setEnhancedBase64(data.enhancedImageBase64);
      setEnhancedMime(data.mimeType || 'image/jpeg');
      setEnhancedUrl(dataUrl);
    } catch (e) {
      setEnhanceErr(e.message || 'Enhancement failed');
    } finally {
      setEnhancing(false);
    }
  }

  async function handleChoosePhoto(which) {
    if (which === 'enhanced' && enhancedBase64) {
      try {
        const byteStr = atob(enhancedBase64);
        const uint8 = new Uint8Array(byteStr.length);
        for (let i = 0; i < byteStr.length; i++) uint8[i] = byteStr.charCodeAt(i);
        const ext = (enhancedMime || 'image/jpeg').split('/')[1] || 'jpg';
        const blob = new Blob([uint8], { type: enhancedMime || 'image/jpeg' });
        const fd = new FormData();
        fd.append('image', blob, `enhanced.${ext}`);
        const uploadRes = await fetch('/api/upload-image', { method: 'POST', body: fd });
        const uploadData = await uploadRes.json();
        if (!uploadRes.ok || uploadData.error) throw new Error(uploadData.error || 'Upload failed');
        if (onEnhancePhoto) await onEnhancePhoto(recipe.id, uploadData.url);
      } catch (e) {
        setEnhanceErr(e.message || 'Save failed');
        return;
      }
    }
    setEnhancedUrl(null);
    setEnhancedBase64(null);
    setEnhancedMime(null);
    setEnhanceErr('');
  }

  const rating = avgRating(recipe);
  const ratingCount = (recipe.ratings || []).length;
  const img = recipe.image || '';
  const otherRecipes = (allRecipes || []).filter(r => r.id !== recipe.id);
  const openRef = onOpenReference || (() => {});

  return (
    <div className="recipe-view">
      <div className="recipe-top">
        <button className="back-btn" onClick={onBack}>
          <IconChevLeft size={18} /> All recipes
        </button>
        <div className="recipe-top-actions">
          <button className="icon-btn" onClick={onToggleFav} title={(recipe.hearts || []).includes(myUserId) ? "Remove my heart" : "Heart this recipe"}>
            <IconHeart size={18} filled={(recipe.hearts || []).includes(myUserId)} />
          </button>
          <button className="icon-btn" onClick={onEdit} title="Edit">
            <IconEdit size={18} />
          </button>
          <button className="icon-btn danger" onClick={onDelete} title="Delete">
            <IconTrash size={18} />
          </button>
        </div>
      </div>

      <div className="recipe-hero">
        <div className="recipe-hero-image-wrap">
          <div className="recipe-hero-image" style={{ overflow: 'hidden', position: 'relative' }}>
            {img
              ? <img className="recipe-hero-img" src={enhancedUrl || img} alt={recipe.name}
                  style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center' }} />
              : <IconBook size={48} stroke={1} />}
          </div>
          {img && (
            <div className="enhance-wrap">
              <button
                className={`enhance-btn${enhancing ? ' loading' : ''}`}
                onClick={() => setStylePickerOpen(o => !o)}
                disabled={enhancing || !!enhancedUrl}
                title="AI photo enhancement"
              >
                {enhancing ? (
                  <><span className="enhance-spinner" /> Enhancing…</>
                ) : enhancedUrl ? (
                  <>✨ Enhanced</>
                ) : (
                  <>✨ Enhance Photo</>
                )}
              </button>
              {stylePickerOpen && !enhancing && !enhancedUrl && (
                <div className="enhance-style-pop">
                  <div className="enhance-style-head">Pick a look</div>
                  {[
                    { key: 'bright', name: 'Bright & airy',     desc: 'Daylight, light backdrop' },
                    { key: 'moody',  name: 'Moody restaurant',  desc: 'Dark walnut, dramatic side light' },
                    { key: 'rustic', name: 'Rustic homestyle',  desc: 'Linen, warm, overhead' },
                  ].map(s => (
                    <button key={s.key} className="enhance-style-opt" onClick={() => handleEnhanceClick(s.key)}>
                      <span className="enhance-style-name">{s.name}</span>
                      <span className="enhance-style-desc">{s.desc}</span>
                    </button>
                  ))}
                </div>
              )}
            </div>
          )}
          {enhanceErr && <div className="enhance-error">{enhanceErr}</div>}
        </div>
        <div className="recipe-hero-text">
          <div className="recipe-eyebrow">
            {recipe.category || 'Recipe'}
          </div>
          <h1 className="recipe-title">{recipe.name}</h1>
          {recipe.addedBy && (
            <div className="recipe-author">
              <IconBook size={14} stroke={1.7} />
              <span>Added by <strong>{recipe.addedBy}</strong></span>
            </div>
          )}
          {recipe.author && (
            <div className="recipe-adapted">Adapted from {recipe.author}</div>
          )}
          {recipe.description && <p className="recipe-desc">{recipe.description}</p>}
          {rating > 0 && (
            <div className="recipe-rating">
              <Stars value={Math.round(rating)} size={16} />
              <span>{rating.toFixed(1)}</span>
              <span className="muted">· {ratingCount} {ratingCount === 1 ? "rating" : "ratings"}</span>
            </div>
          )}

          <div className="recipe-stats">
            <MetaStat icon={IconClock} label="Prep" value={recipe.prep || "—"} />
            <MetaStat icon={IconFlame} label="Cook" value={recipe.cook || "—"} />
            <MetaStat icon={IconUsers} label="Serves" value={
              <span className="serving-ctrl">
                <button onClick={() => setServings(Math.max(1, servings - 1))} aria-label="Fewer">−</button>
                <span>{servings}</span>
                <button onClick={() => setServings(servings + 1)} aria-label="More">+</button>
              </span>
            } />
          </div>

          <div className="recipe-cta">
            <button className="primary-btn big" onClick={onCook} disabled={!(recipe.steps||[]).length}>
              <IconPlay size={16} /> Start cooking
            </button>
            <button className="ghost-btn big" onClick={() => onAddToShopping(recipe, factor)}
                    disabled={!(recipe.ingredients||[]).length}>
              <IconBag size={16} /> Add to list
            </button>
          </div>

          <div className="recipe-tags">
            {(recipe.mealTypes || []).map(t => <span key={t} className="tag tag-meal">{t}</span>)}
            {(recipe.meatTypes || []).map(t => <span key={t} className="tag tag-meat">{t}</span>)}
          </div>
        </div>
      </div>

      {enhancedUrl && (
        <div className="enhance-compare">
          <div className="enhance-compare-header">
            <div className="enhance-compare-title">✨ Enhancement Preview</div>
            <button className="enhance-compare-dismiss" onClick={() => { setEnhancedUrl(null); setEnhancedBase64(null); setEnhancedMime(null); setEnhanceErr(''); }} title="Dismiss">
              <IconX size={15} />
            </button>
          </div>
          <div className="enhance-compare-cols">
            <div className="enhance-col">
              <div className="enhance-thumb" style={{ backgroundImage: `url(${img})` }} />
              <div className="enhance-col-label">Original</div>
              <button className="ghost-btn enhance-use" onClick={() => handleChoosePhoto('original')}>
                Keep Original
              </button>
            </div>
            <div className="enhance-col enhance-col-new">
              <img className="enhance-thumb enhance-thumb-img" src={enhancedUrl} alt="Enhanced preview" />
              <div className="enhance-col-label">Enhanced ✨</div>
              <button className="primary-btn enhance-use" onClick={() => handleChoosePhoto('enhanced')}>
                Save Enhanced
              </button>
            </div>
          </div>
        </div>
      )}

      <div className="recipe-body">
        <aside className="ing-panel">
          <div className="panel-head">
            <h2>Ingredients</h2>
            <span className="panel-sub">{(recipe.ingredients || []).length} items · {servings} {servings === 1 ? "serving" : "servings"}</span>
          </div>
          <ul className="ing-list">
            {(recipe.ingredients || []).map((i, idx) => (
              <li key={idx}>
                <span className="ing-amt">{scaleAmtStr(i.amt || '', factor) || "•"}</span>
                <span className="ing-name">
                  <LinkedText text={i.name || ''} refs={otherRecipes} onOpenRef={openRef} />
                </span>
              </li>
            ))}
          </ul>
        </aside>

        <section className="steps-panel">
          <div className="panel-head">
            <h2>Method</h2>
            <span className="panel-sub">{(recipe.steps || []).length} steps</span>
          </div>
          <ol className="steps-list">
            {(recipe.steps || []).map((s, idx) => (
              <li key={idx}>
                <span className="step-num">{String(idx + 1).padStart(2, "0")}</span>
                <p><LinkedText text={s} refs={otherRecipes} onOpenRef={openRef} /></p>
              </li>
            ))}
          </ol>

          <NotesSection
            recipe={recipe}
            onAdd={onAddComment}
            members={members}
            onDelete={onDeleteComment}
          />

          <FamilyVerdict
            recipe={recipe}
            onSet={onSetVerdict}
            onRemove={onRemoveVerdict}
            currentUserId={currentUserId}
          />
        </section>
      </div>
    </div>
  );
}

// ── Timer helper ──────────────────────────────────────────────────
function useTimer() {
  const [seconds, setSeconds] = React.useState(0);
  const [running, setRunning] = React.useState(false);
  const [target, setTarget] = React.useState(0);

  React.useEffect(() => {
    if (!running) return;
    const t = setInterval(() => setSeconds(s => {
      const n = s + 1;
      if (target > 0 && n >= target) {
        setRunning(false);
        try {
          const ctx = new (window.AudioContext || window.webkitAudioContext)();
          const o = ctx.createOscillator(); const g = ctx.createGain();
          o.frequency.value = 880; o.connect(g); g.connect(ctx.destination);
          g.gain.setValueAtTime(0.0001, ctx.currentTime);
          g.gain.exponentialRampToValueAtTime(0.2, ctx.currentTime + 0.01);
          g.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 1.2);
          o.start(); o.stop(ctx.currentTime + 1.2);
        } catch {}
      }
      return n;
    }), 1000);
    return () => clearInterval(t);
  }, [running, target]);

  const fmt = (s) => {
    const m = Math.floor(s / 60);
    const sec = s % 60;
    return `${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
  };
  const start = (targetSec = 0) => { setSeconds(0); setTarget(targetSec); setRunning(true); };
  const toggle = () => setRunning(r => !r);
  const reset = () => { setRunning(false); setSeconds(0); setTarget(0); };
  return { seconds, running, target, start, toggle, reset, fmt };
}

function extractTimerMinutes(step) {
  if (!step) return 0;
  const range = step.match(/(\d+)\s*(?:–|-|to)\s*(\d+)\s*min/i);
  if (range) return parseInt(range[2]);
  const m = step.match(/(\d+)\s*min/i);
  if (m) return parseInt(m[1]);
  const hr = step.match(/(\d+)\s*hour/i);
  if (hr) return parseInt(hr[1]) * 60;
  return 0;
}

// ── Cook mode ───────────────────────────────────────────────────────
function CookMode({ recipe, onClose }) {
  const [step, setStep] = React.useState(0);
  const total = (recipe.steps || []).length;
  const [showIng, setShowIng] = React.useState(false);
  const [completed, setCompleted] = React.useState({});
  const [wake, setWake] = React.useState(false);
  const [ingChecked, setIngChecked] = React.useState({});
  const timer = useTimer();

  React.useEffect(() => {
    let lock;
    if (wake && 'wakeLock' in navigator) {
      navigator.wakeLock.request('screen').then(l => lock = l).catch(() => {});
    }
    return () => { try { lock && lock.release(); } catch {} };
  }, [wake]);

  React.useEffect(() => {
    const k = (e) => {
      if (e.key === "Escape") onClose();
      if (e.key === "ArrowRight") setStep(s => Math.min(total - 1, s + 1));
      if (e.key === "ArrowLeft") setStep(s => Math.max(0, s - 1));
      if (e.key === " ") { e.preventDefault(); timer.toggle(); }
    };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, [total, onClose]);

  React.useEffect(() => {
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = ''; };
  }, []);

  if (!total) return null;

  const progress = ((step + 1) / total) * 100;
  const currentStep = (recipe.steps || [])[step] || "";
  const nextStep = (recipe.steps || [])[step + 1];
  const suggestedMin = extractTimerMinutes(currentStep);
  const completedCount = Object.values(completed).filter(Boolean).length;

  const toggleDone = (i) => setCompleted(c => ({ ...c, [i]: !c[i] }));
  const markDoneAndNext = () => {
    setCompleted(c => ({ ...c, [step]: true }));
    if (step === total - 1) { onClose(); return; }
    setStep(s => s + 1);
    timer.reset();
  };

  return (
    <div className="cook-mode cook-focus">
      <div className="cook-top">
        <div className="cook-top-left">
          <button className="cook-close" onClick={onClose}>
            <IconX size={18} /> Exit
          </button>
          <div className="cook-recipe">
            <div className="cook-recipe-title">{recipe.name}</div>
            <div className="cook-recipe-sub">
              {recipe.author && <>by {recipe.author} · </>}Serves {recipe.servings || '—'}
            </div>
          </div>
        </div>
        <div className="cook-top-right">
          <button className={`cook-chip ${wake ? "on" : ""}`} onClick={() => setWake(w => !w)} title="Keep screen awake">
            <span className="dot-indicator" /> Screen on
          </button>
          <button className="cook-chip" onClick={() => setShowIng(s => !s)}>
            <IconBook size={15} /> Ingredients
          </button>
        </div>
      </div>

      <div className="cook-rail">
        <div className="cook-rail-track">
          <div className="cook-rail-fill" style={{ width: `${progress}%` }} />
        </div>
        <div className="cook-rail-dots">
          {(recipe.steps || []).map((_, i) => (
            <button
              key={i}
              className={`rail-dot ${i === step ? "active" : ""} ${completed[i] ? "done" : ""}`}
              onClick={() => { setStep(i); timer.reset(); }}
              title={`Step ${i + 1}`}
            >
              {completed[i] ? <IconCheck size={12} stroke={2.5} /> : <span>{i + 1}</span>}
            </button>
          ))}
        </div>
      </div>

      <div className="cook-body">
        <div className="cook-stage">
          <div className="cook-counter-row">
            <div className="cook-counter">
              <span className="cnt-num">{String(step + 1).padStart(2, "0")}</span>
              <span className="cnt-sep">/</span>
              <span className="cnt-tot">{String(total).padStart(2, "0")}</span>
            </div>
            <div className="cook-counter-label">{completedCount} of {total} complete</div>
          </div>

          <div className="cook-step">{currentStep}</div>

          <InlineStepIngredients
            stepText={currentStep}
            ingredients={recipe.ingredients || []}
            checked={ingChecked}
            onToggle={(name) => setIngChecked(m => ({ ...m, [name]: !m[name] }))}
          />

          {nextStep && (
            <div className="cook-next">
              <div className="cook-next-label">Up next</div>
              <div className="cook-next-text">{nextStep}</div>
            </div>
          )}
        </div>

        <div className="cook-timer">
          <TimerDisplay timer={timer} suggestedMin={suggestedMin} />
        </div>
      </div>

      <div className="cook-nav">
        <button className="cook-btn ghost" onClick={() => { setStep(s => Math.max(0, s - 1)); timer.reset(); }} disabled={step === 0}>
          <IconChevLeft size={18} /> Previous
        </button>
        <button className="cook-btn mark" onClick={() => toggleDone(step)}>
          <IconCheck size={16} /> {completed[step] ? "Undo" : "Mark done"}
        </button>
        <button className="cook-btn primary" onClick={markDoneAndNext}>
          {step === total - 1 ? (<><IconCheck size={18} /> Finish</>) : (<>Done — next step <IconChevRight size={18} /></>)}
        </button>
      </div>

      {showIng && (
        <>
          <div className="cook-drawer-bg" onClick={() => setShowIng(false)} />
          <div className="cook-ing-drawer">
            <div className="cook-ing-head">
              <div>
                <h3>All ingredients</h3>
                <div className="muted" style={{fontSize: '0.8rem', marginTop: 2}}>Tap to check off as you prep</div>
              </div>
              <button onClick={() => setShowIng(false)}><IconX size={18} /></button>
            </div>
            <ul className="cook-drawer-list">
              {(recipe.ingredients || []).map((i, idx) => {
                const key = i.name + idx;
                const on = ingChecked[key];
                return (
                  <li key={idx} className={on ? "on" : ""} onClick={() => setIngChecked(m => ({ ...m, [key]: !m[key] }))}>
                    <span className="drawer-check">{on ? <IconCheck size={14} stroke={2.5} /> : null}</span>
                    <span className="drawer-amt">{i.amt || "•"}</span>
                    <span className="drawer-name">{i.name}</span>
                  </li>
                );
              })}
            </ul>
          </div>
        </>
      )}
    </div>
  );
}

function TimerDisplay({ timer, suggestedMin }) {
  const { seconds, running, target, start, toggle, reset, fmt } = timer;
  const display = target > 0 ? Math.max(0, target - seconds) : seconds;
  const pct = target > 0 ? (seconds / target) * 100 : 0;

  const R = 70;
  const C = 2 * Math.PI * R;
  const dash = target > 0 ? (pct / 100) * C : 0;

  const quickTimes = [1, 5, 10, suggestedMin].filter((v, i, a) => v > 0 && a.indexOf(v) === i);

  return (
    <div className="timer-card">
      <div className="timer-label">Timer</div>
      <div className="timer-ring">
        <svg width="160" height="160" viewBox="0 0 160 160">
          <circle cx="80" cy="80" r={R} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth="4" />
          {target > 0 && (
            <circle cx="80" cy="80" r={R} fill="none"
              stroke="var(--accent)" strokeWidth="4" strokeLinecap="round"
              strokeDasharray={`${dash} ${C}`} transform="rotate(-90 80 80)"
              style={{ transition: "stroke-dasharray 0.5s linear" }}
            />
          )}
        </svg>
        <div className="timer-time">{fmt(display)}</div>
      </div>
      <div className="timer-controls">
        {(seconds === 0 && !running) ? (
          <div className="timer-quick">
            {quickTimes.map(m => (
              <button key={m} onClick={() => start(m * 60)} className={suggestedMin === m ? "suggested" : ""}>
                {m}m
              </button>
            ))}
          </div>
        ) : (
          <div className="timer-running-ctrls">
            <button className="timer-toggle" onClick={toggle}>{running ? "Pause" : "Resume"}</button>
            <button className="timer-reset" onClick={reset}>Reset</button>
          </div>
        )}
      </div>
      {suggestedMin > 0 && seconds === 0 && !running && (
        <div className="timer-hint">Step mentions {suggestedMin} min</div>
      )}
    </div>
  );
}

function InlineStepIngredients({ stepText, ingredients, checked, onToggle }) {
  if (!stepText) return null;
  const lower = stepText.toLowerCase();
  const matches = ingredients.filter(i => {
    if (!i.name) return false;
    const firstWord = i.name.split(/[,\s]/)[0].toLowerCase();
    if (firstWord.length < 3) return false;
    return lower.includes(firstWord);
  }).slice(0, 4);

  if (matches.length === 0) return null;

  return (
    <div className="step-ings">
      <div className="step-ings-label">Need now</div>
      <div className="step-ings-row">
        {matches.map((i, idx) => {
          const key = i.name + idx;
          const on = checked[key];
          return (
            <button key={key} className={`step-ing ${on ? "on" : ""}`} onClick={() => onToggle(key)}>
              <span className="step-ing-check">{on ? <IconCheck size={12} stroke={2.5} /> : null}</span>
              <span className="step-ing-amt">{i.amt}</span>
              <span className="step-ing-name">{i.name}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ── Shopping list panel (slide-in) ──────────────────────────────────
function ShoppingPanel({ items, onClose, onToggle, onClearChecked, onClearAll }) {
  const unchecked = items.filter(i => !i.checked);
  const checked = items.filter(i => i.checked);

  return (
    <div className="shop-panel">
      <div className="shop-head">
        <div>
          <h2>Shopping list</h2>
          <div className="shop-sub">{unchecked.length} to get · {checked.length} done</div>
        </div>
        <button className="icon-btn" onClick={onClose}><IconX size={18} /></button>
      </div>

      <div className="shop-actions">
        <button onClick={onClearChecked} disabled={checked.length === 0}>Clear checked</button>
        <button onClick={onClearAll} disabled={items.length === 0}>Clear all</button>
      </div>

      {items.length === 0 ? (
        <div className="shop-empty">
          <IconBag size={40} stroke={1.2} />
          <p>Nothing here yet.</p>
          <p className="muted">Open a recipe and tap <strong>Add to list</strong>.</p>
        </div>
      ) : (
        <ul className="shop-list">
          {unchecked.map(it => (
            <li key={it.id} className="shop-item">
              <label>
                <input type="checkbox" checked={false} onChange={() => onToggle(it.id)} />
                <span className="shop-amt">{it.amt}</span>
                <span className="shop-name">{it.name}</span>
              </label>
            </li>
          ))}
          {checked.length > 0 && <li className="shop-divider">Checked</li>}
          {checked.map(it => (
            <li key={it.id} className="shop-item checked">
              <label>
                <input type="checkbox" checked={true} onChange={() => onToggle(it.id)} />
                <span className="shop-amt">{it.amt}</span>
                <span className="shop-name">{it.name}</span>
              </label>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Object.assign(window, { RecipeView, CookMode, ShoppingPanel, TimerDisplay, InlineStepIngredients, LinkedText, RecipePeek, scaleAmtStr, parseBaseServings });
