// View components for the CMS

const { useState, useEffect, useRef } = React;

/* ============ Shared bits ============ */

const StatusBadge = ({ status }) => {
  const map = {
    published: { cls: "badge-published", label: "Published" },
    draft:     { cls: "badge-draft",     label: "Draft" },
    archived:  { cls: "badge-archived",  label: "Archived" },
    active:    { cls: "badge-active",    label: "Active" },
    inactive:  { cls: "badge-inactive",  label: "Inactive" },
  };
  const m = map[status] || map.draft;
  return (
    <span className={`badge ${m.cls}`}>
      <span className="dot"></span>
      {m.label}
    </span>
  );
};

const LocaleRow = ({ locales }) => {
  const order = ["en", "it", "es"];
  return (
    <span className="locales">
      {order.map(l => (
        <span key={l} className={`locale-pill ${locales[l] ? "" : "off"}`} title={locales[l] ? `${l.toUpperCase()} translation complete` : `${l.toUpperCase()} missing`}>
          {l}
        </span>
      ))}
    </span>
  );
};

/* ============ Tiny markdown ============ */

function renderMarkdown(src) {
  const lines = src.split("\n");
  const out = [];
  let para = [];
  let list = null;
  const flushPara = () => {
    if (para.length) { out.push(`<p>${inline(para.join(" "))}</p>`); para = []; }
  };
  const flushList = () => {
    if (list) { out.push(`<${list.kind}>${list.items.map(i => `<li>${inline(i)}</li>`).join("")}</${list.kind}>`); list = null; }
  };
  const inline = (s) => s
    .replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
    .replace(/`([^`]+)`/g, "<code>$1</code>")
    .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
    .replace(/\*([^*]+)\*/g, "<em>$1</em>")
    .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
  for (const raw of lines) {
    const line = raw.trimEnd();
    if (/^#\s/.test(line))    { flushPara(); flushList(); out.push(`<h1>${inline(line.slice(2))}</h1>`); continue; }
    if (/^##\s/.test(line))   { flushPara(); flushList(); out.push(`<h2>${inline(line.slice(3))}</h2>`); continue; }
    if (/^>\s?/.test(line))   { flushPara(); flushList(); out.push(`<blockquote>${inline(line.replace(/^>\s?/,""))}</blockquote>`); continue; }
    if (/^-\s/.test(line))    { flushPara(); if (!list || list.kind !== "ul") { flushList(); list = { kind: "ul", items: [] }; } list.items.push(line.slice(2)); continue; }
    if (/^\d+\.\s/.test(line)){ flushPara(); if (!list || list.kind !== "ol") { flushList(); list = { kind: "ol", items: [] }; } list.items.push(line.replace(/^\d+\.\s/,"")); continue; }
    if (line.trim() === "")   { flushPara(); flushList(); continue; }
    para.push(line);
  }
  flushPara(); flushList();
  return out.join("");
}

/* ============ Helpers ============ */

function buildLocales(translationLocales) {
  const locs = { it: false, en: false, es: false };
  (translationLocales || []).forEach(l => { if (l in locs) locs[l] = true; });
  return locs;
}

function buildAuthor(name) {
  if (!name) return { name: "Unknown", initials: "??" };
  const parts = name.trim().split(" ");
  const initials = parts.map(p => p[0]).join("").toUpperCase().slice(0, 2);
  return { name, initials };
}

function formatDate(d) {
  if (!d) return "—";
  return new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
}

function formatDateRange(start, end) {
  const fmt = d => d ? new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "?";
  if (!start && !end) return "—";
  return `${fmt(start)} – ${fmt(end)}`;
}

function slugify(str) {
  return (str || "article")
    .toLowerCase()
    .normalize("NFD").replace(/[̀-ͯ]/g, "")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "") || "article";
}

/* ============ Micro UI ============ */

const Spinner = () => (
  <div style={{ display: "flex", justifyContent: "center", padding: "40px 0", color: "var(--muted)", fontSize: 13 }}>
    Loading…
  </div>
);

const ErrorBanner = ({ msg }) => (
  <div style={{ padding: "10px 14px", background: "var(--red-50)", color: "var(--red)", borderRadius: "var(--radius-sm)", marginBottom: 12, fontSize: 13 }}>
    {msg}
  </div>
);

const Toast = ({ msg, type }) => (
  <div style={{
    position: "fixed", bottom: 24, right: 24, zIndex: 9999,
    padding: "10px 18px", borderRadius: "var(--radius)",
    background: type === "error" ? "var(--red)" : "#166534",
    color: "#fff", fontSize: 13,
    boxShadow: "0 4px 12px rgba(0,0,0,0.18)",
  }}>{msg}</div>
);

/* ============ TagsInput ============ */

const TagsInput = ({ tags, onChange }) => {
  const [input, setInput] = useState("");

  const addTag = (raw) => {
    const tag = raw.trim().toLowerCase();
    if (!tag || tags.includes(tag)) return;
    onChange([...tags, tag]);
  };

  const handleKeyDown = (e) => {
    if (e.key === "Enter" || e.key === ",") {
      e.preventDefault();
      addTag(input);
      setInput("");
    } else if (e.key === "Backspace" && input === "" && tags.length > 0) {
      onChange(tags.slice(0, -1));
    }
  };

  const handleBlur = () => {
    if (input.trim()) { addTag(input); setInput(""); }
  };

  const removeTag = (i) => onChange(tags.filter((_, idx) => idx !== i));

  return (
    <div style={{
      display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center",
      border: "1px solid var(--border)", borderRadius: "var(--radius-sm)",
      padding: "6px 10px", minHeight: 38, background: "#fff", cursor: "text",
    }} onClick={e => e.currentTarget.querySelector("input").focus()}>
      {tags.map((t, i) => (
        <span key={t} style={{
          display: "inline-flex", alignItems: "center", gap: 4,
          background: "var(--green-50)", color: "var(--green-700, #15803d)",
          borderRadius: 99, padding: "2px 8px", fontSize: 12, fontWeight: 500,
        }}>
          {t}
          <button
            type="button"
            onClick={() => removeTag(i)}
            style={{ background: "none", border: "none", cursor: "pointer", padding: 0, lineHeight: 1, color: "inherit", opacity: 0.6, fontSize: 14 }}
          >×</button>
        </span>
      ))}
      <input
        style={{ border: "none", outline: "none", fontSize: 13, minWidth: 100, flex: 1, padding: 0, background: "transparent" }}
        value={input}
        placeholder={tags.length === 0 ? "sonno, melatonina… Invio per aggiungere" : ""}
        onChange={e => setInput(e.target.value)}
        onKeyDown={handleKeyDown}
        onBlur={handleBlur}
      />
    </div>
  );
};

/* ============ Media Picker ============ */

const MediaPicker = ({ onSelect, onClose }) => {
  const [media, setMedia] = useState([]);
  const [loading, setLoading] = useState(true);
  const [uploading, setUploading] = useState(false);
  const fileRef = useRef(null);

  useEffect(() => {
    api.getMedia()
      .then(({ data }) => setMedia(data || []))
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);

  const handleUpload = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    setUploading(true);
    try {
      const row = await api.uploadMedia(file);
      setMedia(prev => [row, ...prev]);
      onSelect(row.url);
    } catch (err) {
      alert(err.message);
    } finally {
      setUploading(false);
      e.target.value = "";
    }
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" style={{ width: 720, maxHeight: "80vh" }} onClick={e => e.stopPropagation()}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14, flexShrink: 0 }}>
          <strong style={{ fontSize: 15 }}>Media library</strong>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-sm btn-primary" onClick={() => fileRef.current && fileRef.current.click()} disabled={uploading}>
              <Icon name="upload" size={13}/> {uploading ? "Uploading…" : "Upload new"}
            </button>
            <button className="btn btn-sm btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={16}/></button>
          </div>
          <input ref={fileRef} type="file" accept="image/jpeg,image/png,image/webp" style={{ display: "none" }} onChange={handleUpload}/>
        </div>
        <div style={{ overflowY: "auto", flex: 1 }}>
          {loading ? <Spinner/> : (
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 10 }}>
              {media.map(m => (
                <div
                  key={m.id}
                  onClick={() => onSelect(m.url)}
                  style={{ cursor: "pointer", borderRadius: 6, overflow: "hidden", border: "2px solid var(--border)", background: "var(--surface-2)" }}
                >
                  <div style={{ height: 100, backgroundImage: `url(${m.url})`, backgroundSize: "cover", backgroundPosition: "center" }}></div>
                  <div style={{ padding: "5px 8px", fontSize: 11, color: "var(--muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{m.filename}</div>
                </div>
              ))}
              {media.length === 0 && (
                <div style={{ gridColumn: "1/-1", padding: "30px 0", textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
                  No images yet. Click "Upload new" to add one.
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

/* ============ Login ============ */

const LoginView = ({ onLogin }) => {
  const [email, setEmail] = useState("admin@naturmia.com");
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    try {
      const { token } = await api.login(email, password);
      onLogin(token);
    } catch {
      setError("Invalid email or password");
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "var(--surface)" }}>
      <div style={{ background: "#fff", borderRadius: 12, padding: 36, width: 360, boxShadow: "var(--shadow-md)", border: "1px solid var(--border)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 24 }}>
          <div className="brand-mark"><Icon name="leaf" size={17}/></div>
          <div className="brand-name" style={{ fontSize: 18 }}>Natur<span>Mia</span></div>
        </div>
        {error && <ErrorBanner msg={error}/>}
        <form onSubmit={handleSubmit}>
          <div className="form-row">
            <label className="label">Email</label>
            <input className="input" type="email" value={email} onChange={e => setEmail(e.target.value)} required autoFocus/>
          </div>
          <div className="form-row">
            <label className="label">Password</label>
            <input className="input" type="password" value={password} onChange={e => setPassword(e.target.value)} required placeholder="••••••••"/>
          </div>
          <button type="submit" className="btn btn-primary" style={{ width: "100%", marginTop: 8 }} disabled={loading}>
            {loading ? "Signing in…" : "Sign in"}
          </button>
        </form>
      </div>
    </div>
  );
};

/* ============ Dashboard ============ */

const Dashboard = ({ onGo }) => {
  const [stats, setStats] = useState({});
  const [recent, setRecent] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    Promise.all([
      api.getStats().catch(() => ({})),
      api.getArticles({ limit: 8 }).catch(() => ({ articles: [] })),
    ]).then(([s, { articles }]) => {
      setStats(s);
      setRecent(articles || []);
    }).finally(() => setLoading(false));
  }, []);

  const queue = recent.filter(a => {
    const locs = buildLocales(a.translation_locales);
    return !(locs.it && locs.en && locs.es);
  });

  return (
    <div data-screen-label="Dashboard">
      <div className="stat-grid">
        <div className="stat">
          <span className="l">Published articles</span>
          <span className="v">{loading ? "…" : (stats.published || 0)}</span>
          <span className="d">+8 this month</span>
        </div>
        <div className="stat">
          <span className="l">Drafts in review</span>
          <span className="v">{loading ? "…" : (stats.draft || 0)}</span>
          <span className="d down">awaiting translation</span>
        </div>
        <div className="stat">
          <span className="l">Archived</span>
          <span className="v">{loading ? "…" : (stats.archived || 0)}</span>
          <span className="d">all time</span>
        </div>
        <div className="stat">
          <span className="l">Total articles</span>
          <span className="v">{loading ? "…" : Object.values(stats).reduce((a, b) => a + b, 0)}</span>
          <span className="d">across all statuses</span>
        </div>
      </div>
      <div className="dash-grid">
        <div className="card">
          <div className="toolbar" style={{ justifyContent: "space-between" }}>
            <strong style={{ fontSize: 14 }}>Recently edited</strong>
            <button className="btn btn-sm btn-ghost" onClick={() => onGo("articles")}>
              View all <Icon name="chevron-right" size={14}/>
            </button>
          </div>
          {loading ? <Spinner/> : (
            <table className="table">
              <tbody>
                {recent.slice(0, 4).map(a => (
                  <tr key={a.id}>
                    <td>
                      <div className="title-cell" style={{ maxWidth: 360 }}>
                        <span className="t">{a.title || "(Untitled)"}</span>
                        <span className="s">{a.category} · {a.author_name || "Unknown"}</span>
                      </div>
                    </td>
                    <td><LocaleRow locales={buildLocales(a.translation_locales)}/></td>
                    <td><StatusBadge status={a.status}/></td>
                  </tr>
                ))}
                {recent.length === 0 && (
                  <tr><td colSpan={3} style={{ padding: 24, textAlign: "center", color: "var(--muted)" }}>No articles yet.</td></tr>
                )}
              </tbody>
            </table>
          )}
        </div>
        <div className="card" style={{ padding: 18 }}>
          <strong style={{ fontSize: 14 }}>Translation queue</strong>
          <p style={{ color: "var(--muted)", fontSize: 12.5, margin: "4px 0 14px" }}>Articles missing one or more locales</p>
          {loading ? <Spinner/> : queue.length === 0 ? (
            <p style={{ color: "var(--muted)", fontSize: 12.5 }}>All articles are fully translated.</p>
          ) : queue.slice(0, 4).map(a => (
            <div key={a.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 0", borderTop: "1px solid var(--border)" }}>
              <div style={{ minWidth: 0, paddingRight: 10 }}>
                <div style={{ fontWeight: 600, fontSize: 13, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{a.title || "(Untitled)"}</div>
                <div style={{ fontSize: 11.5, color: "var(--muted)" }}>{a.category}</div>
              </div>
              <LocaleRow locales={buildLocales(a.translation_locales)}/>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

/* ============ Articles list ============ */

const ArticlesList = ({ onOpen }) => {
  const [status, setStatus] = useState("All statuses");
  const [cat, setCat] = useState("All categories");
  const [categories, setCategories] = useState(CATEGORIES);
  const [q, setQ] = useState("");
  const [debouncedQ, setDebouncedQ] = useState("");
  const [page, setPage] = useState(1);
  const [articles, setArticles] = useState([]);
  const [total, setTotal] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const LIMIT = 20;

  const handleArchive = async (id, title) => {
    if (!confirm(`Archive "${title}"? It will no longer appear in the feed.`)) return;
    try {
      await api.deleteArticle(id);
      setArticles(prev => prev.filter(a => a.id !== id));
      setTotal(prev => prev - 1);
    } catch (e) {
      setError(e.message);
    }
  };

  useEffect(() => {
    api.getCategories().then(({ data }) => {
      if (data && data.length) setCategories(["All categories", ...data]);
    }).catch(() => {});
  }, []);

  // Debounce search input 300ms and reset to page 1
  // (categories are objects {id,slug,name_it,name_en,name_es}; cat state holds name_en string)
  useEffect(() => {
    const t = setTimeout(() => { setDebouncedQ(q); setPage(1); }, 300);
    return () => clearTimeout(t);
  }, [q]);

  // Reset page when filters change
  useEffect(() => { setPage(1); }, [status, cat]);

  useEffect(() => {
    setLoading(true);
    setError(null);
    api.getArticles({ status, category: cat === "All categories" ? undefined : cat, q: debouncedQ, page, limit: LIMIT })
      .then(({ articles, total }) => { setArticles(articles || []); setTotal(total || 0); })
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, [status, cat, debouncedQ, page]);

  const totalPages = Math.max(1, Math.ceil(total / LIMIT));
  const pageStart = Math.max(1, Math.min(page - 2, totalPages - 4));
  const pageEnd   = Math.min(totalPages, pageStart + 4);
  const pageButtons = [];
  for (let p = pageStart; p <= pageEnd; p++) pageButtons.push(p);

  return (
    <div data-screen-label="Articles List">
      {error && <ErrorBanner msg={error}/>}
      <div className="card">
        <div className="toolbar">
          <div className="search">
            <span className="icon"><Icon name="search" size={15}/></span>
            <input className="input" placeholder="Search by title or author…" value={q} onChange={e => setQ(e.target.value)}/>
          </div>
          <div className="filter-group" style={{ marginLeft: "auto" }}>
            <div style={{ position: "relative" }}>
              <select className="select" value={status} onChange={e => setStatus(e.target.value)} style={{ paddingRight: 30 }}>
                {STATUSES.map(s => <option key={s} value={s}>{s === "All statuses" ? s : s[0].toUpperCase() + s.slice(1)}</option>)}
              </select>
            </div>
            <select className="select" value={cat} onChange={e => setCat(e.target.value)}>
              {categories.map(c => {
                if (c === "All categories") return <option key="__all" value="All categories">All categories</option>;
                return <option key={c.slug || c.name_en} value={c.name_en}>{c.name_it || c.name_en}</option>;
              })}
            </select>
            <button className="btn">
              <Icon name="filter" size={14}/> More filters
            </button>
          </div>
        </div>

        {loading ? <Spinner/> : (
          <table className="table">
            <thead>
              <tr>
                <th style={{ width: "38%" }}>Title</th>
                <th>Locales</th>
                <th>Category</th>
                <th>Tags</th>
                <th>Author</th>
                <th>Status</th>
                <th>Published</th>
                <th style={{ textAlign: "right" }}>Actions</th>
              </tr>
            </thead>
            <tbody>
              {articles.map(a => {
                const author = buildAuthor(a.author_name);
                const locales = buildLocales(a.translation_locales);
                return (
                  <tr key={a.id}>
                    <td>
                      <div className="title-cell">
                        <span className="t">{a.title || "(Untitled)"}</span>
                        <span className="s">{a.slug}</span>
                      </div>
                    </td>
                    <td><LocaleRow locales={locales}/></td>
                    <td><span className="pill-count">{a.category}</span></td>
                    <td>
                      {(() => {
                        const tags = Array.isArray(a.topic_tags) ? a.topic_tags : [];
                        const visible = tags.slice(0, 3);
                        const extra = tags.length - visible.length;
                        return (
                          <div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
                            {visible.map(t => (
                              <span key={t} style={{
                                background: "var(--green-50)", color: "var(--green-700, #15803d)",
                                borderRadius: 99, padding: "1px 7px", fontSize: 11, fontWeight: 500,
                              }}>{t}</span>
                            ))}
                            {extra > 0 && (
                              <span style={{ color: "var(--muted)", fontSize: 11 }}>+{extra}</span>
                            )}
                          </div>
                        );
                      })()}
                    </td>
                    <td>
                      <div className="author-cell">
                        <div className="av">{author.initials}</div>
                        <span>{author.name}</span>
                      </div>
                    </td>
                    <td><StatusBadge status={a.status}/></td>
                    <td style={{ color: "var(--muted)", fontFamily: "JetBrains Mono, monospace", fontSize: 12.5 }}>{formatDate(a.published_at)}</td>
                    <td>
                      <div className="row-actions">
                        <button className="btn btn-sm btn-ghost btn-icon" title="Edit" onClick={() => onOpen(a.id, a.title || "(Untitled)")}><Icon name="edit" size={15}/></button>
                        <button className="btn btn-sm btn-ghost btn-icon" title="Archive" onClick={() => handleArchive(a.id, a.title || "(Untitled)")}><Icon name="archive" size={15}/></button>
                        <button className="btn btn-sm btn-ghost btn-icon" title="More"><Icon name="more" size={15}/></button>
                      </div>
                    </td>
                  </tr>
                );
              })}
              {articles.length === 0 && (
                <tr><td colSpan={8} style={{ textAlign: "center", padding: 40, color: "var(--muted)" }}>No articles match your filters.</td></tr>
              )}
            </tbody>
          </table>
        )}

        <div className="results-meta">
          <span>Showing <b style={{ color: "var(--ink)" }}>{articles.length}</b> of {total} articles</span>
          <div className="pager">
            <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}><Icon name="chevron-left" size={13}/></button>
            {pageButtons.map(p => (
              <button key={p} className={page === p ? "active" : ""} onClick={() => setPage(p)}>{p}</button>
            ))}
            <button onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={page >= totalPages}><Icon name="chevron-right" size={13}/></button>
          </div>
        </div>
      </div>
    </div>
  );
};

/* ============ Article edit ============ */

const ArticleEdit = ({ articleId, onBack, onTitleChange, saveSignal }) => {
  const handleSaveRef = useRef(null);
  const [locale, setLocale] = useState("it");
  const [titles, setTitles] = useState({ it: "", en: "", es: "" });
  const [bodies, setBodies] = useState({ it: SAMPLE_MD, en: "", es: "" });
  const [preview, setPreview] = useState(true);
  const [premium, setPremium] = useState(false);
  const [pinned, setPinned] = useState(false);
  const [status, setStatus] = useState("draft");
  const [category, setCategory] = useState(CATEGORIES[1] || "Nutrition");
  const [categoryList, setCategoryList] = useState(CATEGORIES.filter(c => c !== "All categories"));
  const [coverUrl, setCoverUrl] = useState(null);
  const [pubDate, setPubDate] = useState("");
  const [topicTags, setTopicTags] = useState([]);
  const [pickerOpen, setPickerOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState(null);

  useEffect(() => {
    api.getCategories().then(({ data }) => {
      if (data && data.length) {
        setCategoryList(data);
        // For new articles only: default to first DB category.
        // For existing articles the article load effect sets the correct value.
        if (!articleId) {
          const vals = data.map(c => typeof c === "object" ? c.name_en : c);
          setCategory(vals[0] || CATEGORIES[1] || "Nutrition");
        }
      }
    }).catch(() => {});
  }, []);

  // Load existing article
  useEffect(() => {
    if (!articleId) return;
    api.getArticle(articleId).then(({ data: a }) => {
      const t = { it: "", en: "", es: "" };
      const b = { it: "", en: "", es: "" };
      (a.translations || []).forEach(tr => {
        if (tr.locale in t) { t[tr.locale] = tr.title; b[tr.locale] = tr.body; }
      });
      setTitles(t);
      setBodies(b);
      setStatus(a.status === "archived" ? "draft" : a.status);
      setCategory(a.category || CATEGORIES[1] || "Nutrition");
      setPremium(a.is_premium || false);
      setPinned(a.is_pinned || false);
      setCoverUrl(a.cover_image_url || null);
      setPubDate(a.published_at ? a.published_at.slice(0, 10) : "");
      setTopicTags(Array.isArray(a.topic_tags) ? a.topic_tags : []);
      const displayTitle = t.it || t.en || t.es || "(Untitled)";
      if (onTitleChange) onTitleChange(displayTitle);
    }).catch(e => showToast(e.message, "error"));
  }, [articleId]);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  useEffect(() => {
    if (saveSignal > 0) handleSaveRef.current && handleSaveRef.current();
  }, [saveSignal]);

  const buildPayload = (overrideStatus) => {
    const s = overrideStatus || status;
    const translations = [
      { locale: "it", title: titles.it, body: bodies.it },
      { locale: "en", title: titles.en, body: bodies.en },
      { locale: "es", title: titles.es, body: bodies.es },
    ].filter(t => t.title || t.body);
    return { category, status: s, isPremium: premium, isPinned: pinned, publishedAt: pubDate || null, translations, coverImageUrl: coverUrl || null, topicTags };
  };

  const handleSave = async (publishStatus) => {
    setSaving(true);
    try {
      const payload = buildPayload(publishStatus);
      if (!articleId) {
        const primaryTitle = titles.it || titles.en || titles.es || "article";
        payload.slug = `${slugify(primaryTitle)}-${Date.now()}`;
        await api.createArticle(payload);
        showToast("Article created");
      } else {
        await api.updateArticle(articleId, payload);
        const saved = payload.status;
        showToast(saved === "published" ? "Published!" : saved === "archived" ? "Archived" : "Saved");
      }
    } catch (e) {
      showToast(e.message, "error");
    } finally {
      setSaving(false);
    }
  };

  // Keep ref in sync so the topbar Save button always calls the latest closure
  handleSaveRef.current = handleSave;

  const localeMeta = [
    { code: "it", name: "Italiano", flag: "🇮🇹" },
    { code: "en", name: "English",  flag: "🇬🇧" },
    { code: "es", name: "Español",  flag: "🇪🇸" },
  ];

  const body  = bodies[locale]  || "";
  const title = titles[locale]  || "";

  return (
    <div data-screen-label="Article Edit">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      <div className="edit-grid">
        {/* LEFT: editor */}
        <div className="card form-card">
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
            <div className="tabs">
              {localeMeta.map(l => {
                const complete = (titles[l.code] || "").trim().length > 0 && (bodies[l.code] || "").trim().length > 0;
                return (
                  <button key={l.code} className={`tab ${locale === l.code ? "active" : ""}`} onClick={() => setLocale(l.code)}>
                    <span>{l.flag}</span>
                    <span>{l.name}</span>
                    <span className="lk">{complete ? "·" : "○"}</span>
                  </button>
                );
              })}
            </div>
            <div style={{ display: "flex", gap: 6 }}>
              <button className="btn btn-sm" onClick={() => setPreview(p => !p)}>
                <Icon name={preview ? "split" : "eye"} size={14}/>
                {preview ? "Editor only" : "Live preview"}
              </button>
            </div>
          </div>

          <div className="form-row">
            <label className="label">Title — {locale.toUpperCase()}</label>
            <input
              className="input title-input"
              value={title}
              placeholder={locale === "es" ? "Añade un título en español…" : "Article title"}
              onChange={e => setTitles({ ...titles, [locale]: e.target.value })}
            />
          </div>

          <div className="form-row">
            <label className="label" style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
              <span>Body — Markdown</span>
              <span style={{ fontSize: 11, color: "var(--muted)", textTransform: "none", letterSpacing: 0, fontWeight: 400 }}>
                {body.split(/\s+/).filter(Boolean).length} words
              </span>
            </label>
            <div className="editor-wrap">
              <div className="editor-toolbar">
                <button className="tool-btn" title="Heading 1"><Icon name="h1" size={15}/></button>
                <button className="tool-btn" title="Heading 2"><Icon name="h2" size={15}/></button>
                <div className="sep"></div>
                <button className="tool-btn" title="Bold"><Icon name="bold" size={15}/></button>
                <button className="tool-btn" title="Italic"><Icon name="italic" size={15}/></button>
                <div className="sep"></div>
                <button className="tool-btn" title="List"><Icon name="list" size={15}/></button>
                <button className="tool-btn" title="Quote"><Icon name="quote" size={15}/></button>
                <button className="tool-btn" title="Code"><Icon name="code" size={15}/></button>
                <button className="tool-btn" title="Link"><Icon name="link" size={15}/></button>
                <button className="tool-btn" title="Image"><Icon name="image" size={15}/></button>
                <div style={{ flex: 1 }}></div>
                <button
                  className={`tool-btn toggle ${preview ? "active" : ""}`}
                  onClick={() => setPreview(p => !p)}
                  title="Toggle live preview"
                >
                  <Icon name="eye" size={15}/>
                </button>
              </div>
              <div className={`editor-body ${preview ? "split" : ""}`}>
                <textarea
                  className="md-textarea"
                  value={body}
                  placeholder={locale === "es" ? "# Escribe tu artículo aquí…" : "# Start writing…"}
                  onChange={e => setBodies({ ...bodies, [locale]: e.target.value })}
                />
                {preview && (
                  <div className="md-preview" dangerouslySetInnerHTML={{ __html: renderMarkdown(body || `# ${title || "Untitled"}\n\nNothing here yet.`) }}/>
                )}
              </div>
            </div>
            <div className="help">Supports headings, lists, **bold**, *italic*, `code`, &gt; quotes and [links](url). Drag images directly into the editor.</div>
          </div>
        </div>

        {/* RIGHT: meta */}
        <div className="card side-card">
          {pickerOpen && (
            <MediaPicker
              onSelect={(url) => { setCoverUrl(url); setPickerOpen(false); showToast("Cover selected"); }}
              onClose={() => setPickerOpen(false)}
            />
          )}
          <div className="form-row">
            <label className="label">Cover image</label>
            <div
              className={`cover ${coverUrl ? "has-image" : ""}`}
              style={coverUrl ? { backgroundImage: `url(${coverUrl})`, backgroundSize: "cover", backgroundPosition: "center" } : {}}
            >
              {!coverUrl && <>1600 × 1000 · jpg, png, webp</>}
            </div>
            <div className="cover-actions">
              <button className="btn btn-sm" onClick={() => setPickerOpen(true)}>
                <Icon name="image" size={13}/> {coverUrl ? "Replace" : "Choose image"}
              </button>
              {coverUrl && (
                <button className="btn btn-sm btn-ghost" onClick={() => setCoverUrl(null)}>
                  <Icon name="trash" size={13}/> Remove
                </button>
              )}
            </div>
          </div>

          <div className="form-row">
            <label className="label">Category</label>
            <select className="select" value={category} onChange={e => setCategory(e.target.value)}>
              {categoryList.map(c => {
                const val = typeof c === "object" ? c.name_en : c;
                const label = typeof c === "object" ? (c.name_it || c.name_en) : c;
                return <option key={val} value={val}>{label}</option>;
              })}
            </select>
          </div>

          <div className="form-row">
            <label className="label">Status</label>
            <div className="seg">
              {["draft", "published", "archived"].map(s => (
                <button key={s} className={status === s ? "active" : ""} onClick={() => setStatus(s)}>
                  {s[0].toUpperCase() + s.slice(1)}
                </button>
              ))}
            </div>
          </div>

          <div className="form-row">
            <label className="label">Published date</label>
            <div style={{ position: "relative" }}>
              <input className="input" type="date" value={pubDate} onChange={e => setPubDate(e.target.value)} style={{ paddingLeft: 34 }}/>
              <span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "var(--muted)" }}>
                <Icon name="calendar" size={15}/>
              </span>
            </div>
          </div>

          <div className="form-row">
            <label className="label">Tag tematici</label>
            <TagsInput tags={topicTags} onChange={setTopicTags}/>
            <div className="help">Invio o virgola per aggiungere · Backspace per rimuovere l'ultimo</div>
          </div>

          <div className="form-row" style={{ marginTop: 18 }}>
            <div className="toggle-row">
              <div className="t">
                <strong>Pinned</strong>
                <span>Always first in the feed</span>
              </div>
              <button className={`switch ${pinned ? "on" : ""}`} onClick={() => setPinned(p => !p)}></button>
            </div>
            <div className="toggle-row">
              <div className="t">
                <strong>Premium content</strong>
                <span>Subscribers only</span>
              </div>
              <button className={`switch ${premium ? "on" : ""}`} onClick={() => setPremium(p => !p)}></button>
            </div>
            <div className="toggle-row">
              <div className="t">
                <strong>Allow comments</strong>
                <span>Members can reply</span>
              </div>
              <button className="switch on" onClick={() => {}}></button>
            </div>
            <div className="toggle-row">
              <div className="t">
                <strong>Feature on home</strong>
                <span>Hero carousel slot</span>
              </div>
              <button className="switch" onClick={() => {}}></button>
            </div>
          </div>

          <div className="save-bar">
            <button className="btn" disabled={saving} onClick={() => handleSave()}>
              {saving ? "Saving…" : status === "archived" ? "Archive" : status === "published" ? "Update" : "Save draft"}
            </button>
            <button className="btn btn-primary" disabled={saving} onClick={() => handleSave("published")}>
              <Icon name="check" size={14}/>
              {status === "published" ? "Update & publish" : "Publish"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

/* ============ Recipes list ============ */

const RECIPE_SORT_COLUMNS = [
  { key: "title", label: "Title" },
  { key: "category", label: "Category" },
  { key: "is_premium", label: "Premium" },
  { key: "status", label: "Status" },
  { key: "published_at", label: "Published" },
];

const RecipesList = ({ onOpen }) => {
  const [status, setStatus] = useState("All statuses");
  const [page, setPage] = useState(1);
  const [sortBy, setSortBy] = useState("published_at");
  const [sortDir, setSortDir] = useState("DESC");
  const [recipes, setRecipes] = useState([]);
  const [total, setTotal] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const LIMIT = 20;

  const handleArchive = async (id, title) => {
    if (!confirm(`Archive "${title}"? It will no longer appear in the feed.`)) return;
    try {
      await api.deleteRecipe(id);
      setRecipes(prev => prev.filter(r => r.id !== id));
      setTotal(prev => prev - 1);
    } catch (e) {
      setError(e.message);
    }
  };

  const handleSort = (col) => {
    if (sortBy === col) {
      setSortDir(d => d === "ASC" ? "DESC" : "ASC");
    } else {
      setSortBy(col);
      setSortDir("ASC");
    }
  };

  useEffect(() => { setPage(1); }, [status, sortBy, sortDir]);

  useEffect(() => {
    setLoading(true);
    setError(null);
    api.getRecipes({ status: status === "All statuses" ? undefined : status, page, limit: LIMIT, sortBy, sortDir })
      .then(({ recipes, total }) => { setRecipes(recipes || []); setTotal(total || 0); })
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, [status, page, sortBy, sortDir]);

  const totalPages = Math.max(1, Math.ceil(total / LIMIT));
  const pageStart = Math.max(1, Math.min(page - 2, totalPages - 4));
  const pageEnd   = Math.min(totalPages, pageStart + 4);
  const pageButtons = [];
  for (let p = pageStart; p <= pageEnd; p++) pageButtons.push(p);

  return (
    <div data-screen-label="Recipes List">
      {error && <ErrorBanner msg={error}/>}
      <div className="card">
        <div className="toolbar">
          <div className="filter-group" style={{ marginLeft: "auto" }}>
            <select className="select" value={status} onChange={e => setStatus(e.target.value)}>
              {STATUSES.map(s => <option key={s} value={s}>{s === "All statuses" ? s : s[0].toUpperCase() + s.slice(1)}</option>)}
            </select>
          </div>
        </div>

        {loading ? <Spinner/> : (
          <table className="table">
            <thead>
              <tr>
                {RECIPE_SORT_COLUMNS.map(({ key, label }) => (
                  <th
                    key={key}
                    style={{ width: key === "title" ? "40%" : undefined, cursor: "pointer", userSelect: "none", whiteSpace: "nowrap" }}
                    onClick={() => handleSort(key)}
                  >
                    {label}{sortBy === key && (sortDir === "ASC" ? " ▲" : " ▼")}
                  </th>
                ))}
                <th>Locales</th>
                <th style={{ textAlign: "right" }}>Actions</th>
              </tr>
            </thead>
            <tbody>
              {recipes.map(r => {
                const locales = buildLocales(r.translation_locales);
                return (
                  <tr key={r.id}>
                    <td>
                      <div className="title-cell">
                        <span className="t">{r.title || "(Untitled)"}</span>
                        <span className="s">{r.slug}</span>
                      </div>
                    </td>
                    <td><span className="pill-count">{r.category || "—"}</span></td>
                    <td>{r.is_premium ? "Yes" : "No"}</td>
                    <td><StatusBadge status={r.status}/></td>
                    <td style={{ color: "var(--muted)", fontFamily: "JetBrains Mono, monospace", fontSize: 12.5 }}>{formatDate(r.published_at)}</td>
                    <td><LocaleRow locales={locales}/></td>
                    <td>
                      <div className="row-actions">
                        <button className="btn btn-sm btn-ghost btn-icon" title="Edit" onClick={() => onOpen(r.id, r.title || "(Untitled)")}><Icon name="edit" size={15}/></button>
                        <button className="btn btn-sm btn-ghost btn-icon" title="Archive" onClick={() => handleArchive(r.id, r.title || "(Untitled)")}><Icon name="archive" size={15}/></button>
                      </div>
                    </td>
                  </tr>
                );
              })}
              {recipes.length === 0 && (
                <tr><td colSpan={7} style={{ textAlign: "center", padding: 40, color: "var(--muted)" }}>No recipes match your filters.</td></tr>
              )}
            </tbody>
          </table>
        )}

        <div className="results-meta">
          <span>Showing <b style={{ color: "var(--ink)" }}>{recipes.length}</b> of {total} recipes</span>
          <div className="pager">
            <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}><Icon name="chevron-left" size={13}/></button>
            {pageButtons.map(p => (
              <button key={p} className={page === p ? "active" : ""} onClick={() => setPage(p)}>{p}</button>
            ))}
            <button onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={page >= totalPages}><Icon name="chevron-right" size={13}/></button>
          </div>
        </div>
      </div>
    </div>
  );
};

/* ============ Recipe edit ============ */

const RecipeEdit = ({ recipeId, onBack, onTitleChange, saveSignal }) => {
  const handleSaveRef = useRef(null);
  const [locale, setLocale] = useState("it");
  const [titles, setTitles] = useState({ it: "", en: "", es: "" });
  const [descriptions, setDescriptions] = useState({ it: "", en: "", es: "" });
  const [bodies, setBodies] = useState({ it: "", en: "", es: "" });
  const [ingredients, setIngredients] = useState({ it: [], en: [], es: [] });
  const [steps, setSteps] = useState({ it: [], en: [], es: [] });
  const [premium, setPremium] = useState(false);
  const [status, setStatus] = useState("draft");
  const [category, setCategory] = useState("");
  const [difficulty, setDifficulty] = useState("");
  const [servings, setServings] = useState("");
  const [cookingTime, setCookingTime] = useState("");
  const [preparationTime, setPreparationTime] = useState("");
  const [coverUrl, setCoverUrl] = useState(null);
  const [pickerOpen, setPickerOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState(null);

  useEffect(() => {
    if (!recipeId) return;
    api.getRecipe(recipeId).then(({ data: r }) => {
      const t = { it: "", en: "", es: "" };
      const d = { it: "", en: "", es: "" };
      const b = { it: "", en: "", es: "" };
      (r.translations || []).forEach(tr => {
        if (tr.locale in t) { t[tr.locale] = tr.title || ""; d[tr.locale] = tr.description || ""; b[tr.locale] = tr.body || ""; }
      });
      setTitles(t);
      setDescriptions(d);
      setBodies(b);
      setStatus(r.status === "archived" ? "draft" : r.status);
      setCategory(r.category || "");
      setDifficulty(r.difficulty != null ? String(r.difficulty) : "");
      setServings(r.servings != null ? String(r.servings) : "");
      setCookingTime(r.cooking_time != null ? String(r.cooking_time) : "");
      setPreparationTime(r.preparation_time != null ? String(r.preparation_time) : "");
      setPremium(r.is_premium || false);
      setCoverUrl(r.cover_image_url || null);

      // The API returns ingredients/steps for a single locale at a time (defaults to 'it').
      // Load that locale's rows now; other locales start empty until the user fills them in.
      const primaryLocale = (r.translations && r.translations[0] && r.translations[0].locale) || "it";
      setIngredients(prev => ({ ...prev, [primaryLocale]: (r.ingredients || []).map(i => ({ name: i.name, quantity: i.quantity || "", unit: i.unit || "" })) }));
      setSteps(prev => ({ ...prev, [primaryLocale]: (r.steps || []).map(s => ({ description: s.description })) }));

      const displayTitle = t.it || t.en || t.es || "(Untitled)";
      if (onTitleChange) onTitleChange(displayTitle);
    }).catch(e => showToast(e.message, "error"));
  }, [recipeId]);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  useEffect(() => {
    if (saveSignal > 0) handleSaveRef.current && handleSaveRef.current();
  }, [saveSignal]);

  const buildPayload = (overrideStatus) => {
    const s = overrideStatus || status;
    const translations = [
      { locale: "it", title: titles.it, description: descriptions.it, body: bodies.it },
      { locale: "en", title: titles.en, description: descriptions.en, body: bodies.en },
      { locale: "es", title: titles.es, description: descriptions.es, body: bodies.es },
    ].filter(t => t.title);

    const ingredientRows = [];
    const stepRows = [];
    for (const loc of ["it", "en", "es"]) {
      (ingredients[loc] || []).forEach((row, i) => {
        if (!row.name) return;
        ingredientRows.push({ locale: loc, name: row.name, quantity: row.quantity || null, unit: row.unit || null, sortOrder: i });
      });
      (steps[loc] || []).forEach((row, i) => {
        if (!row.description) return;
        stepRows.push({ locale: loc, stepNumber: i + 1, description: row.description, sortOrder: i });
      });
    }

    return {
      status: s,
      isPremium: premium,
      coverImageUrl: coverUrl || null,
      category: category || null,
      difficulty: difficulty ? parseInt(difficulty, 10) : null,
      servings: servings ? parseInt(servings, 10) : null,
      cookingTime: cookingTime ? parseInt(cookingTime, 10) : null,
      preparationTime: preparationTime ? parseInt(preparationTime, 10) : null,
      translations,
      ingredients: ingredientRows,
      steps: stepRows,
    };
  };

  const handleSave = async (publishStatus) => {
    setSaving(true);
    try {
      const payload = buildPayload(publishStatus);
      if (!recipeId) {
        const primaryTitle = titles.it || titles.en || titles.es || "recipe";
        payload.slug = `${slugify(primaryTitle)}-${Date.now()}`;
        await api.createRecipe(payload);
        showToast("Recipe created");
      } else {
        await api.updateRecipe(recipeId, payload);
        const saved = payload.status;
        showToast(saved === "published" ? "Published!" : saved === "archived" ? "Archived" : "Saved");
      }
    } catch (e) {
      showToast(e.message, "error");
    } finally {
      setSaving(false);
    }
  };

  handleSaveRef.current = handleSave;

  const localeMeta = [
    { code: "it", name: "Italiano", flag: "🇮🇹" },
    { code: "en", name: "English",  flag: "🇬🇧" },
    { code: "es", name: "Español",  flag: "🇪🇸" },
  ];

  const updateIngredient = (i, field, value) => {
    setIngredients(prev => {
      const rows = [...(prev[locale] || [])];
      rows[i] = { ...rows[i], [field]: value };
      return { ...prev, [locale]: rows };
    });
  };
  const addIngredient = () => setIngredients(prev => ({ ...prev, [locale]: [...(prev[locale] || []), { name: "", quantity: "", unit: "" }] }));
  const removeIngredient = (i) => setIngredients(prev => ({ ...prev, [locale]: prev[locale].filter((_, idx) => idx !== i) }));

  const updateStep = (i, value) => {
    setSteps(prev => {
      const rows = [...(prev[locale] || [])];
      rows[i] = { description: value };
      return { ...prev, [locale]: rows };
    });
  };
  const addStep = () => setSteps(prev => ({ ...prev, [locale]: [...(prev[locale] || []), { description: "" }] }));
  const removeStep = (i) => setSteps(prev => ({ ...prev, [locale]: prev[locale].filter((_, idx) => idx !== i) }));

  return (
    <div data-screen-label="Recipe Edit">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      <div className="edit-grid">
        {/* LEFT: editor */}
        <div className="card form-card">
          <div className="tabs" style={{ marginBottom: 14 }}>
            {localeMeta.map(l => {
              const complete = (titles[l.code] || "").trim().length > 0;
              return (
                <button key={l.code} className={`tab ${locale === l.code ? "active" : ""}`} onClick={() => setLocale(l.code)}>
                  <span>{l.flag}</span>
                  <span>{l.name}</span>
                  <span className="lk">{complete ? "·" : "○"}</span>
                </button>
              );
            })}
          </div>

          <div className="form-row">
            <label className="label">Title — {locale.toUpperCase()}</label>
            <input
              className="input title-input"
              value={titles[locale] || ""}
              placeholder="Recipe title"
              onChange={e => setTitles({ ...titles, [locale]: e.target.value })}
            />
          </div>

          <div className="form-row">
            <label className="label">Description — {locale.toUpperCase()}</label>
            <textarea
              className="textarea"
              rows={3}
              value={descriptions[locale] || ""}
              placeholder="Short description shown in the feed…"
              onChange={e => setDescriptions({ ...descriptions, [locale]: e.target.value })}
              style={{ resize: "vertical" }}
            />
          </div>

          <div className="form-row">
            <label className="label">Body — Markdown</label>
            <textarea
              className="textarea md-textarea"
              rows={8}
              value={bodies[locale] || ""}
              placeholder="Full recipe content…"
              onChange={e => setBodies({ ...bodies, [locale]: e.target.value })}
              style={{ resize: "vertical" }}
            />
          </div>

          <div className="form-row">
            <label className="label" style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
              <span>Ingredients — {locale.toUpperCase()}</span>
              <button className="btn btn-sm" type="button" onClick={addIngredient}><Icon name="plus" size={13}/> Add</button>
            </label>
            {(ingredients[locale] || []).map((row, i) => (
              <div key={i} style={{ display: "flex", gap: 8, marginBottom: 6 }}>
                <input className="input" style={{ flex: 3 }} placeholder="Name" value={row.name} onChange={e => updateIngredient(i, "name", e.target.value)}/>
                <input className="input" style={{ flex: 1 }} placeholder="Qty" value={row.quantity} onChange={e => updateIngredient(i, "quantity", e.target.value)}/>
                <input className="input" style={{ flex: 1 }} placeholder="Unit" value={row.unit} onChange={e => updateIngredient(i, "unit", e.target.value)}/>
                <button className="btn btn-sm btn-ghost btn-icon" type="button" onClick={() => removeIngredient(i)}><Icon name="trash" size={13}/></button>
              </div>
            ))}
            {(ingredients[locale] || []).length === 0 && (
              <p style={{ color: "var(--muted)", fontSize: 12.5 }}>No ingredients yet.</p>
            )}
          </div>

          <div className="form-row">
            <label className="label" style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
              <span>Preparation steps — {locale.toUpperCase()}</span>
              <button className="btn btn-sm" type="button" onClick={addStep}><Icon name="plus" size={13}/> Add</button>
            </label>
            {(steps[locale] || []).map((row, i) => (
              <div key={i} style={{ display: "flex", gap: 8, marginBottom: 6, alignItems: "flex-start" }}>
                <span style={{ padding: "8px 0", color: "var(--muted)", fontSize: 12.5, minWidth: 18 }}>{i + 1}.</span>
                <textarea className="textarea" rows={2} style={{ flex: 1, resize: "vertical" }} placeholder="Step description" value={row.description} onChange={e => updateStep(i, e.target.value)}/>
                <button className="btn btn-sm btn-ghost btn-icon" type="button" onClick={() => removeStep(i)}><Icon name="trash" size={13}/></button>
              </div>
            ))}
            {(steps[locale] || []).length === 0 && (
              <p style={{ color: "var(--muted)", fontSize: 12.5 }}>No steps yet.</p>
            )}
          </div>
        </div>

        {/* RIGHT: meta */}
        <div className="card side-card">
          {pickerOpen && (
            <MediaPicker
              onSelect={(url) => { setCoverUrl(url); setPickerOpen(false); showToast("Cover selected"); }}
              onClose={() => setPickerOpen(false)}
            />
          )}
          <div className="form-row">
            <label className="label">Cover image</label>
            <div
              className={`cover ${coverUrl ? "has-image" : ""}`}
              style={coverUrl ? { backgroundImage: `url(${coverUrl})`, backgroundSize: "cover", backgroundPosition: "center" } : {}}
            >
              {!coverUrl && <>1600 × 1000 · jpg, png, webp</>}
            </div>
            <div className="cover-actions">
              <button className="btn btn-sm" onClick={() => setPickerOpen(true)}>
                <Icon name="image" size={13}/> {coverUrl ? "Replace" : "Choose image"}
              </button>
              {coverUrl && (
                <button className="btn btn-sm btn-ghost" onClick={() => setCoverUrl(null)}>
                  <Icon name="trash" size={13}/> Remove
                </button>
              )}
            </div>
          </div>

          <div className="form-row">
            <label className="label">Category</label>
            <input className="input" value={category} placeholder="e.g. Main course" onChange={e => setCategory(e.target.value)}/>
          </div>

          <div className="form-row" style={{ display: "flex", gap: 8 }}>
            <div style={{ flex: 1 }}>
              <label className="label">Difficulty</label>
              <input className="input" type="number" min="1" max="5" value={difficulty} onChange={e => setDifficulty(e.target.value)}/>
            </div>
            <div style={{ flex: 1 }}>
              <label className="label">Servings</label>
              <input className="input" type="number" min="1" value={servings} onChange={e => setServings(e.target.value)}/>
            </div>
          </div>

          <div className="form-row" style={{ display: "flex", gap: 8 }}>
            <div style={{ flex: 1 }}>
              <label className="label">Prep time (min)</label>
              <input className="input" type="number" min="0" value={preparationTime} onChange={e => setPreparationTime(e.target.value)}/>
            </div>
            <div style={{ flex: 1 }}>
              <label className="label">Cook time (min)</label>
              <input className="input" type="number" min="0" value={cookingTime} onChange={e => setCookingTime(e.target.value)}/>
            </div>
          </div>

          <div className="form-row">
            <label className="label">Status</label>
            <div className="seg">
              {["draft", "published", "archived"].map(s => (
                <button key={s} className={status === s ? "active" : ""} onClick={() => setStatus(s)}>
                  {s[0].toUpperCase() + s.slice(1)}
                </button>
              ))}
            </div>
          </div>

          <div className="form-row" style={{ marginTop: 18 }}>
            <div className="toggle-row">
              <div className="t">
                <strong>Premium content</strong>
                <span>Subscribers only</span>
              </div>
              <button className={`switch ${premium ? "on" : ""}`} onClick={() => setPremium(p => !p)}></button>
            </div>
          </div>

          <div className="save-bar">
            <button className="btn" disabled={saving} onClick={() => handleSave()}>
              {saving ? "Saving…" : status === "archived" ? "Archive" : status === "published" ? "Update" : "Save draft"}
            </button>
            <button className="btn btn-primary" disabled={saving} onClick={() => handleSave("published")}>
              <Icon name="check" size={14}/>
              {status === "published" ? "Update & publish" : "Publish"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

/* ============ Advertisements ============ */

const AdsList = ({ onOpen }) => {
  const [filter, setFilter] = useState("all");
  const [ads, setAds] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    setError(null);
    api.getAds({ status: filter === "all" ? null : filter })
      .then(result => setAds(result.data || []))
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, [filter]);

  const handleDelete = async (id, title) => {
    if (!confirm(`Deactivate "${title}"?`)) return;
    try {
      await api.deleteAd(id);
      setAds(prev => prev.filter(a => a.id !== id));
    } catch (e) {
      setError(e.message);
    }
  };

  const list = ads.map((ad, i) => {
    const translations = (ad.translations || []).filter(Boolean);
    const itTrans = translations.find(t => t.locale === "it") || translations[0] || {};
    return {
      id: ad.id,
      title: ad.title_it || itTrans.title || "(Untitled)",
      advertiser: ad.advertiser || (ad.is_internal ? "NaturMia (house)" : "External"),
      status: ad.is_active ? "active" : "inactive",
      cta: ad.cta_url,
      imageUrl: ad.image_url || null,
      range: formatDateRange(ad.start_date, ad.end_date),
      impressions: "—",
      grad: `grad-${(i % 6) + 1}`,
      placement: "—",
    };
  });

  return (
    <div data-screen-label="Advertisements">
      {error && <ErrorBanner msg={error}/>}
      <div className="card" style={{ marginBottom: 18 }}>
        <div className="toolbar">
          <div className="search">
            <span className="icon"><Icon name="search" size={15}/></span>
            <input className="input" placeholder="Search ads, advertisers, URLs…"/>
          </div>
          <div className="filter-group" style={{ marginLeft: "auto" }}>
            <div className="tabs">
              {[["all","All"],["active","Active"],["inactive","Inactive"]].map(([k,l]) => (
                <button key={k} className={`tab ${filter === k ? "active" : ""}`} onClick={() => setFilter(k)}>{l}</button>
              ))}
            </div>
            <select className="select"><option>All placements</option><option>Home sidebar</option><option>Article footer</option><option>In-article</option></select>
          </div>
        </div>
      </div>

      {loading ? <Spinner/> : (
        <div className="ads-grid">
          {list.map(ad => (
            <div key={ad.id} className="ad-card">
              <div
                className={`ad-thumb ${ad.imageUrl ? "" : ad.grad}`}
                style={ad.imageUrl ? { backgroundImage: `url(${ad.imageUrl})`, backgroundSize: "cover", backgroundPosition: "center" } : {}}
              >
                <span className="ribbon"><StatusBadge status={ad.status}/></span>
                <button className="more" title="More"><Icon name="more" size={14}/></button>
              </div>
              <div className="ad-body">
                <h3>{ad.title}</h3>
                <div className="ad-meta">
                  <span>{ad.advertiser}</span>
                  <a className="cta" href={ad.cta}>
                    <Icon name="external" size={12}/>
                    {(ad.cta || "").replace(/^https?:\/\//, "")}
                  </a>
                  <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
                    <Icon name="calendar" size={12}/> {ad.range}
                  </span>
                </div>
              </div>
              <div className="ad-foot">
                <span className="imp">{ad.impressions} impressions</span>
                <div style={{ display: "flex", gap: 6 }}>
                  <button className="btn btn-sm btn-ghost btn-icon" title="Deactivate" onClick={() => handleDelete(ad.id, ad.title)}>
                    <Icon name="trash" size={13}/>
                  </button>
                  <button className="btn btn-sm" onClick={() => onOpen && onOpen(ad.id, ad.title)}>
                    <Icon name="edit" size={13}/> Edit
                  </button>
                </div>
              </div>
            </div>
          ))}
          {list.length === 0 && (
            <p style={{ color: "var(--muted)", fontSize: 13, padding: "16px 0" }}>No advertisements found.</p>
          )}
        </div>
      )}
    </div>
  );
};

/* ============ Users ============ */

const UsersView = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [showInvite, setShowInvite] = useState(false);
  const [inviteForm, setInviteForm] = useState({ name: "", email: "", role: "contributor", password: "" });
  const [inviting, setInviting] = useState(false);
  const [toast, setToast] = useState(null);

  const ROLES = ["admin", "editor", "author", "contributor"];

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  const load = () => {
    setLoading(true);
    api.getUsers()
      .then(({ data }) => setUsers(data || []))
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  };

  useEffect(() => { load(); }, []);

  const handleInvite = async (e) => {
    e.preventDefault();
    setInviting(true);
    try {
      await api.inviteUser(inviteForm);
      setShowInvite(false);
      setInviteForm({ name: "", email: "", role: "contributor", password: "" });
      showToast("User invited");
      load();
    } catch (err) {
      showToast(err.message, "error");
    } finally {
      setInviting(false);
    }
  };

  const handleRoleChange = async (id, role) => {
    try {
      await api.updateUserRole(id, role);
      setUsers(prev => prev.map(u => u.id === id ? { ...u, role } : u));
    } catch (err) {
      showToast(err.message, "error");
    }
  };

  const handleRemove = async (id, name) => {
    if (!confirm(`Remove ${name}?`)) return;
    try {
      await api.removeUser(id);
      setUsers(prev => prev.filter(u => u.id !== id));
      showToast("User removed");
    } catch (err) {
      showToast(err.message, "error");
    }
  };

  const initials = (name) => (name || "?").trim().split(" ").map(p => p[0]).join("").toUpperCase().slice(0, 2);

  return (
    <div data-screen-label="Users">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      {error && <ErrorBanner msg={error}/>}

      {showInvite && (
        <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.35)", zIndex: 9000, display: "flex", alignItems: "center", justifyContent: "center" }}>
          <div className="card" style={{ width: 400, padding: 28 }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 20 }}>
              <strong style={{ fontSize: 15 }}>Invite team member</strong>
              <button className="btn btn-sm btn-ghost btn-icon" onClick={() => setShowInvite(false)}><Icon name="x" size={16}/></button>
            </div>
            <form onSubmit={handleInvite}>
              <div className="form-row">
                <label className="label">Name</label>
                <input className="input" required value={inviteForm.name} onChange={e => setInviteForm(f => ({ ...f, name: e.target.value }))}/>
              </div>
              <div className="form-row">
                <label className="label">Email</label>
                <input className="input" type="email" required value={inviteForm.email} onChange={e => setInviteForm(f => ({ ...f, email: e.target.value }))}/>
              </div>
              <div className="form-row">
                <label className="label">Password</label>
                <input className="input" type="password" required value={inviteForm.password} onChange={e => setInviteForm(f => ({ ...f, password: e.target.value }))} placeholder="Temporary password"/>
              </div>
              <div className="form-row">
                <label className="label">Role</label>
                <select className="select" value={inviteForm.role} onChange={e => setInviteForm(f => ({ ...f, role: e.target.value }))}>
                  {ROLES.map(r => <option key={r} value={r}>{r[0].toUpperCase() + r.slice(1)}</option>)}
                </select>
              </div>
              <div style={{ display: "flex", gap: 8, marginTop: 20 }}>
                <button type="button" className="btn" style={{ flex: 1 }} onClick={() => setShowInvite(false)}>Cancel</button>
                <button type="submit" className="btn btn-primary" style={{ flex: 1 }} disabled={inviting}>
                  {inviting ? "Inviting…" : "Send invite"}
                </button>
              </div>
            </form>
          </div>
        </div>
      )}

      <div className="card">
        <div className="toolbar">
          <div className="search">
            <span className="icon"><Icon name="search" size={15}/></span>
            <input className="input" placeholder="Search team…"/>
          </div>
          <div style={{ marginLeft: "auto" }}>
            <button className="btn btn-primary" onClick={() => setShowInvite(true)}><Icon name="plus" size={14}/> Invite member</button>
          </div>
        </div>
        {loading ? <Spinner/> : (
          <table className="table">
            <thead>
              <tr><th>Member</th><th>Role</th><th style={{ textAlign: "right" }}></th></tr>
            </thead>
            <tbody>
              {users.map(u => (
                <tr key={u.id}>
                  <td>
                    <div className="author-cell">
                      <div className="av" style={{ width: 32, height: 32, fontSize: 12 }}>{initials(u.name)}</div>
                      <div style={{ display: "flex", flexDirection: "column" }}>
                        <strong style={{ fontSize: 13.5 }}>{u.name}</strong>
                        <span style={{ color: "var(--muted)", fontSize: 12 }}>{u.email}</span>
                      </div>
                    </div>
                  </td>
                  <td>
                    <select className="select" value={u.role} onChange={e => handleRoleChange(u.id, e.target.value)} style={{ fontSize: 12.5, padding: "3px 8px" }}>
                      {ROLES.map(r => <option key={r} value={r}>{r[0].toUpperCase() + r.slice(1)}</option>)}
                    </select>
                  </td>
                  <td style={{ textAlign: "right" }}>
                    <button className="btn btn-sm btn-ghost btn-icon" title="Remove" onClick={() => handleRemove(u.id, u.name)}><Icon name="trash" size={15}/></button>
                  </td>
                </tr>
              ))}
              {users.length === 0 && (
                <tr><td colSpan={3} style={{ padding: 30, textAlign: "center", color: "var(--muted)" }}>No team members yet.</td></tr>
              )}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
};

/* ============ Ad edit ============ */

const AdEdit = ({ adId, onBack, onTitleChange }) => {
  const [locale, setLocale] = useState("it");
  const [titles, setTitles] = useState({ it: "", en: "", es: "" });
  const [descriptions, setDescriptions] = useState({ it: "", en: "", es: "" });
  const [ctaTexts, setCtaTexts] = useState({ it: "", en: "", es: "" });
  const [advertiser, setAdvertiser] = useState("");
  const [ctaUrl, setCtaUrl] = useState("");
  const [imageUrl, setImageUrl] = useState(null);
  const [startDate, setStartDate] = useState("");
  const [endDate, setEndDate] = useState("");
  const [isActive, setIsActive] = useState(true);
  const [isInternal, setIsInternal] = useState(false);
  const [pickerOpen, setPickerOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState(null);

  useEffect(() => {
    if (!adId) return;
    api.getAd(adId).then(({ data: ad }) => {
      const t = { it: ad.title_it || "", en: ad.title_en || "", es: ad.title_es || "" };
      const d = { it: ad.description_it || "", en: ad.description_en || "", es: ad.description_es || "" };
      const c = { it: ad.cta_text_it || "", en: ad.cta_text_en || "", es: ad.cta_text_es || "" };
      (ad.translations || []).filter(Boolean).forEach(tr => {
        if (tr.locale in t && !t[tr.locale]) t[tr.locale] = tr.title;
        if (tr.locale in c && !c[tr.locale]) c[tr.locale] = tr.cta_label || "";
      });
      setTitles(t);
      setDescriptions(d);
      setCtaTexts(c);
      setAdvertiser(ad.advertiser || "");
      setCtaUrl(ad.cta_url || "");
      setImageUrl(ad.image_url || null);
      setStartDate(ad.start_date ? ad.start_date.slice(0, 10) : "");
      setEndDate(ad.end_date ? ad.end_date.slice(0, 10) : "");
      setIsActive(ad.is_active);
      setIsInternal(ad.is_internal);
      const displayTitle = t.it || t.en || t.es || "(Untitled)";
      if (onTitleChange) onTitleChange(displayTitle);
    }).catch(e => showToast(e.message, "error"));
  }, [adId]);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  const handleSave = async () => {
    setSaving(true);
    try {
      const payload = {
        advertiser: advertiser || null,
        cta_url: ctaUrl || null,
        active: isActive,
        house_ad: isInternal,
        cover_url: imageUrl || null,
        start_date: startDate || null,
        end_date: endDate || null,
        title_it: titles.it,
        title_en: titles.en,
        title_es: titles.es,
        description_it: descriptions.it,
        description_en: descriptions.en,
        description_es: descriptions.es,
        cta_text_it: ctaTexts.it,
        cta_text_en: ctaTexts.en,
        cta_text_es: ctaTexts.es,
      };
      if (!adId) {
        await api.createAd(payload);
        showToast("Advertisement created");
      } else {
        await api.updateAd(adId, payload);
        showToast("Saved");
      }
    } catch (e) {
      showToast(e.message, "error");
    } finally {
      setSaving(false);
    }
  };

  const localeMeta = [
    { code: "it", name: "Italiano", flag: "🇮🇹" },
    { code: "en", name: "English",  flag: "🇬🇧" },
    { code: "es", name: "Español",  flag: "🇪🇸" },
  ];

  return (
    <div data-screen-label="Ad Edit">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      {pickerOpen && (
        <MediaPicker
          onSelect={(url) => { setImageUrl(url); setPickerOpen(false); showToast("Image selected"); }}
          onClose={() => setPickerOpen(false)}
        />
      )}
      <div className="edit-grid">
        {/* LEFT: per-locale content */}
        <div className="card form-card">
          <div className="tabs" style={{ marginBottom: 14 }}>
            {localeMeta.map(l => (
              <button key={l.code} className={`tab ${locale === l.code ? "active" : ""}`} onClick={() => setLocale(l.code)}>
                <span>{l.flag}</span><span>{l.name}</span>
              </button>
            ))}
          </div>
          <div className="form-row">
            <label className="label">Title — {locale.toUpperCase()}</label>
            <input
              className="input title-input"
              value={titles[locale] || ""}
              placeholder="Advertisement title"
              onChange={e => setTitles({ ...titles, [locale]: e.target.value })}
            />
          </div>
          <div className="form-row">
            <label className="label">Description — {locale.toUpperCase()}</label>
            <textarea
              className="textarea"
              rows={4}
              value={descriptions[locale] || ""}
              placeholder="Optional ad description…"
              onChange={e => setDescriptions({ ...descriptions, [locale]: e.target.value })}
              style={{ resize: "vertical" }}
            />
          </div>
          <div className="form-row">
            <label className="label">CTA Text — {locale.toUpperCase()}</label>
            <input
              className="input"
              value={ctaTexts[locale] || ""}
              placeholder="e.g. Learn more"
              onChange={e => setCtaTexts({ ...ctaTexts, [locale]: e.target.value })}
            />
          </div>
        </div>

        {/* RIGHT: meta */}
        <div className="card side-card">
          <div className="form-row">
            <label className="label">Advertiser</label>
            <input className="input" value={advertiser} placeholder="Company or brand name" onChange={e => setAdvertiser(e.target.value)}/>
          </div>
          <div className="form-row">
            <label className="label">CTA URL</label>
            <input className="input" type="url" value={ctaUrl} placeholder="https://…" onChange={e => setCtaUrl(e.target.value)}/>
          </div>
          <div className="form-row">
            <label className="label">Cover image</label>
            <div
              className={`cover ${imageUrl ? "has-image" : ""}`}
              style={imageUrl ? { backgroundImage: `url(${imageUrl})`, backgroundSize: "cover", backgroundPosition: "center" } : {}}
            >
              {!imageUrl && <>1600 × 600 · jpg, png, webp</>}
            </div>
            <div className="cover-actions">
              <button className="btn btn-sm" onClick={() => setPickerOpen(true)}>
                <Icon name="image" size={13}/> {imageUrl ? "Replace" : "Choose image"}
              </button>
              {imageUrl && (
                <button className="btn btn-sm btn-ghost" onClick={() => setImageUrl(null)}>
                  <Icon name="trash" size={13}/> Remove
                </button>
              )}
            </div>
          </div>
          <div className="form-row">
            <label className="label">Start date</label>
            <input className="input" type="date" value={startDate} onChange={e => setStartDate(e.target.value)}/>
          </div>
          <div className="form-row">
            <label className="label">End date</label>
            <input className="input" type="date" value={endDate} onChange={e => setEndDate(e.target.value)}/>
          </div>
          <div className="form-row" style={{ marginTop: 10 }}>
            <div className="toggle-row">
              <div className="t"><strong>Active</strong><span>Show this ad</span></div>
              <button className={`switch ${isActive ? "on" : ""}`} onClick={() => setIsActive(v => !v)}></button>
            </div>
            <div className="toggle-row">
              <div className="t"><strong>House ad</strong><span>Internal NaturMia ad</span></div>
              <button className={`switch ${isInternal ? "on" : ""}`} onClick={() => setIsInternal(v => !v)}></button>
            </div>
          </div>
          <div className="save-bar">
            <button className="btn" onClick={onBack}>Cancel</button>
            <button className="btn btn-primary" disabled={saving} onClick={handleSave}>
              <Icon name="check" size={14}/> {saving ? "Saving…" : "Save"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

/* ============ Categories ============ */

const CategoriesView = ({ addNew, onAddNewDone }) => {
  const [categories, setCategories] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [editingId, setEditingId] = useState(null);
  const [editValues, setEditValues] = useState({ name_it: "", name_en: "", name_es: "" });
  const [addValues, setAddValues] = useState({ name_it: "", name_en: "", name_es: "", slug: "" });
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState(null);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  const load = () => {
    setLoading(true);
    api.getCategories()
      .then(({ data }) => setCategories(data || []))
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  };

  useEffect(() => { load(); }, []);

  useEffect(() => {
    if (addNew) setAddValues({ name_it: "", name_en: "", name_es: "", slug: "" });
  }, [addNew]);

  const handleAdd = async () => {
    if (!addValues.name_it || !addValues.name_en || !addValues.name_es) {
      showToast("All three language names are required", "error");
      return;
    }
    setSaving(true);
    try {
      const slug = addValues.slug.trim() || slugify(addValues.name_en);
      const { data } = await api.createCategory({
        slug,
        name_it: addValues.name_it.trim(),
        name_en: addValues.name_en.trim(),
        name_es: addValues.name_es.trim(),
      });
      setCategories(prev => [...prev, data]);
      setAddValues({ name_it: "", name_en: "", name_es: "", slug: "" });
      if (onAddNewDone) onAddNewDone();
      showToast("Category added");
    } catch (e) {
      showToast(e.message, "error");
    } finally {
      setSaving(false);
    }
  };

  const handleSaveEdit = async (id) => {
    if (!editValues.name_it || !editValues.name_en || !editValues.name_es) {
      showToast("All three language names are required", "error");
      return;
    }
    try {
      const { data } = await api.updateCategory(id, editValues);
      setCategories(prev => prev.map(c => c.id === id ? data : c));
      setEditingId(null);
      showToast("Category updated");
    } catch (e) {
      showToast(e.message, "error");
    }
  };

  const handleDelete = async (cat) => {
    if (!confirm(`Delete "${cat.name_en}"?`)) return;
    try {
      await api.deleteCategory(cat.id);
      setCategories(prev => prev.filter(c => c.id !== cat.id));
      showToast("Category deleted");
    } catch (e) {
      showToast(e.message, "error");
    }
  };

  const startEdit = (cat) => {
    setEditingId(cat.id);
    setEditValues({ name_it: cat.name_it, name_en: cat.name_en, name_es: cat.name_es });
  };

  return (
    <div data-screen-label="Categories">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      {error && <ErrorBanner msg={error}/>}
      <div className="card">
        <table className="table">
          <thead>
            <tr>
              <th>Italian (IT)</th>
              <th>English (EN)</th>
              <th>Spanish (ES)</th>
              <th>Slug</th>
              <th style={{ textAlign: "right" }}>Actions</th>
            </tr>
          </thead>
          <tbody>
            {addNew && (
              <tr style={{ background: "var(--green-50)" }}>
                <td><input className="input" autoFocus placeholder="Nome italiano" value={addValues.name_it} onChange={e => setAddValues(v => ({ ...v, name_it: e.target.value }))}/></td>
                <td><input className="input" placeholder="English name" value={addValues.name_en} onChange={e => setAddValues(v => ({ ...v, name_en: e.target.value }))}/></td>
                <td><input className="input" placeholder="Nombre español" value={addValues.name_es} onChange={e => setAddValues(v => ({ ...v, name_es: e.target.value }))}/></td>
                <td>
                  <input
                    className="input"
                    placeholder={addValues.name_en ? slugify(addValues.name_en) : "auto"}
                    value={addValues.slug}
                    onChange={e => setAddValues(v => ({ ...v, slug: e.target.value }))}
                    style={{ fontFamily: "JetBrains Mono, monospace", fontSize: 12 }}
                  />
                </td>
                <td>
                  <div className="row-actions">
                    <button className="btn btn-sm btn-primary" onClick={handleAdd} disabled={saving}>Save</button>
                    <button className="btn btn-sm btn-ghost" onClick={() => { setAddValues({ name_it: "", name_en: "", name_es: "", slug: "" }); if (onAddNewDone) onAddNewDone(); }}>Cancel</button>
                  </div>
                </td>
              </tr>
            )}
            {loading ? (
              <tr><td colSpan={5}><Spinner/></td></tr>
            ) : categories.map(cat => (
              <tr key={cat.id}>
                {editingId === cat.id ? (
                  <>
                    <td><input className="input" value={editValues.name_it} onChange={e => setEditValues(v => ({ ...v, name_it: e.target.value }))}/></td>
                    <td><input className="input" value={editValues.name_en} onChange={e => setEditValues(v => ({ ...v, name_en: e.target.value }))}/></td>
                    <td><input className="input" value={editValues.name_es} onChange={e => setEditValues(v => ({ ...v, name_es: e.target.value }))}/></td>
                    <td style={{ fontFamily: "JetBrains Mono, monospace", fontSize: 12, color: "var(--muted)" }}>{cat.slug}</td>
                    <td>
                      <div className="row-actions">
                        <button className="btn btn-sm btn-primary" onClick={() => handleSaveEdit(cat.id)}>Save</button>
                        <button className="btn btn-sm btn-ghost" onClick={() => setEditingId(null)}>Cancel</button>
                      </div>
                    </td>
                  </>
                ) : (
                  <>
                    <td style={{ fontWeight: 500 }}>{cat.name_it}</td>
                    <td>{cat.name_en}</td>
                    <td>{cat.name_es}</td>
                    <td style={{ fontFamily: "JetBrains Mono, monospace", fontSize: 12, color: "var(--muted)" }}>{cat.slug}</td>
                    <td>
                      <div className="row-actions">
                        <button className="btn btn-sm btn-ghost btn-icon" title="Edit" onClick={() => startEdit(cat)}><Icon name="edit" size={14}/></button>
                        <button className="btn btn-sm btn-ghost btn-icon" title="Delete" onClick={() => handleDelete(cat)}><Icon name="trash" size={14}/></button>
                      </div>
                    </td>
                  </>
                )}
              </tr>
            ))}
            {!loading && categories.length === 0 && !addNew && (
              <tr><td colSpan={5} style={{ padding: "24px 16px", textAlign: "center", color: "var(--muted)" }}>
                No categories yet. Click "New category" to add one.
              </td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
};

/* ============ Media library ============ */

const MediaView = () => {
  const [media, setMedia] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [selected, setSelected] = useState(null);
  const [toast, setToast] = useState(null);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  useEffect(() => {
    api.getMedia()
      .then(({ data }) => setMedia(data || []))
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, []);

  const handleDelete = async (m) => {
    if (!confirm(`Delete ${m.filename}?`)) return;
    try {
      await api.deleteMedia(m.id);
      setMedia(prev => prev.filter(x => x.id !== m.id));
      if (selected && selected.id === m.id) setSelected(null);
      showToast("File deleted");
    } catch (e) {
      showToast(e.message, "error");
    }
  };

  const handleCopy = (url) => {
    navigator.clipboard.writeText(url).then(() => showToast("URL copied")).catch(() => showToast("Copy failed", "error"));
  };

  const formatSize = (bytes) => {
    if (!bytes) return "—";
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1048576) return `${(bytes / 1024).toFixed(0)} KB`;
    return `${(bytes / 1048576).toFixed(1)} MB`;
  };

  return (
    <div data-screen-label="Media" style={{ display: "flex", gap: 20, alignItems: "flex-start" }}>
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      <div style={{ flex: 1, minWidth: 0 }}>
        {error && <ErrorBanner msg={error}/>}
        {loading ? <Spinner/> : media.length === 0 ? (
          <div className="card" style={{ padding: 40, textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
            No media files yet. Upload images from the article or ad editor.
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(160px, 1fr))", gap: 12 }}>
            {media.map(m => (
              <div
                key={m.id}
                className="card"
                style={{ padding: 0, overflow: "hidden", cursor: "pointer", outline: selected && selected.id === m.id ? "2px solid var(--green)" : "none" }}
                onClick={() => setSelected(m)}
              >
                <div style={{ height: 120, background: "var(--surface-2)", backgroundImage: `url(${m.url})`, backgroundSize: "cover", backgroundPosition: "center" }}></div>
                <div style={{ padding: "8px 10px" }}>
                  <div style={{ fontSize: 11, color: "var(--muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{m.filename}</div>
                  <div style={{ fontSize: 10.5, color: "var(--muted-2)", marginTop: 2 }}>{formatSize(m.size_bytes)}</div>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>

      {selected && (
        <div className="card side-card" style={{ width: 280, flexShrink: 0 }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
            <strong style={{ fontSize: 13.5 }}>File details</strong>
            <button className="btn btn-sm btn-ghost btn-icon" onClick={() => setSelected(null)}><Icon name="x" size={15}/></button>
          </div>
          <div style={{ aspectRatio: "16/10", background: "var(--surface-2)", backgroundImage: `url(${selected.url})`, backgroundSize: "cover", backgroundPosition: "center", borderRadius: 6, marginBottom: 14 }}></div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8, fontSize: 12.5 }}>
            <div>
              <div className="label" style={{ marginBottom: 2 }}>Filename</div>
              <div style={{ color: "var(--ink-2)", wordBreak: "break-all", fontFamily: "JetBrains Mono, monospace", fontSize: 11.5 }}>{selected.filename}</div>
            </div>
            <div>
              <div className="label" style={{ marginBottom: 2 }}>Size</div>
              <div style={{ color: "var(--ink-2)" }}>{formatSize(selected.size_bytes)}</div>
            </div>
            {selected.uploaded_at && (
              <div>
                <div className="label" style={{ marginBottom: 2 }}>Uploaded</div>
                <div style={{ color: "var(--ink-2)" }}>{formatDate(selected.uploaded_at)}</div>
              </div>
            )}
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 6, marginTop: 14 }}>
            <button className="btn btn-sm btn-primary" onClick={() => handleCopy(selected.url)}>
              <Icon name="link" size={13}/> Copy URL
            </button>
            <button className="btn btn-sm btn-ghost" style={{ color: "var(--red)" }} onClick={() => handleDelete(selected)}>
              <Icon name="trash" size={13}/> Delete
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

/* ============ Notifications ============ */

const NotificationsView = () => {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [toast, setToast] = useState(null);

  const showToast = (msg, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  };

  useEffect(() => {
    api.getNotifications()
      .then(({ data }) => setItems(data || []))
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);

  const markAll = async () => {
    await api.markAllRead().catch(() => {});
    setItems([]);
    showToast("All marked as read");
  };

  return (
    <div data-screen-label="Notifications">
      {toast && <Toast msg={toast.msg} type={toast.type}/>}
      <div className="card" style={{ maxWidth: 600 }}>
        <div className="toolbar" style={{ justifyContent: "space-between" }}>
          <strong style={{ fontSize: 14 }}>Notifications</strong>
          {items.length > 0 && (
            <button className="btn btn-sm btn-ghost" onClick={markAll}>Mark all read</button>
          )}
        </div>
        {loading ? <Spinner/> : items.length === 0 ? (
          <div style={{ padding: "40px 0", textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
            You're all caught up.
          </div>
        ) : items.map(n => (
          <div key={n.id} style={{ padding: "12px 0", borderTop: "1px solid var(--border)", display: "flex", gap: 12, alignItems: "flex-start" }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13.5, fontWeight: n.read ? 400 : 600 }}>{n.message}</div>
              <div style={{ fontSize: 11.5, color: "var(--muted)", marginTop: 3 }}>{formatDate(n.created_at)}</div>
            </div>
            {!n.read && <span style={{ width: 8, height: 8, background: "var(--primary)", borderRadius: "50%", flexShrink: 0, marginTop: 5 }}></span>}
          </div>
        ))}
      </div>
    </div>
  );
};

Object.assign(window, { Dashboard, ArticlesList, ArticleEdit, RecipesList, RecipeEdit, AdsList, AdEdit, CategoriesView, MediaView, NotificationsView, UsersView, StatusBadge, LocaleRow, LoginView, MediaPicker });
